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