Commit | Line | Data |
---|---|---|
4d4f7684 | 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 | ||
80c2e267 JC |
18 | /* |
19 | * Implementation notes: | |
20 | * | |
21 | * USBEndpointConfig - Configured using explicit order instead of struct member name. | |
22 | * This is due to ChibiOS hal LLD differences, which is dependent on hardware, | |
23 | * "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`. | |
24 | * Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file | |
25 | * makes the assumption this is safe to avoid littering with preprocessor directives. | |
26 | */ | |
27 | ||
4d4f7684 | 28 | #include "ch.h" |
29 | #include "hal.h" | |
30 | ||
31 | #include "usb_main.h" | |
32 | ||
33 | #include "host.h" | |
34 | #include "debug.h" | |
35 | #include "suspend.h" | |
36 | #ifdef SLEEP_LED_ENABLE | |
b624f32f | 37 | # include "sleep_led.h" |
38 | # include "led.h" | |
4d4f7684 | 39 | #endif |
5fd68266 | 40 | #include "wait.h" |
53ff8a31 | 41 | #include "usb_descriptor.h" |
e9d32b60 | 42 | #include "usb_driver.h" |
4d4f7684 | 43 | |
558f3ec1 | 44 | #ifdef NKRO_ENABLE |
b624f32f | 45 | # include "keycode_config.h" |
558f3ec1 | 46 | |
b624f32f | 47 | extern keymap_config_t keymap_config; |
558f3ec1 I |
48 | #endif |
49 | ||
4d4f7684 | 50 | /* --------------------------------------------------------- |
51 | * Global interface variables and declarations | |
52 | * --------------------------------------------------------- | |
53 | */ | |
54 | ||
5fd68266 | 55 | #ifndef usb_lld_connect_bus |
b624f32f | 56 | # define usb_lld_connect_bus(usbp) |
5fd68266 | 57 | #endif |
58 | ||
59 | #ifndef usb_lld_disconnect_bus | |
b624f32f | 60 | # define usb_lld_disconnect_bus(usbp) |
5fd68266 | 61 | #endif |
62 | ||
dee1d68d QB |
63 | uint8_t keyboard_idle __attribute__((aligned(2))) = 0; |
64 | uint8_t keyboard_protocol __attribute__((aligned(2))) = 1; | |
65 | uint8_t keyboard_led_stats = 0; | |
66 | volatile uint16_t keyboard_idle_count = 0; | |
4d4f7684 | 67 | static virtual_timer_t keyboard_idle_timer; |
b624f32f | 68 | static void keyboard_idle_timer_cb(void *arg); |
4d4f7684 | 69 | |
70 | report_keyboard_t keyboard_report_sent = {{0}}; | |
71 | #ifdef MOUSE_ENABLE | |
72 | report_mouse_t mouse_report_blank = {0}; | |
73 | #endif /* MOUSE_ENABLE */ | |
74 | #ifdef EXTRAKEY_ENABLE | |
75 | uint8_t extra_report_blank[3] = {0}; | |
76 | #endif /* EXTRAKEY_ENABLE */ | |
77 | ||
4d4f7684 | 78 | /* --------------------------------------------------------- |
79 | * Descriptors and USB driver objects | |
80 | * --------------------------------------------------------- | |
81 | */ | |
82 | ||
83 | /* HID specific constants */ | |
4d4f7684 | 84 | #define HID_GET_REPORT 0x01 |
85 | #define HID_GET_IDLE 0x02 | |
86 | #define HID_GET_PROTOCOL 0x03 | |
87 | #define HID_SET_REPORT 0x09 | |
88 | #define HID_SET_IDLE 0x0A | |
89 | #define HID_SET_PROTOCOL 0x0B | |
90 | ||
4d4f7684 | 91 | /* |
92 | * Handles the GET_DESCRIPTOR callback | |
93 | * | |
94 | * Returns the proper descriptor | |
95 | */ | |
53ff8a31 | 96 | static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) { |
b624f32f | 97 | (void)usbp; |
98 | static USBDescriptor desc; | |
99 | uint16_t wValue = ((uint16_t)dtype << 8) | dindex; | |
100 | desc.ud_string = NULL; | |
101 | desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const) & desc.ud_string); | |
102 | if (desc.ud_string == NULL) | |
103 | return NULL; | |
104 | else | |
105 | return &desc; | |
4d4f7684 | 106 | } |
107 | ||
39bd760f | 108 | #ifndef KEYBOARD_SHARED_EP |
4d4f7684 | 109 | /* keyboard endpoint state structure */ |
110 | static USBInEndpointState kbd_ep_state; | |
80c2e267 | 111 | /* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ |
4d4f7684 | 112 | static const USBEndpointConfig kbd_ep_config = { |
b624f32f | 113 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ |
114 | NULL, /* SETUP packet notification callback */ | |
115 | kbd_in_cb, /* IN notification callback */ | |
116 | NULL, /* OUT notification callback */ | |
117 | KEYBOARD_EPSIZE, /* IN maximum packet size */ | |
118 | 0, /* OUT maximum packet size */ | |
119 | &kbd_ep_state, /* IN Endpoint state */ | |
120 | NULL, /* OUT endpoint state */ | |
121 | 2, /* IN multiplier */ | |
122 | NULL /* SETUP buffer (not a SETUP endpoint) */ | |
4d4f7684 | 123 | }; |
39bd760f | 124 | #endif |
4d4f7684 | 125 | |
39bd760f | 126 | #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) |
4d4f7684 | 127 | /* mouse endpoint state structure */ |
128 | static USBInEndpointState mouse_ep_state; | |
129 | ||
80c2e267 | 130 | /* mouse endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ |
4d4f7684 | 131 | static const USBEndpointConfig mouse_ep_config = { |
b624f32f | 132 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ |
133 | NULL, /* SETUP packet notification callback */ | |
134 | mouse_in_cb, /* IN notification callback */ | |
135 | NULL, /* OUT notification callback */ | |
136 | MOUSE_EPSIZE, /* IN maximum packet size */ | |
137 | 0, /* OUT maximum packet size */ | |
138 | &mouse_ep_state, /* IN Endpoint state */ | |
139 | NULL, /* OUT endpoint state */ | |
140 | 2, /* IN multiplier */ | |
141 | NULL /* SETUP buffer (not a SETUP endpoint) */ | |
4d4f7684 | 142 | }; |
39bd760f | 143 | #endif |
4d4f7684 | 144 | |
39bd760f JLW |
145 | #ifdef SHARED_EP_ENABLE |
146 | /* shared endpoint state structure */ | |
147 | static USBInEndpointState shared_ep_state; | |
4d4f7684 | 148 | |
80c2e267 | 149 | /* shared endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ |
39bd760f | 150 | static const USBEndpointConfig shared_ep_config = { |
b624f32f | 151 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ |
152 | NULL, /* SETUP packet notification callback */ | |
153 | shared_in_cb, /* IN notification callback */ | |
154 | NULL, /* OUT notification callback */ | |
155 | SHARED_EPSIZE, /* IN maximum packet size */ | |
156 | 0, /* OUT maximum packet size */ | |
157 | &shared_ep_state, /* IN Endpoint state */ | |
158 | NULL, /* OUT endpoint state */ | |
159 | 2, /* IN multiplier */ | |
160 | NULL /* SETUP buffer (not a SETUP endpoint) */ | |
4d4f7684 | 161 | }; |
39bd760f | 162 | #endif |
4d4f7684 | 163 | |
53ff8a31 | 164 | typedef struct { |
b624f32f | 165 | size_t queue_capacity_in; |
166 | size_t queue_capacity_out; | |
167 | USBInEndpointState in_ep_state; | |
168 | USBOutEndpointState out_ep_state; | |
169 | USBInEndpointState int_ep_state; | |
170 | USBEndpointConfig in_ep_config; | |
171 | USBEndpointConfig out_ep_config; | |
172 | USBEndpointConfig int_ep_config; | |
173 | const QMKUSBConfig config; | |
174 | QMKUSBDriver driver; | |
e9d32b60 | 175 | } usb_driver_config_t; |
53ff8a31 | 176 | |
80c2e267 | 177 | /* Reusable initialization structure - see USBEndpointConfig comment at top of file */ |
09370a95 QB |
178 | #define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ |
179 | { \ | |
180 | .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ | |
181 | .in_ep_config = \ | |
182 | { \ | |
183 | stream##_IN_MODE, /* Interrupt EP */ \ | |
184 | NULL, /* SETUP packet notification callback */ \ | |
185 | qmkusbDataTransmitted, /* IN notification callback */ \ | |
186 | NULL, /* OUT notification callback */ \ | |
187 | stream##_EPSIZE, /* IN maximum packet size */ \ | |
188 | 0, /* OUT maximum packet size */ \ | |
189 | NULL, /* IN Endpoint state */ \ | |
190 | NULL, /* OUT endpoint state */ \ | |
191 | 2, /* IN multiplier */ \ | |
192 | NULL /* SETUP buffer (not a SETUP endpoint) */ \ | |
193 | }, \ | |
194 | .out_ep_config = \ | |
195 | { \ | |
196 | stream##_OUT_MODE, /* Interrupt EP */ \ | |
197 | NULL, /* SETUP packet notification callback */ \ | |
198 | NULL, /* IN notification callback */ \ | |
199 | qmkusbDataReceived, /* OUT notification callback */ \ | |
200 | 0, /* IN maximum packet size */ \ | |
201 | stream##_EPSIZE, /* OUT maximum packet size */ \ | |
202 | NULL, /* IN Endpoint state */ \ | |
203 | NULL, /* OUT endpoint state */ \ | |
204 | 2, /* IN multiplier */ \ | |
205 | NULL, /* SETUP buffer (not a SETUP endpoint) */ \ | |
206 | }, \ | |
207 | .int_ep_config = \ | |
208 | { \ | |
209 | USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ | |
210 | NULL, /* SETUP packet notification callback */ \ | |
211 | qmkusbInterruptTransmitted, /* IN notification callback */ \ | |
212 | NULL, /* OUT notification callback */ \ | |
213 | CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ | |
214 | 0, /* OUT maximum packet size */ \ | |
215 | NULL, /* IN Endpoint state */ \ | |
216 | NULL, /* OUT endpoint state */ \ | |
217 | 2, /* IN multiplier */ \ | |
218 | NULL, /* SETUP buffer (not a SETUP endpoint) */ \ | |
219 | }, \ | |
220 | .config = { \ | |
221 | .usbp = &USB_DRIVER, \ | |
222 | .bulk_in = stream##_IN_EPNUM, \ | |
223 | .bulk_out = stream##_OUT_EPNUM, \ | |
224 | .int_in = notification, \ | |
225 | .in_buffers = stream##_IN_CAPACITY, \ | |
226 | .out_buffers = stream##_OUT_CAPACITY, \ | |
227 | .in_size = stream##_EPSIZE, \ | |
228 | .out_size = stream##_EPSIZE, \ | |
229 | .fixed_size = fixedsize, \ | |
e9ffc534 GH |
230 | .ib = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ |
231 | .ob = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ | |
09370a95 | 232 | } \ |
b624f32f | 233 | } |
53ff8a31 | 234 | |
235 | typedef struct { | |
b624f32f | 236 | union { |
237 | struct { | |
53ff8a31 | 238 | #ifdef CONSOLE_ENABLE |
b624f32f | 239 | usb_driver_config_t console_driver; |
53ff8a31 | 240 | #endif |
241 | #ifdef RAW_ENABLE | |
b624f32f | 242 | usb_driver_config_t raw_driver; |
53ff8a31 | 243 | #endif |
244 | #ifdef MIDI_ENABLE | |
b624f32f | 245 | usb_driver_config_t midi_driver; |
53ff8a31 | 246 | #endif |
247 | #ifdef VIRTSER_ENABLE | |
b624f32f | 248 | usb_driver_config_t serial_driver; |
53ff8a31 | 249 | #endif |
b624f32f | 250 | }; |
251 | usb_driver_config_t array[0]; | |
53ff8a31 | 252 | }; |
e9d32b60 | 253 | } usb_driver_configs_t; |
53ff8a31 | 254 | |
e9d32b60 | 255 | static usb_driver_configs_t drivers = { |
53ff8a31 | 256 | #ifdef CONSOLE_ENABLE |
b624f32f | 257 | # define CONSOLE_IN_CAPACITY 4 |
258 | # define CONSOLE_OUT_CAPACITY 4 | |
259 | # define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR | |
260 | # define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR | |
261 | .console_driver = QMK_USB_DRIVER_CONFIG(CONSOLE, 0, true), | |
53ff8a31 | 262 | #endif |
263 | #ifdef RAW_ENABLE | |
b624f32f | 264 | # define RAW_IN_CAPACITY 4 |
265 | # define RAW_OUT_CAPACITY 4 | |
266 | # define RAW_IN_MODE USB_EP_MODE_TYPE_INTR | |
267 | # define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR | |
268 | .raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false), | |
53ff8a31 | 269 | #endif |
270 | ||
271 | #ifdef MIDI_ENABLE | |
b624f32f | 272 | # define MIDI_STREAM_IN_CAPACITY 4 |
273 | # define MIDI_STREAM_OUT_CAPACITY 4 | |
274 | # define MIDI_STREAM_IN_MODE USB_EP_MODE_TYPE_BULK | |
275 | # define MIDI_STREAM_OUT_MODE USB_EP_MODE_TYPE_BULK | |
276 | .midi_driver = QMK_USB_DRIVER_CONFIG(MIDI_STREAM, 0, false), | |
53ff8a31 | 277 | #endif |
278 | ||
279 | #ifdef VIRTSER_ENABLE | |
b624f32f | 280 | # define CDC_IN_CAPACITY 4 |
281 | # define CDC_OUT_CAPACITY 4 | |
282 | # define CDC_IN_MODE USB_EP_MODE_TYPE_BULK | |
283 | # define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK | |
284 | .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false), | |
53ff8a31 | 285 | #endif |
286 | }; | |
287 | ||
e9d32b60 | 288 | #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t)) |
53ff8a31 | 289 | |
4d4f7684 | 290 | /* --------------------------------------------------------- |
291 | * USB driver functions | |
292 | * --------------------------------------------------------- | |
293 | */ | |
294 | ||
295 | /* Handles the USB driver global events | |
296 | * TODO: maybe disable some things when connection is lost? */ | |
297 | static void usb_event_cb(USBDriver *usbp, usbevent_t event) { | |
b624f32f | 298 | switch (event) { |
299 | case USB_EVENT_ADDRESS: | |
300 | return; | |
4d4f7684 | 301 | |
b624f32f | 302 | case USB_EVENT_CONFIGURED: |
303 | osalSysLockFromISR(); | |
304 | /* Enable the endpoints specified into the configuration. */ | |
39bd760f | 305 | #ifndef KEYBOARD_SHARED_EP |
b624f32f | 306 | usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config); |
39bd760f JLW |
307 | #endif |
308 | #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) | |
b624f32f | 309 | usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config); |
39bd760f JLW |
310 | #endif |
311 | #ifdef SHARED_EP_ENABLE | |
b624f32f | 312 | usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); |
39bd760f | 313 | #endif |
b624f32f | 314 | for (int i = 0; i < NUM_USB_DRIVERS; i++) { |
315 | usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config); | |
316 | usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config); | |
317 | if (drivers.array[i].config.int_in) { | |
318 | usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config); | |
319 | } | |
320 | qmkusbConfigureHookI(&drivers.array[i].driver); | |
321 | } | |
322 | osalSysUnlockFromISR(); | |
323 | return; | |
324 | case USB_EVENT_SUSPEND: | |
4d4f7684 | 325 | #ifdef SLEEP_LED_ENABLE |
b624f32f | 326 | sleep_led_enable(); |
a91c0c47 | 327 | #endif /* SLEEP_LED_ENABLE */ |
b624f32f | 328 | /* Falls into.*/ |
329 | case USB_EVENT_UNCONFIGURED: | |
330 | /* Falls into.*/ | |
331 | case USB_EVENT_RESET: | |
332 | for (int i = 0; i < NUM_USB_DRIVERS; i++) { | |
333 | chSysLockFromISR(); | |
334 | /* Disconnection event on suspend.*/ | |
335 | qmkusbSuspendHookI(&drivers.array[i].driver); | |
336 | chSysUnlockFromISR(); | |
337 | } | |
338 | return; | |
339 | ||
340 | case USB_EVENT_WAKEUP: | |
341 | // TODO: from ISR! print("[W]"); | |
342 | for (int i = 0; i < NUM_USB_DRIVERS; i++) { | |
343 | chSysLockFromISR(); | |
344 | /* Disconnection event on suspend.*/ | |
345 | qmkusbWakeupHookI(&drivers.array[i].driver); | |
346 | chSysUnlockFromISR(); | |
347 | } | |
348 | suspend_wakeup_init(); | |
4d4f7684 | 349 | #ifdef SLEEP_LED_ENABLE |
b624f32f | 350 | sleep_led_disable(); |
351 | // NOTE: converters may not accept this | |
352 | led_set(host_keyboard_leds()); | |
4d4f7684 | 353 | #endif /* SLEEP_LED_ENABLE */ |
b624f32f | 354 | return; |
4d4f7684 | 355 | |
b624f32f | 356 | case USB_EVENT_STALLED: |
357 | return; | |
358 | } | |
4d4f7684 | 359 | } |
360 | ||
361 | /* Function used locally in os/hal/src/usb.c for getting descriptors | |
362 | * need it here for HID descriptor */ | |
363 | static uint16_t get_hword(uint8_t *p) { | |
b624f32f | 364 | uint16_t hw; |
4d4f7684 | 365 | |
b624f32f | 366 | hw = (uint16_t)*p++; |
367 | hw |= (uint16_t)*p << 8U; | |
368 | return hw; | |
4d4f7684 | 369 | } |
370 | ||
371 | /* | |
372 | * Appendix G: HID Request Support Requirements | |
373 | * | |
374 | * The following table enumerates the requests that need to be supported by various types of HID class devices. | |
375 | * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol | |
376 | * ------------------------------------------------------------------------------------------ | |
377 | * Boot Mouse Required Optional Optional Optional Required Required | |
378 | * Non-Boot Mouse Required Optional Optional Optional Optional Optional | |
379 | * Boot Keyboard Required Optional Required Required Required Required | |
380 | * Non-Boot Keybrd Required Optional Required Required Optional Optional | |
381 | * Other Device Required Optional Optional Optional Optional Optional | |
382 | */ | |
383 | ||
39bd760f | 384 | static uint8_t set_report_buf[2] __attribute__((aligned(2))); |
b624f32f | 385 | static void set_led_transfer_cb(USBDriver *usbp) { |
123ae73e | 386 | if (usbp->setup[6] == 2) { /* LSB(wLength) */ |
387 | uint8_t report_id = set_report_buf[0]; | |
388 | if ((report_id == REPORT_ID_KEYBOARD) || (report_id == REPORT_ID_NKRO)) { | |
389 | keyboard_led_stats = set_report_buf[1]; | |
390 | } | |
391 | } else { | |
392 | keyboard_led_stats = set_report_buf[0]; | |
b624f32f | 393 | } |
39bd760f | 394 | } |
39bd760f | 395 | |
4d4f7684 | 396 | /* Callback for SETUP request on the endpoint 0 (control) */ |
397 | static bool usb_request_hook_cb(USBDriver *usbp) { | |
b624f32f | 398 | const USBDescriptor *dp; |
b624f32f | 399 | |
400 | /* usbp->setup fields: | |
401 | * 0: bmRequestType (bitmask) | |
402 | * 1: bRequest | |
403 | * 2,3: (LSB,MSB) wValue | |
404 | * 4,5: (LSB,MSB) wIndex | |
405 | * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ | |
406 | ||
407 | /* Handle HID class specific requests */ | |
408 | if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { | |
409 | switch (usbp->setup[0] & USB_RTYPE_DIR_MASK) { | |
410 | case USB_RTYPE_DIR_DEV2HOST: | |
411 | switch (usbp->setup[1]) { /* bRequest */ | |
412 | case HID_GET_REPORT: | |
413 | switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ | |
414 | case KEYBOARD_INTERFACE: | |
415 | usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); | |
416 | return TRUE; | |
417 | break; | |
4d4f7684 | 418 | |
39bd760f | 419 | #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) |
b624f32f | 420 | case MOUSE_INTERFACE: |
421 | usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); | |
422 | return TRUE; | |
423 | break; | |
39bd760f | 424 | #endif |
4d4f7684 | 425 | |
b624f32f | 426 | default: |
427 | usbSetupTransfer(usbp, NULL, 0, NULL); | |
428 | return TRUE; | |
429 | break; | |
430 | } | |
431 | break; | |
432 | ||
433 | case HID_GET_PROTOCOL: | |
434 | if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ | |
435 | usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); | |
436 | return TRUE; | |
437 | } | |
438 | break; | |
439 | ||
440 | case HID_GET_IDLE: | |
441 | usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); | |
442 | return TRUE; | |
443 | break; | |
444 | } | |
445 | break; | |
446 | ||
447 | case USB_RTYPE_DIR_HOST2DEV: | |
448 | switch (usbp->setup[1]) { /* bRequest */ | |
449 | case HID_SET_REPORT: | |
123ae73e | 450 | switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ |
451 | case KEYBOARD_INTERFACE: | |
39bd760f | 452 | #if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) |
b624f32f | 453 | case SHARED_INTERFACE: |
39bd760f | 454 | #endif |
123ae73e | 455 | usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb); |
b624f32f | 456 | return TRUE; |
457 | break; | |
458 | } | |
459 | break; | |
460 | ||
461 | case HID_SET_PROTOCOL: | |
462 | if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ | |
463 | keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ | |
4d4f7684 | 464 | #ifdef NKRO_ENABLE |
b624f32f | 465 | keymap_config.nkro = !!keyboard_protocol; |
466 | if (!keymap_config.nkro && keyboard_idle) { | |
a91c0c47 | 467 | #else /* NKRO_ENABLE */ |
b624f32f | 468 | if (keyboard_idle) { |
a91c0c47 | 469 | #endif /* NKRO_ENABLE */ |
b624f32f | 470 | /* arm the idle timer if boot protocol & idle */ |
471 | osalSysLockFromISR(); | |
26eef35f | 472 | chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); |
b624f32f | 473 | osalSysUnlockFromISR(); |
474 | } | |
475 | } | |
476 | usbSetupTransfer(usbp, NULL, 0, NULL); | |
477 | return TRUE; | |
478 | break; | |
479 | ||
480 | case HID_SET_IDLE: | |
481 | keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ | |
482 | /* arm the timer */ | |
4d4f7684 | 483 | #ifdef NKRO_ENABLE |
b624f32f | 484 | if (!keymap_config.nkro && keyboard_idle) { |
485 | #else /* NKRO_ENABLE */ | |
486 | if (keyboard_idle) { | |
4d4f7684 | 487 | #endif /* NKRO_ENABLE */ |
b624f32f | 488 | osalSysLockFromISR(); |
26eef35f | 489 | chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); |
b624f32f | 490 | osalSysUnlockFromISR(); |
491 | } | |
492 | usbSetupTransfer(usbp, NULL, 0, NULL); | |
493 | return TRUE; | |
494 | break; | |
495 | } | |
496 | break; | |
4d4f7684 | 497 | } |
b624f32f | 498 | } |
499 | ||
500 | /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ | |
501 | if ((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { | |
502 | dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); | |
503 | if (dp == NULL) return FALSE; | |
504 | usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); | |
4d4f7684 | 505 | return TRUE; |
4d4f7684 | 506 | } |
b624f32f | 507 | |
508 | for (int i = 0; i < NUM_USB_DRIVERS; i++) { | |
509 | if (drivers.array[i].config.int_in) { | |
510 | // NOTE: Assumes that we only have one serial driver | |
511 | return qmkusbRequestsHook(usbp); | |
512 | } | |
53ff8a31 | 513 | } |
53ff8a31 | 514 | |
b624f32f | 515 | return FALSE; |
4d4f7684 | 516 | } |
517 | ||
518 | /* Start-of-frame callback */ | |
519 | static void usb_sof_cb(USBDriver *usbp) { | |
b624f32f | 520 | kbd_sof_cb(usbp); |
521 | osalSysLockFromISR(); | |
522 | for (int i = 0; i < NUM_USB_DRIVERS; i++) { | |
523 | qmkusbSOFHookI(&drivers.array[i].driver); | |
524 | } | |
525 | osalSysUnlockFromISR(); | |
4d4f7684 | 526 | } |
527 | ||
4d4f7684 | 528 | /* USB driver configuration */ |
529 | static const USBConfig usbcfg = { | |
b624f32f | 530 | usb_event_cb, /* USB events callback */ |
531 | usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ | |
532 | usb_request_hook_cb, /* Requests hook callback */ | |
533 | usb_sof_cb /* Start Of Frame callback */ | |
4d4f7684 | 534 | }; |
535 | ||
536 | /* | |
537 | * Initialize the USB driver | |
538 | */ | |
539 | void init_usb_driver(USBDriver *usbp) { | |
b624f32f | 540 | for (int i = 0; i < NUM_USB_DRIVERS; i++) { |
541 | QMKUSBDriver *driver = &drivers.array[i].driver; | |
542 | drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state; | |
543 | drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state; | |
544 | drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; | |
545 | qmkusbObjectInit(driver, &drivers.array[i].config); | |
546 | qmkusbStart(driver, &drivers.array[i].config); | |
547 | } | |
548 | ||
549 | /* | |
550 | * Activates the USB driver and then the USB bus pull-up on D+. | |
551 | * Note, a delay is inserted in order to not have to disconnect the cable | |
552 | * after a reset. | |
553 | */ | |
554 | usbDisconnectBus(usbp); | |
555 | wait_ms(1500); | |
556 | usbStart(usbp, &usbcfg); | |
557 | usbConnectBus(usbp); | |
558 | ||
559 | chVTObjectInit(&keyboard_idle_timer); | |
4d4f7684 | 560 | } |
561 | ||
4d4f7684 | 562 | /* --------------------------------------------------------- |
563 | * Keyboard functions | |
564 | * --------------------------------------------------------- | |
565 | */ | |
4d4f7684 | 566 | /* keyboard IN callback hander (a kbd report has made it IN) */ |
39bd760f | 567 | #ifndef KEYBOARD_SHARED_EP |
4d4f7684 | 568 | void kbd_in_cb(USBDriver *usbp, usbep_t ep) { |
b624f32f | 569 | /* STUB */ |
570 | (void)usbp; | |
571 | (void)ep; | |
4d4f7684 | 572 | } |
39bd760f | 573 | #endif |
4d4f7684 | 574 | |
575 | /* start-of-frame handler | |
576 | * TODO: i guess it would be better to re-implement using timers, | |
577 | * so that this is not going to have to be checked every 1ms */ | |
b624f32f | 578 | void kbd_sof_cb(USBDriver *usbp) { (void)usbp; } |
4d4f7684 | 579 | |
580 | /* Idle requests timer code | |
581 | * callback (called from ISR, unlocked state) */ | |
582 | static void keyboard_idle_timer_cb(void *arg) { | |
b624f32f | 583 | USBDriver *usbp = (USBDriver *)arg; |
4d4f7684 | 584 | |
b624f32f | 585 | osalSysLockFromISR(); |
4d4f7684 | 586 | |
b624f32f | 587 | /* check that the states of things are as they're supposed to */ |
588 | if (usbGetDriverStateI(usbp) != USB_ACTIVE) { | |
589 | /* do not rearm the timer, should be enabled on IDLE request */ | |
590 | osalSysUnlockFromISR(); | |
591 | return; | |
592 | } | |
4d4f7684 | 593 | |
594 | #ifdef NKRO_ENABLE | |
b624f32f | 595 | if (!keymap_config.nkro && keyboard_idle && keyboard_protocol) { |
596 | #else /* NKRO_ENABLE */ | |
597 | if (keyboard_idle && keyboard_protocol) { | |
4d4f7684 | 598 | #endif /* NKRO_ENABLE */ |
b624f32f | 599 | /* TODO: are we sure we want the KBD_ENDPOINT? */ |
600 | if (!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) { | |
601 | usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE); | |
602 | } | |
603 | /* rearm the timer */ | |
26eef35f | 604 | chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); |
4d4f7684 | 605 | } |
4d4f7684 | 606 | |
b624f32f | 607 | /* do not rearm the timer if the condition above fails |
608 | * it should be enabled again on either IDLE or SET_PROTOCOL requests */ | |
609 | osalSysUnlockFromISR(); | |
4d4f7684 | 610 | } |
611 | ||
612 | /* LED status */ | |
123ae73e | 613 | uint8_t keyboard_leds(void) { return keyboard_led_stats; } |
4d4f7684 | 614 | |
615 | /* prepare and start sending a report IN | |
616 | * not callable from ISR or locked state */ | |
617 | void send_keyboard(report_keyboard_t *report) { | |
4d4f7684 | 618 | osalSysLock(); |
b624f32f | 619 | if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { |
83be1aed | 620 | goto unlock; |
4d4f7684 | 621 | } |
b624f32f | 622 | |
623 | #ifdef NKRO_ENABLE | |
624 | if (keymap_config.nkro && keyboard_protocol) { /* NKRO protocol */ | |
625 | /* need to wait until the previous packet has made it through */ | |
626 | /* can rewrite this using the synchronous API, then would wait | |
627 | * until *after* the packet has been transmitted. I think | |
628 | * this is more efficient */ | |
629 | /* busy wait, should be short and not very common */ | |
b624f32f | 630 | if (usbGetTransmitStatusI(&USB_DRIVER, SHARED_IN_EPNUM)) { |
631 | /* Need to either suspend, or loop and call unlock/lock during | |
632 | * every iteration - otherwise the system will remain locked, | |
633 | * no interrupts served, so USB not going through as well. | |
634 | * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ | |
635 | osalThreadSuspendS(&(&USB_DRIVER)->epc[SHARED_IN_EPNUM]->in_state->thread); | |
83be1aed | 636 | |
637 | /* after osalThreadSuspendS returns USB status might have changed */ | |
638 | if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { | |
639 | goto unlock; | |
640 | } | |
b624f32f | 641 | } |
642 | usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report)); | |
b624f32f | 643 | } else |
4d4f7684 | 644 | #endif /* NKRO_ENABLE */ |
b624f32f | 645 | { /* regular protocol */ |
646 | /* need to wait until the previous packet has made it through */ | |
647 | /* busy wait, should be short and not very common */ | |
b624f32f | 648 | if (usbGetTransmitStatusI(&USB_DRIVER, KEYBOARD_IN_EPNUM)) { |
649 | /* Need to either suspend, or loop and call unlock/lock during | |
650 | * every iteration - otherwise the system will remain locked, | |
651 | * no interrupts served, so USB not going through as well. | |
652 | * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ | |
653 | osalThreadSuspendS(&(&USB_DRIVER)->epc[KEYBOARD_IN_EPNUM]->in_state->thread); | |
83be1aed | 654 | |
655 | /* after osalThreadSuspendS returns USB status might have changed */ | |
656 | if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { | |
657 | goto unlock; | |
658 | } | |
b624f32f | 659 | } |
660 | uint8_t *data, size; | |
661 | if (keyboard_protocol) { | |
662 | data = (uint8_t *)report; | |
663 | size = KEYBOARD_REPORT_SIZE; | |
664 | } else { /* boot protocol */ | |
665 | data = &report->mods; | |
666 | size = 8; | |
667 | } | |
668 | usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, data, size); | |
39bd760f | 669 | } |
b624f32f | 670 | keyboard_report_sent = *report; |
83be1aed | 671 | |
672 | unlock: | |
673 | osalSysUnlock(); | |
4d4f7684 | 674 | } |
675 | ||
676 | /* --------------------------------------------------------- | |
677 | * Mouse functions | |
678 | * --------------------------------------------------------- | |
679 | */ | |
680 | ||
681 | #ifdef MOUSE_ENABLE | |
682 | ||
b624f32f | 683 | # ifndef MOUSE_SHARED_EP |
4d4f7684 | 684 | /* mouse IN callback hander (a mouse report has made it IN) */ |
685 | void mouse_in_cb(USBDriver *usbp, usbep_t ep) { | |
b624f32f | 686 | (void)usbp; |
687 | (void)ep; | |
4d4f7684 | 688 | } |
b624f32f | 689 | # endif |
4d4f7684 | 690 | |
691 | void send_mouse(report_mouse_t *report) { | |
b624f32f | 692 | osalSysLock(); |
693 | if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { | |
694 | osalSysUnlock(); | |
695 | return; | |
b1bf0879 | 696 | } |
4d4f7684 | 697 | |
b624f32f | 698 | if (usbGetTransmitStatusI(&USB_DRIVER, MOUSE_IN_EPNUM)) { |
699 | /* Need to either suspend, or loop and call unlock/lock during | |
700 | * every iteration - otherwise the system will remain locked, | |
701 | * no interrupts served, so USB not going through as well. | |
702 | * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ | |
26eef35f | 703 | if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[MOUSE_IN_EPNUM]->in_state->thread, TIME_MS2I(10)) == MSG_TIMEOUT) { |
b624f32f | 704 | osalSysUnlock(); |
705 | return; | |
706 | } | |
707 | } | |
708 | usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t)); | |
709 | osalSysUnlock(); | |
4d4f7684 | 710 | } |
b624f32f | 711 | |
712 | #else /* MOUSE_ENABLE */ | |
713 | void send_mouse(report_mouse_t *report) { (void)report; } | |
4d4f7684 | 714 | #endif /* MOUSE_ENABLE */ |
715 | ||
716 | /* --------------------------------------------------------- | |
39bd760f | 717 | * Shared EP functions |
4d4f7684 | 718 | * --------------------------------------------------------- |
719 | */ | |
39bd760f JLW |
720 | #ifdef SHARED_EP_ENABLE |
721 | /* shared IN callback hander */ | |
722 | void shared_in_cb(USBDriver *usbp, usbep_t ep) { | |
b624f32f | 723 | /* STUB */ |
724 | (void)usbp; | |
725 | (void)ep; | |
4d4f7684 | 726 | } |
39bd760f JLW |
727 | #endif |
728 | ||
729 | /* --------------------------------------------------------- | |
730 | * Extrakey functions | |
731 | * --------------------------------------------------------- | |
732 | */ | |
4d4f7684 | 733 | |
39bd760f | 734 | #ifdef EXTRAKEY_ENABLE |
088b64ab | 735 | static void send_extra(uint8_t report_id, uint16_t data) { |
b624f32f | 736 | osalSysLock(); |
737 | if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { | |
738 | osalSysUnlock(); | |
739 | return; | |
740 | } | |
4d4f7684 | 741 | |
b624f32f | 742 | report_extra_t report = {.report_id = report_id, .usage = data}; |
4d4f7684 | 743 | |
b624f32f | 744 | usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t)); |
745 | osalSysUnlock(); | |
4d4f7684 | 746 | } |
088b64ab | 747 | #endif |
4d4f7684 | 748 | |
088b64ab R |
749 | void send_system(uint16_t data) { |
750 | #ifdef EXTRAKEY_ENABLE | |
751 | send_extra(REPORT_ID_SYSTEM, data); | |
752 | #endif | |
753 | } | |
4d4f7684 | 754 | |
088b64ab R |
755 | void send_consumer(uint16_t data) { |
756 | #ifdef EXTRAKEY_ENABLE | |
757 | send_extra(REPORT_ID_CONSUMER, data); | |
758 | #endif | |
759 | } | |
4d4f7684 | 760 | |
761 | /* --------------------------------------------------------- | |
762 | * Console functions | |
763 | * --------------------------------------------------------- | |
764 | */ | |
765 | ||
766 | #ifdef CONSOLE_ENABLE | |
767 | ||
53ff8a31 | 768 | int8_t sendchar(uint8_t c) { |
b624f32f | 769 | // The previous implmentation had timeouts, but I think it's better to just slow down |
770 | // and make sure that everything is transferred, rather than dropping stuff | |
771 | return chnWrite(&drivers.console_driver.driver, &c, 1); | |
53ff8a31 | 772 | } |
4d4f7684 | 773 | |
53ff8a31 | 774 | // Just a dummy function for now, this could be exposed as a weak function |
775 | // Or connected to the actual QMK console | |
b624f32f | 776 | static void console_receive(uint8_t *data, uint8_t length) { |
777 | (void)data; | |
778 | (void)length; | |
53ff8a31 | 779 | } |
4d4f7684 | 780 | |
53ff8a31 | 781 | void console_task(void) { |
b624f32f | 782 | uint8_t buffer[CONSOLE_EPSIZE]; |
783 | size_t size = 0; | |
784 | do { | |
785 | size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); | |
786 | if (size > 0) { | |
787 | console_receive(buffer, size); | |
788 | } | |
789 | } while (size > 0); | |
53ff8a31 | 790 | } |
4d4f7684 | 791 | |
b624f32f | 792 | #else /* CONSOLE_ENABLE */ |
53ff8a31 | 793 | int8_t sendchar(uint8_t c) { |
b624f32f | 794 | (void)c; |
795 | return 0; | |
53ff8a31 | 796 | } |
797 | #endif /* CONSOLE_ENABLE */ | |
4d4f7684 | 798 | |
53ff8a31 | 799 | void sendchar_pf(void *p, char c) { |
b624f32f | 800 | (void)p; |
801 | sendchar((uint8_t)c); | |
53ff8a31 | 802 | } |
4d4f7684 | 803 | |
53ff8a31 | 804 | #ifdef RAW_ENABLE |
b624f32f | 805 | void raw_hid_send(uint8_t *data, uint8_t length) { |
806 | // TODO: implement variable size packet | |
807 | if (length != RAW_EPSIZE) { | |
808 | return; | |
809 | } | |
810 | chnWrite(&drivers.raw_driver.driver, data, length); | |
4d4f7684 | 811 | } |
812 | ||
b624f32f | 813 | __attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) { |
814 | // Users should #include "raw_hid.h" in their own code | |
815 | // and implement this function there. Leave this as weak linkage | |
816 | // so users can opt to not handle data coming in. | |
53ff8a31 | 817 | } |
4d4f7684 | 818 | |
53ff8a31 | 819 | void raw_hid_task(void) { |
b624f32f | 820 | uint8_t buffer[RAW_EPSIZE]; |
821 | size_t size = 0; | |
822 | do { | |
823 | size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); | |
824 | if (size > 0) { | |
825 | raw_hid_receive(buffer, size); | |
826 | } | |
827 | } while (size > 0); | |
4d4f7684 | 828 | } |
829 | ||
53ff8a31 | 830 | #endif |
4d4f7684 | 831 | |
53ff8a31 | 832 | #ifdef MIDI_ENABLE |
4d4f7684 | 833 | |
b624f32f | 834 | void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); } |
4d4f7684 | 835 | |
b624f32f | 836 | bool recv_midi_packet(MIDI_EventPacket_t *const event) { |
837 | size_t size = chnReadTimeout(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t), TIME_IMMEDIATE); | |
838 | return size == sizeof(MIDI_EventPacket_t); | |
53ff8a31 | 839 | } |
0010d0c4 DJ |
840 | void midi_ep_task(void) { |
841 | uint8_t buffer[MIDI_STREAM_EPSIZE]; | |
842 | size_t size = 0; | |
843 | do { | |
844 | size_t size = chnReadTimeout(&drivers.midi_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); | |
845 | if (size > 0) { | |
846 | MIDI_EventPacket_t event; | |
847 | recv_midi_packet(&event); | |
848 | } | |
849 | } while (size > 0); | |
850 | } | |
53ff8a31 | 851 | #endif |
4d4f7684 | 852 | |
53ff8a31 | 853 | #ifdef VIRTSER_ENABLE |
4d4f7684 | 854 | |
b624f32f | 855 | void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); } |
4d4f7684 | 856 | |
b624f32f | 857 | __attribute__((weak)) void virtser_recv(uint8_t c) { |
858 | // Ignore by default | |
4d4f7684 | 859 | } |
860 | ||
53ff8a31 | 861 | void virtser_task(void) { |
b624f32f | 862 | uint8_t numBytesReceived = 0; |
863 | uint8_t buffer[16]; | |
864 | do { | |
865 | numBytesReceived = chnReadTimeout(&drivers.serial_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); | |
866 | for (int i = 0; i < numBytesReceived; i++) { | |
867 | virtser_recv(buffer[i]); | |
868 | } | |
869 | } while (numBytesReceived > 0); | |
4d4f7684 | 870 | } |
4d4f7684 | 871 | |
53ff8a31 | 872 | #endif |