28203e90 |
1 | /* |
2 | * (c) 2015 flabberast <s3+flabbergast@sdfeu.org> |
3 | * |
4 | * Based on the following work: |
5 | * - Guillaume Duc's raw hid example (MIT License) |
6 | * https://github.com/guiduc/usb-hid-chibios-example |
7 | * - PJRC Teensy examples (MIT License) |
8 | * https://www.pjrc.com/teensy/usb_keyboard.html |
9 | * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) |
10 | * https://github.com/tmk/tmk_keyboard/ |
11 | * - ChibiOS demo code (Apache 2.0 License) |
12 | * http://www.chibios.org |
13 | * |
14 | * Since some GPL'd code is used, this work is licensed under |
15 | * GPL v2 or later. |
16 | */ |
17 | |
18 | #include "ch.h" |
19 | #include "hal.h" |
20 | |
21 | #include "usb_main.h" |
22 | |
23 | #include "host.h" |
24 | #include "debug.h" |
25 | #include "suspend.h" |
26 | #ifdef SLEEP_LED_ENABLE |
27 | #include "sleep_led.h" |
28 | #include "led.h" |
29 | #endif |
71381457 |
30 | #include "hook.h" |
31 | |
32 | /* TMK hooks */ |
33 | __attribute__((weak)) |
34 | void hook_usb_wakeup(void) { |
35 | #ifdef SLEEP_LED_ENABLE |
36 | sleep_led_disable(); |
37 | // NOTE: converters may not accept this |
38 | led_set(host_keyboard_leds()); |
39 | #endif /* SLEEP_LED_ENABLE */ |
40 | } |
41 | |
42 | __attribute__((weak)) |
43 | void hook_usb_suspend_entry(void) { |
44 | #ifdef SLEEP_LED_ENABLE |
45 | sleep_led_enable(); |
46 | #endif /* SLEEP_LED_ENABLE */ |
47 | } |
48 | |
28203e90 |
49 | |
50 | /* --------------------------------------------------------- |
51 | * Global interface variables and declarations |
52 | * --------------------------------------------------------- |
53 | */ |
54 | |
71381457 |
55 | uint8_t keyboard_idle __attribute__((aligned(2))) = 0; |
56 | uint8_t keyboard_protocol __attribute__((aligned(2))) = 1; |
57 | uint16_t keyboard_led_stats __attribute__((aligned(2))) = 0; |
28203e90 |
58 | volatile uint16_t keyboard_idle_count = 0; |
59 | static virtual_timer_t keyboard_idle_timer; |
60 | static void keyboard_idle_timer_cb(void *arg); |
61 | #ifdef NKRO_ENABLE |
62 | extern bool keyboard_nkro; |
63 | #endif /* NKRO_ENABLE */ |
64 | |
65 | report_keyboard_t keyboard_report_sent = {{0}}; |
66 | #ifdef MOUSE_ENABLE |
67 | report_mouse_t mouse_report_blank = {0}; |
68 | #endif /* MOUSE_ENABLE */ |
69 | #ifdef EXTRAKEY_ENABLE |
70 | uint8_t extra_report_blank[3] = {0}; |
71 | #endif /* EXTRAKEY_ENABLE */ |
72 | |
73 | #ifdef CONSOLE_ENABLE |
74 | /* The emission buffers queue */ |
75 | output_buffers_queue_t console_buf_queue; |
76 | static uint8_t console_queue_buffer[BQ_BUFFER_SIZE(CONSOLE_QUEUE_CAPACITY, CONSOLE_EPSIZE)]; |
77 | |
78 | static virtual_timer_t console_flush_timer; |
79 | void console_queue_onotify(io_buffers_queue_t *bqp); |
80 | static void console_flush_cb(void *arg); |
81 | #endif /* CONSOLE_ENABLE */ |
82 | |
83 | /* --------------------------------------------------------- |
84 | * Descriptors and USB driver objects |
85 | * --------------------------------------------------------- |
86 | */ |
87 | |
88 | /* HID specific constants */ |
89 | #define USB_DESCRIPTOR_HID 0x21 |
90 | #define USB_DESCRIPTOR_HID_REPORT 0x22 |
91 | #define HID_GET_REPORT 0x01 |
92 | #define HID_GET_IDLE 0x02 |
93 | #define HID_GET_PROTOCOL 0x03 |
94 | #define HID_SET_REPORT 0x09 |
95 | #define HID_SET_IDLE 0x0A |
96 | #define HID_SET_PROTOCOL 0x0B |
97 | |
98 | /* USB Device Descriptor */ |
99 | static const uint8_t usb_device_descriptor_data[] = { |
100 | USB_DESC_DEVICE(0x0200, // bcdUSB (1.1) |
101 | 0, // bDeviceClass (defined in later in interface) |
102 | 0, // bDeviceSubClass |
103 | 0, // bDeviceProtocol |
104 | 64, // bMaxPacketSize (64 bytes) (the driver didn't work with 32) |
105 | VENDOR_ID, // idVendor |
106 | PRODUCT_ID, // idProduct |
107 | DEVICE_VER, // bcdDevice |
108 | 1, // iManufacturer |
109 | 2, // iProduct |
110 | 3, // iSerialNumber |
111 | 1) // bNumConfigurations |
112 | }; |
113 | |
114 | /* Device Descriptor wrapper */ |
115 | static const USBDescriptor usb_device_descriptor = { |
116 | sizeof usb_device_descriptor_data, |
117 | usb_device_descriptor_data |
118 | }; |
119 | |
120 | /* |
121 | * HID Report Descriptor |
122 | * |
123 | * See "Device Class Definition for Human Interface Devices (HID)" |
124 | * (http://www.usb.org/developers/hidpage/HID1_11.pdf) for the |
125 | * detailed descrition of all the fields |
126 | */ |
127 | |
128 | /* Keyboard Protocol 1, HID 1.11 spec, Appendix B, page 59-60 */ |
129 | static const uint8_t keyboard_hid_report_desc_data[] = { |
130 | 0x05, 0x01, // Usage Page (Generic Desktop), |
131 | 0x09, 0x06, // Usage (Keyboard), |
132 | 0xA1, 0x01, // Collection (Application), |
133 | 0x75, 0x01, // Report Size (1), |
134 | 0x95, 0x08, // Report Count (8), |
135 | 0x05, 0x07, // Usage Page (Key Codes), |
136 | 0x19, 0xE0, // Usage Minimum (224), |
137 | 0x29, 0xE7, // Usage Maximum (231), |
138 | 0x15, 0x00, // Logical Minimum (0), |
139 | 0x25, 0x01, // Logical Maximum (1), |
140 | 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte |
141 | 0x95, 0x01, // Report Count (1), |
142 | 0x75, 0x08, // Report Size (8), |
143 | 0x81, 0x03, // Input (Constant), ;Reserved byte |
144 | 0x95, 0x05, // Report Count (5), |
145 | 0x75, 0x01, // Report Size (1), |
146 | 0x05, 0x08, // Usage Page (LEDs), |
147 | 0x19, 0x01, // Usage Minimum (1), |
148 | 0x29, 0x05, // Usage Maximum (5), |
149 | 0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report |
150 | 0x95, 0x01, // Report Count (1), |
151 | 0x75, 0x03, // Report Size (3), |
152 | 0x91, 0x03, // Output (Constant), ;LED report padding |
153 | 0x95, KBD_REPORT_KEYS, // Report Count (), |
154 | 0x75, 0x08, // Report Size (8), |
155 | 0x15, 0x00, // Logical Minimum (0), |
7ce326de |
156 | 0x26, 0xFF, 0x00, // Logical Maximum(255), |
28203e90 |
157 | 0x05, 0x07, // Usage Page (Key Codes), |
158 | 0x19, 0x00, // Usage Minimum (0), |
159 | 0x29, 0xFF, // Usage Maximum (255), |
160 | 0x81, 0x00, // Input (Data, Array), |
161 | 0xc0 // End Collection |
162 | }; |
163 | /* wrapper */ |
164 | static const USBDescriptor keyboard_hid_report_descriptor = { |
165 | sizeof keyboard_hid_report_desc_data, |
166 | keyboard_hid_report_desc_data |
167 | }; |
168 | |
169 | #ifdef NKRO_ENABLE |
170 | static const uint8_t nkro_hid_report_desc_data[] = { |
171 | 0x05, 0x01, // Usage Page (Generic Desktop), |
172 | 0x09, 0x06, // Usage (Keyboard), |
173 | 0xA1, 0x01, // Collection (Application), |
174 | // bitmap of modifiers |
175 | 0x75, 0x01, // Report Size (1), |
176 | 0x95, 0x08, // Report Count (8), |
177 | 0x05, 0x07, // Usage Page (Key Codes), |
178 | 0x19, 0xE0, // Usage Minimum (224), |
179 | 0x29, 0xE7, // Usage Maximum (231), |
180 | 0x15, 0x00, // Logical Minimum (0), |
181 | 0x25, 0x01, // Logical Maximum (1), |
182 | 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte |
183 | // LED output report |
184 | 0x95, 0x05, // Report Count (5), |
185 | 0x75, 0x01, // Report Size (1), |
186 | 0x05, 0x08, // Usage Page (LEDs), |
187 | 0x19, 0x01, // Usage Minimum (1), |
188 | 0x29, 0x05, // Usage Maximum (5), |
189 | 0x91, 0x02, // Output (Data, Variable, Absolute), |
190 | 0x95, 0x01, // Report Count (1), |
191 | 0x75, 0x03, // Report Size (3), |
192 | 0x91, 0x03, // Output (Constant), |
193 | // bitmap of keys |
194 | 0x95, NKRO_REPORT_KEYS * 8, // Report Count (), |
195 | 0x75, 0x01, // Report Size (1), |
196 | 0x15, 0x00, // Logical Minimum (0), |
197 | 0x25, 0x01, // Logical Maximum(1), |
198 | 0x05, 0x07, // Usage Page (Key Codes), |
199 | 0x19, 0x00, // Usage Minimum (0), |
200 | 0x29, NKRO_REPORT_KEYS * 8 - 1, // Usage Maximum (), |
201 | 0x81, 0x02, // Input (Data, Variable, Absolute), |
202 | 0xc0 // End Collection |
203 | }; |
204 | /* wrapper */ |
205 | static const USBDescriptor nkro_hid_report_descriptor = { |
206 | sizeof nkro_hid_report_desc_data, |
207 | nkro_hid_report_desc_data |
208 | }; |
209 | #endif /* NKRO_ENABLE */ |
210 | |
211 | #ifdef MOUSE_ENABLE |
212 | /* Mouse Protocol 1, HID 1.11 spec, Appendix B, page 59-60, with wheel extension |
213 | * http://www.microchip.com/forums/tm.aspx?high=&m=391435&mpage=1#391521 |
214 | * http://www.keil.com/forum/15671/ |
215 | * http://www.microsoft.com/whdc/device/input/wheel.mspx */ |
216 | static const uint8_t mouse_hid_report_desc_data[] = { |
217 | /* mouse */ |
218 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) |
219 | 0x09, 0x02, // USAGE (Mouse) |
220 | 0xa1, 0x01, // COLLECTION (Application) |
221 | //0x85, REPORT_ID_MOUSE, // REPORT_ID (1) |
222 | 0x09, 0x01, // USAGE (Pointer) |
223 | 0xa1, 0x00, // COLLECTION (Physical) |
224 | // ---------------------------- Buttons |
225 | 0x05, 0x09, // USAGE_PAGE (Button) |
226 | 0x19, 0x01, // USAGE_MINIMUM (Button 1) |
227 | 0x29, 0x05, // USAGE_MAXIMUM (Button 5) |
228 | 0x15, 0x00, // LOGICAL_MINIMUM (0) |
229 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) |
230 | 0x75, 0x01, // REPORT_SIZE (1) |
231 | 0x95, 0x05, // REPORT_COUNT (5) |
232 | 0x81, 0x02, // INPUT (Data,Var,Abs) |
233 | 0x75, 0x03, // REPORT_SIZE (3) |
234 | 0x95, 0x01, // REPORT_COUNT (1) |
235 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) |
236 | // ---------------------------- X,Y position |
237 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) |
238 | 0x09, 0x30, // USAGE (X) |
239 | 0x09, 0x31, // USAGE (Y) |
240 | 0x15, 0x81, // LOGICAL_MINIMUM (-127) |
241 | 0x25, 0x7f, // LOGICAL_MAXIMUM (127) |
242 | 0x75, 0x08, // REPORT_SIZE (8) |
243 | 0x95, 0x02, // REPORT_COUNT (2) |
244 | 0x81, 0x06, // INPUT (Data,Var,Rel) |
245 | // ---------------------------- Vertical wheel |
246 | 0x09, 0x38, // USAGE (Wheel) |
247 | 0x15, 0x81, // LOGICAL_MINIMUM (-127) |
248 | 0x25, 0x7f, // LOGICAL_MAXIMUM (127) |
249 | 0x35, 0x00, // PHYSICAL_MINIMUM (0) - reset physical |
250 | 0x45, 0x00, // PHYSICAL_MAXIMUM (0) |
251 | 0x75, 0x08, // REPORT_SIZE (8) |
252 | 0x95, 0x01, // REPORT_COUNT (1) |
253 | 0x81, 0x06, // INPUT (Data,Var,Rel) |
254 | // ---------------------------- Horizontal wheel |
255 | 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) |
256 | 0x0a, 0x38, 0x02, // USAGE (AC Pan) |
257 | 0x15, 0x81, // LOGICAL_MINIMUM (-127) |
258 | 0x25, 0x7f, // LOGICAL_MAXIMUM (127) |
259 | 0x75, 0x08, // REPORT_SIZE (8) |
260 | 0x95, 0x01, // REPORT_COUNT (1) |
261 | 0x81, 0x06, // INPUT (Data,Var,Rel) |
262 | 0xc0, // END_COLLECTION |
263 | 0xc0, // END_COLLECTION |
264 | }; |
265 | /* wrapper */ |
266 | static const USBDescriptor mouse_hid_report_descriptor = { |
267 | sizeof mouse_hid_report_desc_data, |
268 | mouse_hid_report_desc_data |
269 | }; |
270 | #endif /* MOUSE_ENABLE */ |
271 | |
272 | #ifdef CONSOLE_ENABLE |
273 | static const uint8_t console_hid_report_desc_data[] = { |
274 | 0x06, 0x31, 0xFF, // Usage Page 0xFF31 (vendor defined) |
275 | 0x09, 0x74, // Usage 0x74 |
276 | 0xA1, 0x53, // Collection 0x53 |
277 | 0x75, 0x08, // report size = 8 bits |
278 | 0x15, 0x00, // logical minimum = 0 |
279 | 0x26, 0xFF, 0x00, // logical maximum = 255 |
280 | 0x95, CONSOLE_EPSIZE, // report count |
281 | 0x09, 0x75, // usage |
282 | 0x81, 0x02, // Input (array) |
283 | 0xC0 // end collection |
284 | }; |
285 | /* wrapper */ |
286 | static const USBDescriptor console_hid_report_descriptor = { |
287 | sizeof console_hid_report_desc_data, |
288 | console_hid_report_desc_data |
289 | }; |
290 | #endif /* CONSOLE_ENABLE */ |
291 | |
292 | #ifdef EXTRAKEY_ENABLE |
293 | /* audio controls & system controls |
294 | * http://www.microsoft.com/whdc/archive/w2kbd.mspx */ |
295 | static const uint8_t extra_hid_report_desc_data[] = { |
296 | /* system control */ |
297 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) |
298 | 0x09, 0x80, // USAGE (System Control) |
299 | 0xa1, 0x01, // COLLECTION (Application) |
300 | 0x85, REPORT_ID_SYSTEM, // REPORT_ID (2) |
301 | 0x15, 0x01, // LOGICAL_MINIMUM (0x1) |
7ce326de |
302 | 0x26, 0xb7, 0x00, // LOGICAL_MAXIMUM (0xb7) |
28203e90 |
303 | 0x19, 0x01, // USAGE_MINIMUM (0x1) |
304 | 0x29, 0xb7, // USAGE_MAXIMUM (0xb7) |
305 | 0x75, 0x10, // REPORT_SIZE (16) |
306 | 0x95, 0x01, // REPORT_COUNT (1) |
307 | 0x81, 0x00, // INPUT (Data,Array,Abs) |
308 | 0xc0, // END_COLLECTION |
309 | /* consumer */ |
310 | 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) |
311 | 0x09, 0x01, // USAGE (Consumer Control) |
312 | 0xa1, 0x01, // COLLECTION (Application) |
313 | 0x85, REPORT_ID_CONSUMER, // REPORT_ID (3) |
314 | 0x15, 0x01, // LOGICAL_MINIMUM (0x1) |
315 | 0x26, 0x9c, 0x02, // LOGICAL_MAXIMUM (0x29c) |
316 | 0x19, 0x01, // USAGE_MINIMUM (0x1) |
317 | 0x2a, 0x9c, 0x02, // USAGE_MAXIMUM (0x29c) |
318 | 0x75, 0x10, // REPORT_SIZE (16) |
319 | 0x95, 0x01, // REPORT_COUNT (1) |
320 | 0x81, 0x00, // INPUT (Data,Array,Abs) |
321 | 0xc0, // END_COLLECTION |
322 | }; |
323 | /* wrapper */ |
324 | static const USBDescriptor extra_hid_report_descriptor = { |
325 | sizeof extra_hid_report_desc_data, |
326 | extra_hid_report_desc_data |
327 | }; |
328 | #endif /* EXTRAKEY_ENABLE */ |
329 | |
330 | |
331 | /* |
332 | * Configuration Descriptor tree for a HID device |
333 | * |
334 | * The HID Specifications version 1.11 require the following order: |
335 | * - Configuration Descriptor |
336 | * - Interface Descriptor |
337 | * - HID Descriptor |
338 | * - Endpoints Descriptors |
339 | */ |
340 | #define KBD_HID_DESC_NUM 0 |
341 | #define KBD_HID_DESC_OFFSET (9 + (9 + 9 + 7) * KBD_HID_DESC_NUM + 9) |
342 | |
343 | #ifdef MOUSE_ENABLE |
344 | # define MOUSE_HID_DESC_NUM (KBD_HID_DESC_NUM + 1) |
345 | # define MOUSE_HID_DESC_OFFSET (9 + (9 + 9 + 7) * MOUSE_HID_DESC_NUM + 9) |
346 | #else /* MOUSE_ENABLE */ |
347 | # define MOUSE_HID_DESC_NUM (KBD_HID_DESC_NUM + 0) |
348 | #endif /* MOUSE_ENABLE */ |
349 | |
350 | #ifdef CONSOLE_ENABLE |
351 | #define CONSOLE_HID_DESC_NUM (MOUSE_HID_DESC_NUM + 1) |
352 | #define CONSOLE_HID_DESC_OFFSET (9 + (9 + 9 + 7) * CONSOLE_HID_DESC_NUM + 9) |
353 | #else /* CONSOLE_ENABLE */ |
354 | # define CONSOLE_HID_DESC_NUM (MOUSE_HID_DESC_NUM + 0) |
355 | #endif /* CONSOLE_ENABLE */ |
356 | |
357 | #ifdef EXTRAKEY_ENABLE |
358 | # define EXTRA_HID_DESC_NUM (CONSOLE_HID_DESC_NUM + 1) |
359 | # define EXTRA_HID_DESC_OFFSET (9 + (9 + 9 + 7) * EXTRA_HID_DESC_NUM + 9) |
360 | #else /* EXTRAKEY_ENABLE */ |
361 | # define EXTRA_HID_DESC_NUM (CONSOLE_HID_DESC_NUM + 0) |
362 | #endif /* EXTRAKEY_ENABLE */ |
363 | |
364 | #ifdef NKRO_ENABLE |
365 | # define NKRO_HID_DESC_NUM (EXTRA_HID_DESC_NUM + 1) |
366 | # define NKRO_HID_DESC_OFFSET (9 + (9 + 9 + 7) * EXTRA_HID_DESC_NUM + 9) |
367 | #else /* NKRO_ENABLE */ |
368 | # define NKRO_HID_DESC_NUM (EXTRA_HID_DESC_NUM + 0) |
369 | #endif /* NKRO_ENABLE */ |
370 | |
371 | #define NUM_INTERFACES (NKRO_HID_DESC_NUM + 1) |
372 | #define CONFIG1_DESC_SIZE (9 + (9 + 9 + 7) * NUM_INTERFACES) |
373 | |
374 | static const uint8_t hid_configuration_descriptor_data[] = { |
375 | /* Configuration Descriptor (9 bytes) USB spec 9.6.3, page 264-266, Table 9-10 */ |
376 | USB_DESC_CONFIGURATION(CONFIG1_DESC_SIZE, // wTotalLength |
377 | NUM_INTERFACES, // bNumInterfaces |
378 | 1, // bConfigurationValue |
379 | 0, // iConfiguration |
380 | 0xA0, // bmAttributes (RESERVED|REMOTEWAKEUP) |
381 | 50), // bMaxPower (50mA) |
382 | |
383 | /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ |
384 | USB_DESC_INTERFACE(KBD_INTERFACE, // bInterfaceNumber |
385 | 0, // bAlternateSetting |
386 | 1, // bNumEndpoints |
387 | 0x03, // bInterfaceClass: HID |
388 | 0x01, // bInterfaceSubClass: Boot |
389 | 0x01, // bInterfaceProtocol: Keyboard |
390 | 0), // iInterface |
391 | |
392 | /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ |
393 | USB_DESC_BYTE(9), // bLength |
394 | USB_DESC_BYTE(0x21), // bDescriptorType (HID class) |
395 | USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 |
396 | USB_DESC_BYTE(0), // bCountryCode |
397 | USB_DESC_BYTE(1), // bNumDescriptors |
398 | USB_DESC_BYTE(0x22), // bDescriptorType (report desc) |
399 | USB_DESC_WORD(sizeof(keyboard_hid_report_desc_data)), // wDescriptorLength |
400 | |
401 | /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ |
402 | USB_DESC_ENDPOINT(KBD_ENDPOINT | 0x80, // bEndpointAddress |
403 | 0x03, // bmAttributes (Interrupt) |
404 | KBD_EPSIZE,// wMaxPacketSize |
405 | 10), // bInterval |
406 | |
407 | #ifdef MOUSE_ENABLE |
408 | /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ |
409 | USB_DESC_INTERFACE(MOUSE_INTERFACE, // bInterfaceNumber |
410 | 0, // bAlternateSetting |
411 | 1, // bNumEndpoints |
412 | 0x03, // bInterfaceClass (0x03 = HID) |
413 | // ThinkPad T23 BIOS doesn't work with boot mouse. |
414 | 0x00, // bInterfaceSubClass (0x01 = Boot) |
415 | 0x00, // bInterfaceProtocol (0x02 = Mouse) |
416 | /* |
417 | 0x01, // bInterfaceSubClass (0x01 = Boot) |
418 | 0x02, // bInterfaceProtocol (0x02 = Mouse) |
419 | */ |
420 | 0), // iInterface |
421 | |
422 | /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ |
423 | USB_DESC_BYTE(9), // bLength |
424 | USB_DESC_BYTE(0x21), // bDescriptorType (HID class) |
425 | USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 |
426 | USB_DESC_BYTE(0), // bCountryCode |
427 | USB_DESC_BYTE(1), // bNumDescriptors |
428 | USB_DESC_BYTE(0x22), // bDescriptorType (report desc) |
429 | USB_DESC_WORD(sizeof(mouse_hid_report_desc_data)), // wDescriptorLength |
430 | |
431 | /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ |
432 | USB_DESC_ENDPOINT(MOUSE_ENDPOINT | 0x80, // bEndpointAddress |
433 | 0x03, // bmAttributes (Interrupt) |
434 | MOUSE_EPSIZE, // wMaxPacketSize |
435 | 1), // bInterval |
436 | #endif /* MOUSE_ENABLE */ |
437 | |
438 | #ifdef CONSOLE_ENABLE |
439 | /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ |
440 | USB_DESC_INTERFACE(CONSOLE_INTERFACE, // bInterfaceNumber |
441 | 0, // bAlternateSetting |
442 | 1, // bNumEndpoints |
443 | 0x03, // bInterfaceClass: HID |
444 | 0x00, // bInterfaceSubClass: None |
445 | 0x00, // bInterfaceProtocol: None |
446 | 0), // iInterface |
447 | |
448 | /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ |
449 | USB_DESC_BYTE(9), // bLength |
450 | USB_DESC_BYTE(0x21), // bDescriptorType (HID class) |
451 | USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 |
452 | USB_DESC_BYTE(0), // bCountryCode |
453 | USB_DESC_BYTE(1), // bNumDescriptors |
454 | USB_DESC_BYTE(0x22), // bDescriptorType (report desc) |
455 | USB_DESC_WORD(sizeof(console_hid_report_desc_data)), // wDescriptorLength |
456 | |
457 | /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ |
458 | USB_DESC_ENDPOINT(CONSOLE_ENDPOINT | 0x80, // bEndpointAddress |
459 | 0x03, // bmAttributes (Interrupt) |
460 | CONSOLE_EPSIZE, // wMaxPacketSize |
461 | 1), // bInterval |
462 | #endif /* CONSOLE_ENABLE */ |
463 | |
464 | #ifdef EXTRAKEY_ENABLE |
465 | /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ |
466 | USB_DESC_INTERFACE(EXTRA_INTERFACE, // bInterfaceNumber |
467 | 0, // bAlternateSetting |
468 | 1, // bNumEndpoints |
469 | 0x03, // bInterfaceClass: HID |
470 | 0x00, // bInterfaceSubClass: None |
471 | 0x00, // bInterfaceProtocol: None |
472 | 0), // iInterface |
473 | |
474 | /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ |
475 | USB_DESC_BYTE(9), // bLength |
476 | USB_DESC_BYTE(0x21), // bDescriptorType (HID class) |
477 | USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 |
478 | USB_DESC_BYTE(0), // bCountryCode |
479 | USB_DESC_BYTE(1), // bNumDescriptors |
480 | USB_DESC_BYTE(0x22), // bDescriptorType (report desc) |
481 | USB_DESC_WORD(sizeof(extra_hid_report_desc_data)), // wDescriptorLength |
482 | |
483 | /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ |
484 | USB_DESC_ENDPOINT(EXTRA_ENDPOINT | 0x80, // bEndpointAddress |
485 | 0x03, // bmAttributes (Interrupt) |
486 | EXTRA_EPSIZE, // wMaxPacketSize |
487 | 10), // bInterval |
488 | #endif /* EXTRAKEY_ENABLE */ |
489 | |
490 | #ifdef NKRO_ENABLE |
491 | /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ |
492 | USB_DESC_INTERFACE(NKRO_INTERFACE, // bInterfaceNumber |
493 | 0, // bAlternateSetting |
494 | 1, // bNumEndpoints |
495 | 0x03, // bInterfaceClass: HID |
496 | 0x00, // bInterfaceSubClass: None |
497 | 0x00, // bInterfaceProtocol: None |
498 | 0), // iInterface |
499 | |
500 | /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ |
501 | USB_DESC_BYTE(9), // bLength |
502 | USB_DESC_BYTE(0x21), // bDescriptorType (HID class) |
503 | USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 |
504 | USB_DESC_BYTE(0), // bCountryCode |
505 | USB_DESC_BYTE(1), // bNumDescriptors |
506 | USB_DESC_BYTE(0x22), // bDescriptorType (report desc) |
507 | USB_DESC_WORD(sizeof(nkro_hid_report_desc_data)), // wDescriptorLength |
508 | |
509 | /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ |
510 | USB_DESC_ENDPOINT(NKRO_ENDPOINT | 0x80, // bEndpointAddress |
511 | 0x03, // bmAttributes (Interrupt) |
512 | NKRO_EPSIZE, // wMaxPacketSize |
513 | 1), // bInterval |
514 | #endif /* NKRO_ENABLE */ |
515 | }; |
516 | |
517 | /* Configuration Descriptor wrapper */ |
518 | static const USBDescriptor hid_configuration_descriptor = { |
519 | sizeof hid_configuration_descriptor_data, |
520 | hid_configuration_descriptor_data |
521 | }; |
522 | |
523 | /* wrappers */ |
524 | #define HID_DESCRIPTOR_SIZE 9 |
525 | static const USBDescriptor keyboard_hid_descriptor = { |
526 | HID_DESCRIPTOR_SIZE, |
527 | &hid_configuration_descriptor_data[KBD_HID_DESC_OFFSET] |
528 | }; |
529 | #ifdef MOUSE_ENABLE |
530 | static const USBDescriptor mouse_hid_descriptor = { |
531 | HID_DESCRIPTOR_SIZE, |
532 | &hid_configuration_descriptor_data[MOUSE_HID_DESC_OFFSET] |
533 | }; |
534 | #endif /* MOUSE_ENABLE */ |
535 | #ifdef CONSOLE_ENABLE |
536 | static const USBDescriptor console_hid_descriptor = { |
537 | HID_DESCRIPTOR_SIZE, |
538 | &hid_configuration_descriptor_data[CONSOLE_HID_DESC_OFFSET] |
539 | }; |
540 | #endif /* CONSOLE_ENABLE */ |
541 | #ifdef EXTRAKEY_ENABLE |
542 | static const USBDescriptor extra_hid_descriptor = { |
543 | HID_DESCRIPTOR_SIZE, |
544 | &hid_configuration_descriptor_data[EXTRA_HID_DESC_OFFSET] |
545 | }; |
546 | #endif /* EXTRAKEY_ENABLE */ |
547 | #ifdef NKRO_ENABLE |
548 | static const USBDescriptor nkro_hid_descriptor = { |
549 | HID_DESCRIPTOR_SIZE, |
550 | &hid_configuration_descriptor_data[NKRO_HID_DESC_OFFSET] |
551 | }; |
552 | #endif /* NKRO_ENABLE */ |
553 | |
554 | |
555 | /* U.S. English language identifier */ |
556 | static const uint8_t usb_string_langid[] = { |
557 | USB_DESC_BYTE(4), // bLength |
558 | USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType |
559 | USB_DESC_WORD(0x0409) // wLANGID (U.S. English) |
560 | }; |
561 | |
562 | /* ugly ugly hack */ |
563 | #define PP_NARG(...) \ |
564 | PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) |
565 | #define PP_NARG_(...) \ |
566 | PP_ARG_N(__VA_ARGS__) |
567 | #define PP_ARG_N( \ |
568 | _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ |
569 | _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ |
570 | _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ |
571 | _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ |
572 | _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ |
573 | _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ |
574 | _61,_62,_63,N,...) N |
575 | #define PP_RSEQ_N() \ |
576 | 63,62,61,60, \ |
577 | 59,58,57,56,55,54,53,52,51,50, \ |
578 | 49,48,47,46,45,44,43,42,41,40, \ |
579 | 39,38,37,36,35,34,33,32,31,30, \ |
580 | 29,28,27,26,25,24,23,22,21,20, \ |
581 | 19,18,17,16,15,14,13,12,11,10, \ |
582 | 9,8,7,6,5,4,3,2,1,0 |
583 | |
584 | /* Vendor string = manufacturer */ |
585 | static const uint8_t usb_string_vendor[] = { |
586 | USB_DESC_BYTE(PP_NARG(USBSTR_MANUFACTURER)+2), // bLength |
587 | USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType |
588 | USBSTR_MANUFACTURER |
589 | }; |
590 | |
591 | /* Device Description string = product */ |
592 | static const uint8_t usb_string_description[] = { |
593 | USB_DESC_BYTE(PP_NARG(USBSTR_PRODUCT)+2), // bLength |
594 | USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType |
595 | USBSTR_PRODUCT |
596 | }; |
597 | |
598 | /* Serial Number string (will be filled by the function init_usb_serial_string) */ |
599 | static uint8_t usb_string_serial[] = { |
600 | USB_DESC_BYTE(22), // bLength |
601 | USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType |
602 | '0', 0, 'x', 0, 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'B', 0, 'E', 0, 'E', 0, 'F', 0 |
603 | }; |
604 | |
605 | /* Strings wrappers array */ |
606 | static const USBDescriptor usb_strings[] = { |
607 | { sizeof usb_string_langid, usb_string_langid } |
608 | , |
609 | { sizeof usb_string_vendor, usb_string_vendor } |
610 | , |
611 | { sizeof usb_string_description, usb_string_description } |
612 | , |
613 | { sizeof usb_string_serial, usb_string_serial } |
614 | }; |
615 | |
616 | /* |
617 | * Handles the GET_DESCRIPTOR callback |
618 | * |
619 | * Returns the proper descriptor |
620 | */ |
621 | static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t lang) { |
622 | (void)usbp; |
623 | (void)lang; |
624 | switch(dtype) { |
625 | /* Generic descriptors */ |
626 | case USB_DESCRIPTOR_DEVICE: /* Device Descriptor */ |
627 | return &usb_device_descriptor; |
628 | |
629 | case USB_DESCRIPTOR_CONFIGURATION: /* Configuration Descriptor */ |
630 | return &hid_configuration_descriptor; |
631 | |
632 | case USB_DESCRIPTOR_STRING: /* Strings */ |
633 | if(dindex < 4) |
634 | return &usb_strings[dindex]; |
635 | break; |
636 | |
637 | /* HID specific descriptors */ |
638 | case USB_DESCRIPTOR_HID: /* HID Descriptors */ |
639 | switch(lang) { /* yea, poor label, it's actually wIndex from the setup packet */ |
640 | case KBD_INTERFACE: |
641 | return &keyboard_hid_descriptor; |
642 | |
643 | #ifdef MOUSE_ENABLE |
644 | case MOUSE_INTERFACE: |
645 | return &mouse_hid_descriptor; |
646 | #endif /* MOUSE_ENABLE */ |
647 | #ifdef CONSOLE_ENABLE |
648 | case CONSOLE_INTERFACE: |
649 | return &console_hid_descriptor; |
650 | #endif /* CONSOLE_ENABLE */ |
651 | #ifdef EXTRAKEY_ENABLE |
652 | case EXTRA_INTERFACE: |
653 | return &extra_hid_descriptor; |
654 | #endif /* EXTRAKEY_ENABLE */ |
655 | #ifdef NKRO_ENABLE |
656 | case NKRO_INTERFACE: |
657 | return &nkro_hid_descriptor; |
658 | #endif /* NKRO_ENABLE */ |
659 | } |
660 | |
661 | case USB_DESCRIPTOR_HID_REPORT: /* HID Report Descriptor */ |
662 | switch(lang) { |
663 | case KBD_INTERFACE: |
664 | return &keyboard_hid_report_descriptor; |
665 | |
666 | #ifdef MOUSE_ENABLE |
667 | case MOUSE_INTERFACE: |
668 | return &mouse_hid_report_descriptor; |
669 | #endif /* MOUSE_ENABLE */ |
670 | #ifdef CONSOLE_ENABLE |
671 | case CONSOLE_INTERFACE: |
672 | return &console_hid_report_descriptor; |
673 | #endif /* CONSOLE_ENABLE */ |
674 | #ifdef EXTRAKEY_ENABLE |
675 | case EXTRA_INTERFACE: |
676 | return &extra_hid_report_descriptor; |
677 | #endif /* EXTRAKEY_ENABLE */ |
678 | #ifdef NKRO_ENABLE |
679 | case NKRO_INTERFACE: |
680 | return &nkro_hid_report_descriptor; |
681 | #endif /* NKRO_ENABLE */ |
682 | } |
683 | } |
684 | return NULL; |
685 | } |
686 | |
687 | /* keyboard endpoint state structure */ |
688 | static USBInEndpointState kbd_ep_state; |
689 | /* keyboard endpoint initialization structure (IN) */ |
690 | static const USBEndpointConfig kbd_ep_config = { |
691 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ |
692 | NULL, /* SETUP packet notification callback */ |
693 | kbd_in_cb, /* IN notification callback */ |
694 | NULL, /* OUT notification callback */ |
695 | KBD_EPSIZE, /* IN maximum packet size */ |
696 | 0, /* OUT maximum packet size */ |
697 | &kbd_ep_state, /* IN Endpoint state */ |
698 | NULL, /* OUT endpoint state */ |
699 | 2, /* IN multiplier */ |
700 | NULL /* SETUP buffer (not a SETUP endpoint) */ |
701 | }; |
702 | |
703 | #ifdef MOUSE_ENABLE |
704 | /* mouse endpoint state structure */ |
705 | static USBInEndpointState mouse_ep_state; |
706 | |
707 | /* mouse endpoint initialization structure (IN) */ |
708 | static const USBEndpointConfig mouse_ep_config = { |
709 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ |
710 | NULL, /* SETUP packet notification callback */ |
711 | mouse_in_cb, /* IN notification callback */ |
712 | NULL, /* OUT notification callback */ |
713 | MOUSE_EPSIZE, /* IN maximum packet size */ |
714 | 0, /* OUT maximum packet size */ |
715 | &mouse_ep_state, /* IN Endpoint state */ |
716 | NULL, /* OUT endpoint state */ |
717 | 2, /* IN multiplier */ |
718 | NULL /* SETUP buffer (not a SETUP endpoint) */ |
719 | }; |
720 | #endif /* MOUSE_ENABLE */ |
721 | |
722 | #ifdef CONSOLE_ENABLE |
723 | /* console endpoint state structure */ |
724 | static USBInEndpointState console_ep_state; |
725 | |
726 | /* console endpoint initialization structure (IN) */ |
727 | static const USBEndpointConfig console_ep_config = { |
728 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ |
729 | NULL, /* SETUP packet notification callback */ |
730 | console_in_cb, /* IN notification callback */ |
731 | NULL, /* OUT notification callback */ |
732 | CONSOLE_EPSIZE, /* IN maximum packet size */ |
733 | 0, /* OUT maximum packet size */ |
734 | &console_ep_state, /* IN Endpoint state */ |
735 | NULL, /* OUT endpoint state */ |
736 | 2, /* IN multiplier */ |
737 | NULL /* SETUP buffer (not a SETUP endpoint) */ |
738 | }; |
739 | #endif /* CONSOLE_ENABLE */ |
740 | |
741 | #ifdef EXTRAKEY_ENABLE |
742 | /* extrakey endpoint state structure */ |
743 | static USBInEndpointState extra_ep_state; |
744 | |
745 | /* extrakey endpoint initialization structure (IN) */ |
746 | static const USBEndpointConfig extra_ep_config = { |
747 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ |
748 | NULL, /* SETUP packet notification callback */ |
749 | extra_in_cb, /* IN notification callback */ |
750 | NULL, /* OUT notification callback */ |
751 | EXTRA_EPSIZE, /* IN maximum packet size */ |
752 | 0, /* OUT maximum packet size */ |
753 | &extra_ep_state, /* IN Endpoint state */ |
754 | NULL, /* OUT endpoint state */ |
755 | 2, /* IN multiplier */ |
756 | NULL /* SETUP buffer (not a SETUP endpoint) */ |
757 | }; |
758 | #endif /* EXTRAKEY_ENABLE */ |
759 | |
760 | #ifdef NKRO_ENABLE |
761 | /* nkro endpoint state structure */ |
762 | static USBInEndpointState nkro_ep_state; |
763 | |
764 | /* nkro endpoint initialization structure (IN) */ |
765 | static const USBEndpointConfig nkro_ep_config = { |
766 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ |
767 | NULL, /* SETUP packet notification callback */ |
768 | nkro_in_cb, /* IN notification callback */ |
769 | NULL, /* OUT notification callback */ |
770 | NKRO_EPSIZE, /* IN maximum packet size */ |
771 | 0, /* OUT maximum packet size */ |
772 | &nkro_ep_state, /* IN Endpoint state */ |
773 | NULL, /* OUT endpoint state */ |
774 | 2, /* IN multiplier */ |
775 | NULL /* SETUP buffer (not a SETUP endpoint) */ |
776 | }; |
777 | #endif /* NKRO_ENABLE */ |
778 | |
779 | /* --------------------------------------------------------- |
780 | * USB driver functions |
781 | * --------------------------------------------------------- |
782 | */ |
783 | |
784 | /* Handles the USB driver global events |
785 | * TODO: maybe disable some things when connection is lost? */ |
786 | static void usb_event_cb(USBDriver *usbp, usbevent_t event) { |
787 | switch(event) { |
788 | case USB_EVENT_RESET: |
789 | //TODO: from ISR! print("[R]"); |
790 | return; |
791 | |
792 | case USB_EVENT_ADDRESS: |
793 | return; |
794 | |
795 | case USB_EVENT_CONFIGURED: |
796 | osalSysLockFromISR(); |
797 | /* Enable the endpoints specified into the configuration. */ |
798 | usbInitEndpointI(usbp, KBD_ENDPOINT, &kbd_ep_config); |
799 | #ifdef MOUSE_ENABLE |
800 | usbInitEndpointI(usbp, MOUSE_ENDPOINT, &mouse_ep_config); |
801 | #endif /* MOUSE_ENABLE */ |
802 | #ifdef CONSOLE_ENABLE |
803 | usbInitEndpointI(usbp, CONSOLE_ENDPOINT, &console_ep_config); |
804 | /* don't need to start the flush timer, it starts from console_in_cb automatically */ |
805 | #endif /* CONSOLE_ENABLE */ |
806 | #ifdef EXTRAKEY_ENABLE |
807 | usbInitEndpointI(usbp, EXTRA_ENDPOINT, &extra_ep_config); |
808 | #endif /* EXTRAKEY_ENABLE */ |
809 | #ifdef NKRO_ENABLE |
810 | usbInitEndpointI(usbp, NKRO_ENDPOINT, &nkro_ep_config); |
811 | #endif /* NKRO_ENABLE */ |
812 | osalSysUnlockFromISR(); |
813 | return; |
814 | |
815 | case USB_EVENT_SUSPEND: |
816 | //TODO: from ISR! print("[S]"); |
71381457 |
817 | hook_usb_suspend_entry(); |
28203e90 |
818 | return; |
819 | |
820 | case USB_EVENT_WAKEUP: |
821 | //TODO: from ISR! print("[W]"); |
822 | suspend_wakeup_init(); |
71381457 |
823 | hook_usb_wakeup(); |
28203e90 |
824 | return; |
825 | |
826 | case USB_EVENT_STALLED: |
827 | return; |
828 | } |
829 | } |
830 | |
831 | /* Function used locally in os/hal/src/usb.c for getting descriptors |
832 | * need it here for HID descriptor */ |
833 | static uint16_t get_hword(uint8_t *p) { |
834 | uint16_t hw; |
835 | |
836 | hw = (uint16_t)*p++; |
837 | hw |= (uint16_t)*p << 8U; |
838 | return hw; |
839 | } |
840 | |
841 | /* |
842 | * Appendix G: HID Request Support Requirements |
843 | * |
844 | * The following table enumerates the requests that need to be supported by various types of HID class devices. |
845 | * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol |
846 | * ------------------------------------------------------------------------------------------ |
847 | * Boot Mouse Required Optional Optional Optional Required Required |
848 | * Non-Boot Mouse Required Optional Optional Optional Optional Optional |
849 | * Boot Keyboard Required Optional Required Required Required Required |
850 | * Non-Boot Keybrd Required Optional Required Required Optional Optional |
851 | * Other Device Required Optional Optional Optional Optional Optional |
852 | */ |
853 | |
854 | /* Callback for SETUP request on the endpoint 0 (control) */ |
855 | static bool usb_request_hook_cb(USBDriver *usbp) { |
856 | const USBDescriptor *dp; |
857 | |
858 | /* usbp->setup fields: |
859 | * 0: bmRequestType (bitmask) |
860 | * 1: bRequest |
861 | * 2,3: (LSB,MSB) wValue |
862 | * 4,5: (LSB,MSB) wIndex |
863 | * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ |
864 | |
865 | /* Handle HID class specific requests */ |
866 | if(((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && |
867 | ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { |
868 | switch(usbp->setup[0] & USB_RTYPE_DIR_MASK) { |
869 | case USB_RTYPE_DIR_DEV2HOST: |
870 | switch(usbp->setup[1]) { /* bRequest */ |
871 | case HID_GET_REPORT: |
872 | switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ |
873 | case KBD_INTERFACE: |
874 | #ifdef NKRO_ENABLE |
875 | case NKRO_INTERFACE: |
876 | #endif /* NKRO_ENABLE */ |
877 | usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); |
878 | return TRUE; |
879 | break; |
880 | |
881 | #ifdef MOUSE_ENABLE |
882 | case MOUSE_INTERFACE: |
883 | usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); |
884 | return TRUE; |
885 | break; |
886 | #endif /* MOUSE_ENABLE */ |
887 | |
888 | #ifdef CONSOLE_ENABLE |
889 | case CONSOLE_INTERFACE: |
890 | usbSetupTransfer(usbp, console_queue_buffer, CONSOLE_EPSIZE, NULL); |
891 | return TRUE; |
892 | break; |
893 | #endif /* CONSOLE_ENABLE */ |
894 | |
895 | #ifdef EXTRAKEY_ENABLE |
896 | case EXTRA_INTERFACE: |
897 | if(usbp->setup[3] == 1) { /* MSB(wValue) [Report Type] == 1 [Input Report] */ |
898 | switch(usbp->setup[2]) { /* LSB(wValue) [Report ID] */ |
899 | case REPORT_ID_SYSTEM: |
900 | extra_report_blank[0] = REPORT_ID_SYSTEM; |
901 | usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL); |
902 | return TRUE; |
903 | break; |
904 | case REPORT_ID_CONSUMER: |
905 | extra_report_blank[0] = REPORT_ID_CONSUMER; |
906 | usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL); |
907 | return TRUE; |
908 | break; |
909 | default: |
910 | return FALSE; |
911 | } |
912 | } else { |
913 | return FALSE; |
914 | } |
915 | break; |
916 | #endif /* EXTRAKEY_ENABLE */ |
917 | |
918 | default: |
919 | usbSetupTransfer(usbp, NULL, 0, NULL); |
920 | return TRUE; |
921 | break; |
922 | } |
923 | break; |
924 | |
925 | case HID_GET_PROTOCOL: |
926 | if((usbp->setup[4] == KBD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ |
927 | usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); |
928 | return TRUE; |
929 | } |
930 | break; |
931 | |
932 | case HID_GET_IDLE: |
933 | usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); |
934 | return TRUE; |
935 | break; |
936 | } |
937 | break; |
938 | |
939 | case USB_RTYPE_DIR_HOST2DEV: |
940 | switch(usbp->setup[1]) { /* bRequest */ |
941 | case HID_SET_REPORT: |
942 | switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */ |
943 | case KBD_INTERFACE: |
944 | #ifdef NKRO_ENABLE |
945 | case NKRO_INTERFACE: |
946 | #endif /* NKRO_ENABLE */ |
947 | /* keyboard_led_stats = <read byte from next OUT report> |
948 | * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */ |
949 | usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL); |
950 | return TRUE; |
951 | break; |
952 | } |
953 | break; |
954 | |
955 | case HID_SET_PROTOCOL: |
956 | if((usbp->setup[4] == KBD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ |
957 | keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ |
958 | #ifdef NKRO_ENABLE |
959 | keyboard_nkro = !!keyboard_protocol; |
960 | if(!keyboard_nkro && keyboard_idle) { |
961 | #else /* NKRO_ENABLE */ |
962 | if(keyboard_idle) { |
963 | #endif /* NKRO_ENABLE */ |
964 | /* arm the idle timer if boot protocol & idle */ |
965 | osalSysLockFromISR(); |
966 | chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); |
967 | osalSysUnlockFromISR(); |
968 | } |
969 | } |
970 | usbSetupTransfer(usbp, NULL, 0, NULL); |
971 | return TRUE; |
972 | break; |
973 | |
974 | case HID_SET_IDLE: |
975 | keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ |
976 | /* arm the timer */ |
977 | #ifdef NKRO_ENABLE |
978 | if(!keyboard_nkro && keyboard_idle) { |
979 | #else /* NKRO_ENABLE */ |
980 | if(keyboard_idle) { |
981 | #endif /* NKRO_ENABLE */ |
982 | osalSysLockFromISR(); |
983 | chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); |
984 | osalSysUnlockFromISR(); |
985 | } |
986 | usbSetupTransfer(usbp, NULL, 0, NULL); |
987 | return TRUE; |
988 | break; |
989 | } |
990 | break; |
991 | } |
992 | } |
993 | |
994 | /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ |
995 | if((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { |
996 | dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); |
997 | if(dp == NULL) |
998 | return FALSE; |
999 | usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); |
1000 | return TRUE; |
1001 | } |
1002 | |
1003 | return FALSE; |
1004 | } |
1005 | |
1006 | /* Start-of-frame callback */ |
1007 | static void usb_sof_cb(USBDriver *usbp) { |
1008 | kbd_sof_cb(usbp); |
1009 | } |
1010 | |
1011 | |
1012 | /* USB driver configuration */ |
1013 | static const USBConfig usbcfg = { |
1014 | usb_event_cb, /* USB events callback */ |
1015 | usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ |
1016 | usb_request_hook_cb, /* Requests hook callback */ |
1017 | usb_sof_cb /* Start Of Frame callback */ |
1018 | }; |
1019 | |
1020 | /* |
1021 | * Initialize the USB driver |
1022 | */ |
1023 | void init_usb_driver(USBDriver *usbp) { |
1024 | /* |
1025 | * Activates the USB driver and then the USB bus pull-up on D+. |
1026 | * Note, a delay is inserted in order to not have to disconnect the cable |
1027 | * after a reset. |
1028 | */ |
1029 | usbDisconnectBus(usbp); |
1030 | chThdSleepMilliseconds(1500); |
1031 | usbStart(usbp, &usbcfg); |
1032 | usbConnectBus(usbp); |
1033 | |
1034 | chVTObjectInit(&keyboard_idle_timer); |
1035 | #ifdef CONSOLE_ENABLE |
d5689296 |
1036 | obqObjectInit(&console_buf_queue, true, console_queue_buffer, CONSOLE_EPSIZE, CONSOLE_QUEUE_CAPACITY, console_queue_onotify, (void*)usbp); |
28203e90 |
1037 | chVTObjectInit(&console_flush_timer); |
1038 | #endif |
1039 | } |
1040 | |
1041 | /* |
1042 | * Send remote wakeup packet |
1043 | * Note: should not be called from ISR |
1044 | */ |
1045 | void send_remote_wakeup(USBDriver *usbp) { |
1046 | (void)usbp; |
1047 | #if defined(K20x) || defined(KL2x) |
1048 | #if KINETIS_USB_USE_USB0 |
1049 | USB0->CTL |= USBx_CTL_RESUME; |
1050 | chThdSleepMilliseconds(15); |
1051 | USB0->CTL &= ~USBx_CTL_RESUME; |
1052 | #endif /* KINETIS_USB_USE_USB0 */ |
1053 | #elif defined(STM32F0XX) || defined(STM32F1XX) /* K20x || KL2x */ |
1054 | STM32_USB->CNTR |= CNTR_RESUME; |
1055 | chThdSleepMilliseconds(15); |
1056 | STM32_USB->CNTR &= ~CNTR_RESUME; |
1057 | #else /* STM32F0XX || STM32F1XX */ |
1058 | #warning Sending remote wakeup packet not implemented for your platform. |
1059 | #endif /* K20x || KL2x */ |
1060 | } |
1061 | |
1062 | /* --------------------------------------------------------- |
1063 | * Keyboard functions |
1064 | * --------------------------------------------------------- |
1065 | */ |
1066 | |
1067 | /* keyboard IN callback hander (a kbd report has made it IN) */ |
1068 | void kbd_in_cb(USBDriver *usbp, usbep_t ep) { |
1069 | /* STUB */ |
1070 | (void)usbp; |
1071 | (void)ep; |
1072 | } |
1073 | |
1074 | #ifdef NKRO_ENABLE |
1075 | /* nkro IN callback hander (a nkro report has made it IN) */ |
1076 | void nkro_in_cb(USBDriver *usbp, usbep_t ep) { |
1077 | /* STUB */ |
1078 | (void)usbp; |
1079 | (void)ep; |
1080 | } |
1081 | #endif /* NKRO_ENABLE */ |
1082 | |
1083 | /* start-of-frame handler |
1084 | * TODO: i guess it would be better to re-implement using timers, |
1085 | * so that this is not going to have to be checked every 1ms */ |
1086 | void kbd_sof_cb(USBDriver *usbp) { |
1087 | (void)usbp; |
1088 | } |
1089 | |
1090 | /* Idle requests timer code |
1091 | * callback (called from ISR, unlocked state) */ |
1092 | static void keyboard_idle_timer_cb(void *arg) { |
1093 | USBDriver *usbp = (USBDriver *)arg; |
1094 | |
1095 | osalSysLockFromISR(); |
1096 | |
1097 | /* check that the states of things are as they're supposed to */ |
1098 | if(usbGetDriverStateI(usbp) != USB_ACTIVE) { |
1099 | /* do not rearm the timer, should be enabled on IDLE request */ |
1100 | osalSysUnlockFromISR(); |
1101 | return; |
1102 | } |
1103 | |
1104 | #ifdef NKRO_ENABLE |
1105 | if(!keyboard_nkro && keyboard_idle) { |
1106 | #else /* NKRO_ENABLE */ |
1107 | if(keyboard_idle) { |
1108 | #endif /* NKRO_ENABLE */ |
1109 | /* TODO: are we sure we want the KBD_ENDPOINT? */ |
1110 | if(!usbGetTransmitStatusI(usbp, KBD_ENDPOINT)) { |
1111 | usbStartTransmitI(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, KBD_EPSIZE); |
1112 | } |
1113 | /* rearm the timer */ |
1114 | chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); |
1115 | } |
1116 | |
1117 | /* do not rearm the timer if the condition above fails |
1118 | * it should be enabled again on either IDLE or SET_PROTOCOL requests */ |
1119 | osalSysUnlockFromISR(); |
1120 | } |
1121 | |
1122 | /* LED status */ |
1123 | uint8_t keyboard_leds(void) { |
1124 | return (uint8_t)(keyboard_led_stats & 0xFF); |
1125 | } |
1126 | |
1127 | /* prepare and start sending a report IN |
1128 | * not callable from ISR or locked state */ |
1129 | void send_keyboard(report_keyboard_t *report) { |
1130 | osalSysLock(); |
1131 | if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { |
1132 | osalSysUnlock(); |
1133 | return; |
1134 | } |
1135 | osalSysUnlock(); |
1136 | |
1137 | #ifdef NKRO_ENABLE |
1138 | if(keyboard_nkro) { /* NKRO protocol */ |
1139 | /* need to wait until the previous packet has made it through */ |
1140 | /* can rewrite this using the synchronous API, then would wait |
1141 | * until *after* the packet has been transmitted. I think |
1142 | * this is more efficient */ |
1143 | /* busy wait, should be short and not very common */ |
1144 | osalSysLock(); |
1145 | if(usbGetTransmitStatusI(&USB_DRIVER, NKRO_ENDPOINT)) { |
1146 | /* Need to either suspend, or loop and call unlock/lock during |
1147 | * every iteration - otherwise the system will remain locked, |
1148 | * no interrupts served, so USB not going through as well. |
1149 | * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ |
1150 | osalThreadSuspendS(&(&USB_DRIVER)->epc[NKRO_ENDPOINT]->in_state->thread); |
1151 | } |
1152 | usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); |
1153 | osalSysUnlock(); |
1154 | } else |
1155 | #endif /* NKRO_ENABLE */ |
1156 | { /* boot protocol */ |
1157 | /* need to wait until the previous packet has made it through */ |
1158 | /* busy wait, should be short and not very common */ |
1159 | osalSysLock(); |
1160 | if(usbGetTransmitStatusI(&USB_DRIVER, KBD_ENDPOINT)) { |
1161 | /* Need to either suspend, or loop and call unlock/lock during |
1162 | * every iteration - otherwise the system will remain locked, |
1163 | * no interrupts served, so USB not going through as well. |
1164 | * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ |
1165 | osalThreadSuspendS(&(&USB_DRIVER)->epc[KBD_ENDPOINT]->in_state->thread); |
1166 | } |
1167 | usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); |
1168 | osalSysUnlock(); |
1169 | } |
1170 | keyboard_report_sent = *report; |
1171 | } |
1172 | |
1173 | /* --------------------------------------------------------- |
1174 | * Mouse functions |
1175 | * --------------------------------------------------------- |
1176 | */ |
1177 | |
1178 | #ifdef MOUSE_ENABLE |
1179 | |
1180 | /* mouse IN callback hander (a mouse report has made it IN) */ |
1181 | void mouse_in_cb(USBDriver *usbp, usbep_t ep) { |
1182 | (void)usbp; |
1183 | (void)ep; |
1184 | } |
1185 | |
1186 | void send_mouse(report_mouse_t *report) { |
1187 | osalSysLock(); |
1188 | if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { |
1189 | osalSysUnlock(); |
1190 | return; |
1191 | } |
1192 | osalSysUnlock(); |
1193 | |
1194 | /* TODO: LUFA manually waits for the endpoint to become ready |
1195 | * for about 10ms for mouse, kbd, system; 1ms for nkro |
1196 | * is this really needed? |
1197 | */ |
1198 | |
1199 | osalSysLock(); |
1200 | usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t)); |
1201 | osalSysUnlock(); |
1202 | } |
1203 | |
1204 | #else /* MOUSE_ENABLE */ |
1205 | void send_mouse(report_mouse_t *report) { |
1206 | (void)report; |
1207 | } |
1208 | #endif /* MOUSE_ENABLE */ |
1209 | |
1210 | /* --------------------------------------------------------- |
1211 | * Extrakey functions |
1212 | * --------------------------------------------------------- |
1213 | */ |
1214 | |
1215 | #ifdef EXTRAKEY_ENABLE |
1216 | |
1217 | /* extrakey IN callback hander */ |
1218 | void extra_in_cb(USBDriver *usbp, usbep_t ep) { |
1219 | /* STUB */ |
1220 | (void)usbp; |
1221 | (void)ep; |
1222 | } |
1223 | |
1224 | static void send_extra_report(uint8_t report_id, uint16_t data) { |
1225 | osalSysLock(); |
1226 | if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { |
1227 | osalSysUnlock(); |
1228 | return; |
1229 | } |
1230 | |
1231 | report_extra_t report = { |
1232 | .report_id = report_id, |
1233 | .usage = data |
1234 | }; |
1235 | |
1236 | usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t)); |
1237 | osalSysUnlock(); |
1238 | } |
1239 | |
1240 | void send_system(uint16_t data) { |
1241 | send_extra_report(REPORT_ID_SYSTEM, data); |
1242 | } |
1243 | |
1244 | void send_consumer(uint16_t data) { |
1245 | send_extra_report(REPORT_ID_CONSUMER, data); |
1246 | } |
1247 | |
1248 | #else /* EXTRAKEY_ENABLE */ |
1249 | void send_system(uint16_t data) { |
1250 | (void)data; |
1251 | } |
1252 | void send_consumer(uint16_t data) { |
1253 | (void)data; |
1254 | } |
1255 | #endif /* EXTRAKEY_ENABLE */ |
1256 | |
1257 | /* --------------------------------------------------------- |
1258 | * Console functions |
1259 | * --------------------------------------------------------- |
1260 | */ |
1261 | |
1262 | #ifdef CONSOLE_ENABLE |
1263 | |
1264 | /* console IN callback hander */ |
1265 | void console_in_cb(USBDriver *usbp, usbep_t ep) { |
1266 | (void)ep; /* should have ep == CONSOLE_ENDPOINT, so use that to save time/space */ |
1267 | uint8_t *buf; |
1268 | size_t n; |
1269 | |
1270 | osalSysLockFromISR(); |
1271 | |
1272 | /* rearm the timer */ |
1273 | chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); |
1274 | |
1275 | /* Freeing the buffer just transmitted, if it was not a zero size packet.*/ |
1276 | if (usbp->epc[CONSOLE_ENDPOINT]->in_state->txsize > 0U) { |
1277 | obqReleaseEmptyBufferI(&console_buf_queue); |
1278 | } |
1279 | |
1280 | /* Checking if there is a buffer ready for transmission.*/ |
1281 | buf = obqGetFullBufferI(&console_buf_queue, &n); |
1282 | |
1283 | if (buf != NULL) { |
1284 | /* The endpoint cannot be busy, we are in the context of the callback, |
1285 | so it is safe to transmit without a check.*/ |
1286 | /* Should have n == CONSOLE_EPSIZE; check it? */ |
1287 | usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); |
1288 | } else { |
1289 | /* Nothing to transmit.*/ |
1290 | } |
1291 | |
1292 | osalSysUnlockFromISR(); |
1293 | } |
1294 | |
1295 | /* Callback when data is inserted into the output queue |
1296 | * Called from a locked state */ |
1297 | void console_queue_onotify(io_buffers_queue_t *bqp) { |
1298 | size_t n; |
1299 | USBDriver *usbp = bqGetLinkX(bqp); |
1300 | |
1301 | if(usbGetDriverStateI(usbp) != USB_ACTIVE) |
1302 | return; |
1303 | |
1304 | /* Checking if there is already a transaction ongoing on the endpoint.*/ |
1305 | if (!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) { |
1306 | /* Trying to get a full buffer.*/ |
1307 | uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n); |
1308 | if (buf != NULL) { |
1309 | /* Buffer found, starting a new transaction.*/ |
1310 | /* Should have n == CONSOLE_EPSIZE; check this? */ |
1311 | usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); |
1312 | } |
1313 | } |
1314 | } |
1315 | |
1316 | /* Flush timer code |
1317 | * callback (called from ISR, unlocked state) */ |
1318 | static void console_flush_cb(void *arg) { |
1319 | USBDriver *usbp = (USBDriver *)arg; |
1320 | osalSysLockFromISR(); |
1321 | |
1322 | /* check that the states of things are as they're supposed to */ |
1323 | if(usbGetDriverStateI(usbp) != USB_ACTIVE) { |
1324 | /* rearm the timer */ |
1325 | chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); |
1326 | osalSysUnlockFromISR(); |
1327 | return; |
1328 | } |
1329 | |
1330 | /* If there is already a transaction ongoing then another one cannot be |
1331 | started.*/ |
1332 | if (usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) { |
1333 | /* rearm the timer */ |
1334 | chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); |
1335 | osalSysUnlockFromISR(); |
1336 | return; |
1337 | } |
1338 | |
1339 | /* Checking if there only a buffer partially filled, if so then it is |
1340 | enforced in the queue and transmitted.*/ |
1341 | if(obqTryFlushI(&console_buf_queue)) { |
1342 | size_t n,i; |
1343 | uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n); |
1344 | |
1345 | osalDbgAssert(buf != NULL, "queue is empty"); |
1346 | |
1347 | /* zero the rest of the buffer (buf should point to allocated space) */ |
1348 | for(i=n; i<CONSOLE_EPSIZE; i++) |
1349 | buf[i]=0; |
1350 | usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); |
1351 | } |
1352 | |
1353 | /* rearm the timer */ |
1354 | chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); |
1355 | osalSysUnlockFromISR(); |
1356 | } |
1357 | |
1358 | |
1359 | int8_t sendchar(uint8_t c) { |
1360 | osalSysLock(); |
1361 | if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { |
1362 | osalSysUnlock(); |
1363 | return 0; |
1364 | } |
1365 | osalSysUnlock(); |
71381457 |
1366 | /* Timeout after 100us if the queue is full. |
28203e90 |
1367 | * Increase this timeout if too much stuff is getting |
1368 | * dropped (i.e. the buffer is getting full too fast |
1369 | * for USB/HIDRAW to dequeue). Another possibility |
1370 | * for fixing this kind of thing is to increase |
1371 | * CONSOLE_QUEUE_CAPACITY. */ |
1372 | return(obqPutTimeout(&console_buf_queue, c, US2ST(100))); |
1373 | } |
1374 | |
1375 | #else /* CONSOLE_ENABLE */ |
1376 | int8_t sendchar(uint8_t c) { |
1377 | (void)c; |
1378 | return 0; |
1379 | } |
1380 | #endif /* CONSOLE_ENABLE */ |
1381 | |
1382 | void sendchar_pf(void *p, char c) { |
1383 | (void)p; |
1384 | sendchar((uint8_t)c); |
1385 | } |