Skip to content

Commit d487fed

Browse files
committed
add USB HID keypad example
1 parent 9ca4ab9 commit d487fed

File tree

9 files changed

+1122
-0
lines changed

9 files changed

+1122
-0
lines changed

firmware/common/hid_keypad.c

+331
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
/*
2+
* Copyright (c) 2023 Erik Bosman <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person
5+
* obtaining a copy of this software and associated documentation
6+
* files (the "Software"), to deal in the Software without restriction,
7+
* including without limitation the rights to use, copy, modify,
8+
* merge, publish, distribute, sublicense, and/or sell copies of the
9+
* Software, and to permit persons to whom the Software is furnished to
10+
* do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be
13+
* included in all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19+
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20+
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*
24+
* (http://opensource.org/licenses/mit-license.html)
25+
*
26+
*/
27+
28+
#include <libopencm3/usb/usbd.h>
29+
#include <libopencm3/usb/hid.h>
30+
31+
#include <string.h>
32+
33+
#include "config.h"
34+
#include "hid_keypad.h"
35+
36+
#ifndef ID_VENDOR
37+
#define ID_VENDOR (0x4242)
38+
#endif
39+
#ifndef ID_PRODUCT
40+
#define ID_PRODUCT (0x4242)
41+
#endif
42+
#ifndef ID_VERSION
43+
#define ID_VERSION (0x0000)
44+
#endif
45+
46+
static usbd_device *device;
47+
48+
static uint8_t report_descriptor[256];
49+
static uint8_t report[32];
50+
static uint8_t control[128];
51+
52+
static uint32_t report_bits;
53+
static volatile uint32_t need_update;
54+
55+
#define VERSION_USB_2_0 (0x0200)
56+
57+
#define DEVICE_CLASS_LOOK_AT_INTERFACE (0)
58+
#define NO_SUBCLASS (0)
59+
#define NO_PROTOCOL (0)
60+
#define PACKET_SIZE_FULL_SPEED (64)
61+
62+
#define BUS_POWERED (1<<7)
63+
#define SELF_POWERED (1<<6)
64+
#define REMOTE_WAKEUP (1<<5)
65+
66+
#define MILLIAMPS(x) ((x+1)>>1)
67+
#define MILLISECONDS(x) (x)
68+
69+
#define COUNTRY_NONE (0)
70+
71+
#define REQ_DEVICE_TO_HOST (0x80)
72+
#define REQ_INTERFACE (REQ_DEVICE_TO_HOST|0x01)
73+
74+
enum
75+
{
76+
NO_STRING = 0,
77+
MANUFACTURER,
78+
PRODUCT,
79+
SERIAL,
80+
};
81+
82+
static const char *const string_descriptors[] =
83+
{
84+
[MANUFACTURER-1] = "techinc",
85+
[PRODUCT-1] = "keypad",
86+
[SERIAL-1] = "00000001",
87+
};
88+
#define N_STRING_DESCRIPTORS (sizeof(string_descriptors)/sizeof(string_descriptors[0]))
89+
90+
/* non-static descriptors */
91+
92+
/* .wMaxPacketSize needs to be set */
93+
static struct usb_endpoint_descriptor endpoint_desc =
94+
{
95+
.bLength = USB_DT_ENDPOINT_SIZE,
96+
.bDescriptorType = USB_DT_ENDPOINT,
97+
.bEndpointAddress = USB_ENDPOINT_ADDR_IN(1),
98+
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
99+
.wMaxPacketSize = 0x4242,
100+
.bInterval = MILLISECONDS(1),
101+
};
102+
103+
typedef struct __attribute__((packed))
104+
{
105+
struct usb_hid_descriptor d;
106+
uint8_t bDescriptorType;
107+
uint16_t wDescriptorLength;
108+
109+
} single_report_hid_descriptor_t;
110+
111+
/* .wDescriptorLength to be set */
112+
static single_report_hid_descriptor_t usb_hid_desciptor =
113+
{
114+
.d.bLength = sizeof(single_report_hid_descriptor_t),
115+
.d.bDescriptorType = USB_HID_DT_HID,
116+
.d.bcdHID = 0x111, /* 1.11 */
117+
.d.bCountryCode = COUNTRY_NONE,
118+
.d.bNumDescriptors = 1,
119+
.bDescriptorType = USB_HID_DT_REPORT,
120+
.wDescriptorLength = 0x4242,
121+
};
122+
123+
static size_t report_size(size_t n_keys)
124+
{
125+
return (n_keys+7)>>3;
126+
}
127+
128+
static size_t create_hid_keypad_descriptor(uint8_t buf[], size_t len, const uint32_t keys[], size_t n_keys)
129+
{
130+
const uint8_t hid_keypad_prologue[] =
131+
{
132+
0x05,0x01, /* Usage Page (Generic Desktop) */
133+
0x09,0x07, /* Usage (Keypad) */
134+
0xa1,0x01, /* Collection (Application) */
135+
};
136+
137+
if (n_keys >= 256)
138+
return 0;
139+
140+
size_t descriptor_size = sizeof(hid_keypad_prologue) + n_keys*5 + 7;
141+
if (n_keys & 0x7)
142+
descriptor_size += 6;
143+
144+
if (len < descriptor_size) return 0;
145+
146+
memcpy(&buf[0], hid_keypad_prologue, sizeof(hid_keypad_prologue));
147+
148+
size_t off = sizeof(hid_keypad_prologue);
149+
size_t i;
150+
151+
for (i=0; i<n_keys; i++)
152+
{
153+
buf[off++] = 0x0b; // (Long) Usage
154+
buf[off++] = (keys[i]>>0) & 0xff; // key
155+
buf[off++] = (keys[i]>>8) & 0xff;
156+
buf[off++] = (keys[i]>>16) & 0xff; // page
157+
buf[off++] = (keys[i]>>24) & 0xff;
158+
}
159+
160+
buf[off++] = 0x75; // Report Size (1)
161+
buf[off++] = 0x01;
162+
163+
buf[off++] = 0x95; // Report Count (n_keys) */
164+
buf[off++] = n_keys;
165+
166+
buf[off++] = 0x81; // Input (Data, Variable, Absolute)
167+
buf[off++] = 0x02;
168+
169+
if (n_keys & 0x7)
170+
{
171+
buf[off++] = 0x75; // Report Size (8- (n_keys&0x7) )
172+
buf[off++] = (0x8-(n_keys&0x7) );
173+
174+
buf[off++] = 0x95; // Report Count (1) */
175+
buf[off++] = 1;
176+
177+
buf[off++] = 0x81; // Input (Constant)
178+
buf[off++] = 0x01;
179+
}
180+
181+
buf[off++] = 0xC0; // End Collection
182+
183+
return off;
184+
}
185+
186+
187+
static const struct usb_device_descriptor device_desc =
188+
{
189+
.bLength = USB_DT_DEVICE_SIZE,
190+
.bDescriptorType = USB_DT_DEVICE,
191+
.bcdUSB = VERSION_USB_2_0,
192+
.bDeviceClass = DEVICE_CLASS_LOOK_AT_INTERFACE,
193+
.bDeviceSubClass = NO_SUBCLASS,
194+
.bDeviceProtocol = NO_PROTOCOL,
195+
.bMaxPacketSize0 = PACKET_SIZE_FULL_SPEED,
196+
.bNumConfigurations = 1,
197+
198+
.idVendor = ID_VENDOR,
199+
.idProduct = ID_PRODUCT,
200+
201+
.bcdDevice = ID_VERSION,
202+
203+
.iManufacturer = MANUFACTURER,
204+
.iProduct = PRODUCT,
205+
.iSerialNumber = SERIAL,
206+
};
207+
208+
static const struct usb_interface_descriptor interface_desc =
209+
{
210+
.bLength = USB_DT_INTERFACE_SIZE,
211+
.bDescriptorType = USB_DT_INTERFACE,
212+
.bInterfaceNumber = 0,
213+
.bAlternateSetting = 0,
214+
.bNumEndpoints = 1,
215+
.bInterfaceClass = USB_CLASS_HID,
216+
.bInterfaceSubClass = USB_HID_SUBCLASS_NO,
217+
.bInterfaceProtocol = USB_HID_INTERFACE_PROTOCOL_KEYBOARD,
218+
.iInterface = NO_STRING,
219+
220+
.endpoint = &endpoint_desc,
221+
.extra = &usb_hid_desciptor,
222+
.extralen = sizeof(usb_hid_desciptor),
223+
};
224+
225+
const struct usb_interface interfaces[] =
226+
{
227+
{
228+
.num_altsetting = 1,
229+
.altsetting = &interface_desc,
230+
},
231+
};
232+
233+
static const struct usb_config_descriptor config_desc =
234+
{
235+
.bLength = USB_DT_CONFIGURATION_SIZE,
236+
.bDescriptorType = USB_DT_CONFIGURATION,
237+
.wTotalLength = 0, /* calculated by USB stack */
238+
.bNumInterfaces = 1,
239+
.bConfigurationValue = 1,
240+
.iConfiguration = NO_STRING,
241+
.bmAttributes = BUS_POWERED,
242+
.bMaxPower = MILLIAMPS(250),
243+
244+
.interface = interfaces,
245+
};
246+
247+
248+
static enum usbd_request_return_codes hid_control_callback(usbd_device *dev,
249+
struct usb_setup_data *req,
250+
uint8_t **buf, uint16_t *len,
251+
usbd_control_complete_callback *complete)
252+
{
253+
(void)dev;
254+
(void)complete;
255+
256+
if( req->bmRequestType != REQ_INTERFACE ||
257+
req->bRequest != USB_REQ_GET_DESCRIPTOR ||
258+
req->wValue != (USB_HID_DT_REPORT<<8) )
259+
return USBD_REQ_NOTSUPP;
260+
261+
*buf = report_descriptor;
262+
*len = usb_hid_desciptor.wDescriptorLength;
263+
264+
return USBD_REQ_HANDLED;
265+
}
266+
267+
static void hid_set_config(usbd_device *dev, uint16_t wValue)
268+
{
269+
(void)wValue;
270+
271+
usbd_ep_setup(dev, USB_ENDPOINT_ADDR_IN(1), USB_ENDPOINT_ATTR_INTERRUPT, endpoint_desc.wMaxPacketSize, NULL);
272+
273+
usbd_register_control_callback(dev,
274+
USB_REQ_TYPE_STANDARD|USB_REQ_TYPE_INTERFACE,
275+
USB_REQ_TYPE_TYPE|USB_REQ_TYPE_RECIPIENT,
276+
hid_control_callback);
277+
}
278+
279+
int usb_hid_keypad_init(const uint32_t keys[], size_t n_keys)
280+
{
281+
size_t len = create_hid_keypad_descriptor(report_descriptor, sizeof(report_descriptor),
282+
keys, n_keys);
283+
284+
if (len == 0)
285+
return 0;
286+
287+
usb_hid_desciptor.wDescriptorLength = len;
288+
289+
endpoint_desc.wMaxPacketSize = report_size(n_keys);
290+
report_bits = n_keys;
291+
292+
device = usbd_init(&st_usbfs_v2_usb_driver, &device_desc, &config_desc,
293+
string_descriptors, N_STRING_DESCRIPTORS,
294+
control, sizeof(control));
295+
296+
usbd_register_set_config_callback(device, hid_set_config);
297+
298+
need_update = 1;
299+
300+
return 1;
301+
}
302+
303+
void usb_hid_keypad_key_up(uint32_t key_index)
304+
{
305+
if (key_index < report_bits)
306+
report[key_index>>3] &=~ (1<<(key_index&0x7));
307+
308+
need_update = 1;
309+
}
310+
311+
void usb_hid_keypad_key_down(uint32_t key_index)
312+
{
313+
if (key_index < report_bits)
314+
report[key_index>>3] |= 1<<(key_index&0x7);
315+
316+
need_update = 1;
317+
}
318+
319+
void usb_hid_keypad_poll(void)
320+
{
321+
if (need_update)
322+
{
323+
need_update = 0;
324+
if (usbd_ep_write_packet(device, endpoint_desc.bEndpointAddress,
325+
report, endpoint_desc.wMaxPacketSize) !=
326+
endpoint_desc.wMaxPacketSize)
327+
need_update = 1;
328+
}
329+
usbd_poll(device);
330+
}
331+

0 commit comments

Comments
 (0)