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