]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/protocol/chibios/usb_main.c
cbe25719441da6f480a639e22a471353de984228
[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   /* TODO: LUFA manually waits for the endpoint to become ready
721    * for about 10ms for mouse, kbd, system; 1ms for nkro
722    * is this really needed?
723    */
724
725   osalSysLock();
726   usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t));
727   osalSysUnlock();
728 }
729
730 #else /* MOUSE_ENABLE */
731 void send_mouse(report_mouse_t *report) {
732   (void)report;
733 }
734 #endif /* MOUSE_ENABLE */
735
736 /* ---------------------------------------------------------
737  *                   Extrakey functions
738  * ---------------------------------------------------------
739  */
740
741 #ifdef EXTRAKEY_ENABLE
742
743 /* extrakey IN callback hander */
744 void extra_in_cb(USBDriver *usbp, usbep_t ep) {
745   /* STUB */
746   (void)usbp;
747   (void)ep;
748 }
749
750 static void send_extra_report(uint8_t report_id, uint16_t data) {
751   osalSysLock();
752   if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
753     osalSysUnlock();
754     return;
755   }
756
757   report_extra_t report = {
758     .report_id = report_id,
759     .usage = data
760   };
761
762   usbStartTransmitI(&USB_DRIVER, EXTRAKEY_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t));
763   osalSysUnlock();
764 }
765
766 void send_system(uint16_t data) {
767   send_extra_report(REPORT_ID_SYSTEM, data);
768 }
769
770 void send_consumer(uint16_t data) {
771   send_extra_report(REPORT_ID_CONSUMER, data);
772 }
773
774 #else /* EXTRAKEY_ENABLE */
775 void send_system(uint16_t data) {
776   (void)data;
777 }
778 void send_consumer(uint16_t data) {
779   (void)data;
780 }
781 #endif /* EXTRAKEY_ENABLE */
782
783 /* ---------------------------------------------------------
784  *                   Console functions
785  * ---------------------------------------------------------
786  */
787
788 #ifdef CONSOLE_ENABLE
789
790 int8_t sendchar(uint8_t c) {
791   // The previous implmentation had timeouts, but I think it's better to just slow down
792   // and make sure that everything is transferred, rather than dropping stuff
793   return chnWrite(&drivers.console_driver.driver, &c, 1);
794 }
795
796 // Just a dummy function for now, this could be exposed as a weak function
797 // Or connected to the actual QMK console
798 static void console_receive( uint8_t *data, uint8_t length ) {
799   (void)data;
800   (void)length;
801 }
802
803 void console_task(void) {
804   uint8_t buffer[CONSOLE_EPSIZE];
805   size_t size = 0;
806   do {
807     size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
808     if (size > 0) {
809         console_receive(buffer, size);
810     }
811   } while(size > 0);
812 }
813
814 #else /* CONSOLE_ENABLE */
815 int8_t sendchar(uint8_t c) {
816   (void)c;
817   return 0;
818 }
819 #endif /* CONSOLE_ENABLE */
820
821 void sendchar_pf(void *p, char c) {
822   (void)p;
823   sendchar((uint8_t)c);
824 }
825
826 #ifdef RAW_ENABLE
827 void raw_hid_send( uint8_t *data, uint8_t length ) {
828         // TODO: implement variable size packet
829         if ( length != RAW_EPSIZE )
830         {
831                 return;
832
833         }
834   chnWrite(&drivers.raw_driver.driver, data, length);
835 }
836
837 __attribute__ ((weak))
838 void raw_hid_receive( uint8_t *data, uint8_t length ) {
839         // Users should #include "raw_hid.h" in their own code
840         // and implement this function there. Leave this as weak linkage
841         // so users can opt to not handle data coming in.
842 }
843
844 void raw_hid_task(void) {
845   uint8_t buffer[RAW_EPSIZE];
846   size_t size = 0;
847   do {
848     size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
849     if (size > 0) {
850         raw_hid_receive(buffer, size);
851     }
852   } while(size > 0);
853 }
854
855 #endif
856
857 #ifdef MIDI_ENABLE
858
859 void send_midi_packet(MIDI_EventPacket_t* event) {
860   chnWrite(&drivers.midi_driver.driver, (uint8_t*)event, sizeof(MIDI_EventPacket_t));
861 }
862
863 bool recv_midi_packet(MIDI_EventPacket_t* const event) {
864   size_t size = chnReadTimeout(&drivers.midi_driver.driver, (uint8_t*)event, sizeof(MIDI_EventPacket_t), TIME_IMMEDIATE);
865   return size == sizeof(MIDI_EventPacket_t);
866 }
867
868 #endif
869
870 #ifdef VIRTSER_ENABLE
871
872 void virtser_send(const uint8_t byte) {
873   chnWrite(&drivers.serial_driver.driver, &byte, 1);
874 }
875
876 __attribute__ ((weak))
877 void virtser_recv(uint8_t c)
878 {
879   // Ignore by default
880 }
881
882 void virtser_task(void) {
883   uint8_t numBytesReceived = 0;
884   uint8_t buffer[16];
885   do {
886     numBytesReceived = chnReadTimeout(&drivers.serial_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
887     for (int i=0;i<numBytesReceived;i++) {
888       virtser_recv(buffer[i]);
889     }
890   } while (numBytesReceived > 0);
891 }
892
893 #endif