2 * (c) 2015 flabberast <s3+flabbergast@sdfeu.org>
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
14 * Since some GPL'd code is used, this work is licensed under
26 #ifdef SLEEP_LED_ENABLE
27 #include "sleep_led.h"
31 #include "usb_descriptor.h"
32 #include "usb_driver.h"
35 #include "keycode_config.h"
37 extern keymap_config_t keymap_config;
40 /* ---------------------------------------------------------
41 * Global interface variables and declarations
42 * ---------------------------------------------------------
45 #ifndef usb_lld_connect_bus
46 #define usb_lld_connect_bus(usbp)
49 #ifndef usb_lld_disconnect_bus
50 #define usb_lld_disconnect_bus(usbp)
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);
60 report_keyboard_t keyboard_report_sent = {{0}};
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 */
68 /* ---------------------------------------------------------
69 * Descriptors and USB driver objects
70 * ---------------------------------------------------------
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
82 * Handles the GET_DESCRIPTOR callback
84 * Returns the proper descriptor
86 static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) {
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)
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) */
116 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
117 /* mouse endpoint state structure */
118 static USBInEndpointState mouse_ep_state;
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) */
135 #ifdef SHARED_EP_ENABLE
136 /* shared endpoint state structure */
137 static USBInEndpointState shared_ep_state;
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) */
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;
165 } usb_driver_config_t;
167 #define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) { \
168 .queue_capacity_in = stream##_IN_CAPACITY, \
169 .queue_capacity_out = stream##_OUT_CAPACITY, \
171 .ep_mode = stream##_IN_MODE, \
173 .in_cb = qmkusbDataTransmitted, \
175 .in_maxsize = stream##_EPSIZE, \
177 /* The pointer to the states will be filled during initialization */ \
184 .ep_mode = stream##_OUT_MODE, \
187 .out_cb = qmkusbDataReceived, \
189 .out_maxsize = stream##_EPSIZE, \
190 /* The pointer to the states will be filled during initialization */ \
197 .ep_mode = USB_EP_MODE_TYPE_INTR, \
199 .in_cb = qmkusbInterruptTransmitted, \
201 .in_maxsize = CDC_NOTIFICATION_EPSIZE, \
203 /* The pointer to the states will be filled during initialization */ \
210 .usbp = &USB_DRIVER, \
211 .bulk_in = stream##_IN_EPNUM, \
212 .bulk_out = stream##_OUT_EPNUM, \
213 .int_in = notification, \
214 .in_buffers = stream##_IN_CAPACITY, \
215 .out_buffers = stream##_OUT_CAPACITY, \
216 .in_size = stream##_EPSIZE, \
217 .out_size = stream##_EPSIZE, \
218 .fixed_size = fixedsize, \
219 .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]) {}, \
220 .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY,stream##_EPSIZE)]) {}, \
227 #ifdef CONSOLE_ENABLE
228 usb_driver_config_t console_driver;
231 usb_driver_config_t raw_driver;
234 usb_driver_config_t midi_driver;
236 #ifdef VIRTSER_ENABLE
237 usb_driver_config_t serial_driver;
240 usb_driver_config_t array[0];
242 } usb_driver_configs_t;
244 static usb_driver_configs_t drivers = {
245 #ifdef CONSOLE_ENABLE
246 #define CONSOLE_IN_CAPACITY 4
247 #define CONSOLE_OUT_CAPACITY 4
248 #define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR
249 #define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR
250 .console_driver = QMK_USB_DRIVER_CONFIG(CONSOLE, 0, true),
253 #define RAW_IN_CAPACITY 4
254 #define RAW_OUT_CAPACITY 4
255 #define RAW_IN_MODE USB_EP_MODE_TYPE_INTR
256 #define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR
257 .raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false),
261 #define MIDI_STREAM_IN_CAPACITY 4
262 #define MIDI_STREAM_OUT_CAPACITY 4
263 #define MIDI_STREAM_IN_MODE USB_EP_MODE_TYPE_BULK
264 #define MIDI_STREAM_OUT_MODE USB_EP_MODE_TYPE_BULK
265 .midi_driver = QMK_USB_DRIVER_CONFIG(MIDI_STREAM, 0, false),
268 #ifdef VIRTSER_ENABLE
269 #define CDC_IN_CAPACITY 4
270 #define CDC_OUT_CAPACITY 4
271 #define CDC_IN_MODE USB_EP_MODE_TYPE_BULK
272 #define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK
273 .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false),
277 #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
280 /* ---------------------------------------------------------
281 * USB driver functions
282 * ---------------------------------------------------------
285 /* Handles the USB driver global events
286 * TODO: maybe disable some things when connection is lost? */
287 static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
289 case USB_EVENT_ADDRESS:
292 case USB_EVENT_CONFIGURED:
293 osalSysLockFromISR();
294 /* Enable the endpoints specified into the configuration. */
295 #ifndef KEYBOARD_SHARED_EP
296 usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config);
298 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
299 usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config);
301 #ifdef SHARED_EP_ENABLE
302 usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config);
304 for (int i=0;i<NUM_USB_DRIVERS;i++) {
305 usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config);
306 usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config);
307 if (drivers.array[i].config.int_in) {
308 usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config);
310 qmkusbConfigureHookI(&drivers.array[i].driver);
312 osalSysUnlockFromISR();
314 case USB_EVENT_SUSPEND:
315 #ifdef SLEEP_LED_ENABLE
317 #endif /* SLEEP_LED_ENABLE */
319 case USB_EVENT_UNCONFIGURED:
321 case USB_EVENT_RESET:
322 for (int i=0;i<NUM_USB_DRIVERS;i++) {
324 /* Disconnection event on suspend.*/
325 qmkusbSuspendHookI(&drivers.array[i].driver);
326 chSysUnlockFromISR();
330 case USB_EVENT_WAKEUP:
331 //TODO: from ISR! print("[W]");
332 for (int i=0;i<NUM_USB_DRIVERS;i++) {
334 /* Disconnection event on suspend.*/
335 qmkusbWakeupHookI(&drivers.array[i].driver);
336 chSysUnlockFromISR();
338 suspend_wakeup_init();
339 #ifdef SLEEP_LED_ENABLE
341 // NOTE: converters may not accept this
342 led_set(host_keyboard_leds());
343 #endif /* SLEEP_LED_ENABLE */
346 case USB_EVENT_STALLED:
351 /* Function used locally in os/hal/src/usb.c for getting descriptors
352 * need it here for HID descriptor */
353 static uint16_t get_hword(uint8_t *p) {
357 hw |= (uint16_t)*p << 8U;
362 * Appendix G: HID Request Support Requirements
364 * The following table enumerates the requests that need to be supported by various types of HID class devices.
365 * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol
366 * ------------------------------------------------------------------------------------------
367 * Boot Mouse Required Optional Optional Optional Required Required
368 * Non-Boot Mouse Required Optional Optional Optional Optional Optional
369 * Boot Keyboard Required Optional Required Required Required Required
370 * Non-Boot Keybrd Required Optional Required Required Optional Optional
371 * Other Device Required Optional Optional Optional Optional Optional
374 #ifdef SHARED_EP_ENABLE
375 static uint8_t set_report_buf[2] __attribute__((aligned(2)));
376 static void set_led_transfer_cb(USBDriver *usbp) {
377 if ((set_report_buf[0] == REPORT_ID_KEYBOARD) ||
378 (set_report_buf[0] == REPORT_ID_NKRO)) {
379 keyboard_led_stats = set_report_buf[1];
384 /* Callback for SETUP request on the endpoint 0 (control) */
385 static bool usb_request_hook_cb(USBDriver *usbp) {
386 const USBDescriptor *dp;
389 /* usbp->setup fields:
390 * 0: bmRequestType (bitmask)
392 * 2,3: (LSB,MSB) wValue
393 * 4,5: (LSB,MSB) wIndex
394 * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */
396 /* Handle HID class specific requests */
397 if(((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) &&
398 ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) {
399 switch(usbp->setup[0] & USB_RTYPE_DIR_MASK) {
400 case USB_RTYPE_DIR_DEV2HOST:
401 switch(usbp->setup[1]) { /* bRequest */
403 switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */
404 case KEYBOARD_INTERFACE:
405 usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL);
409 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
410 case MOUSE_INTERFACE:
411 usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL);
417 usbSetupTransfer(usbp, NULL, 0, NULL);
423 case HID_GET_PROTOCOL:
424 if((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */
425 usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL);
431 usbSetupTransfer(usbp, &keyboard_idle, 1, NULL);
437 case USB_RTYPE_DIR_HOST2DEV:
438 switch(usbp->setup[1]) { /* bRequest */
440 switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */
441 case KEYBOARD_INTERFACE:
442 #if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
443 case SHARED_INTERFACE:
445 /* keyboard_led_stats = <read byte from next OUT report>
446 * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */
448 #if defined(SHARED_EP_ENABLE)
449 if (usbp->setup[4] == SHARED_INTERFACE) {
453 if (usbp->setup[4] == KEYBOARD_INTERFACE && !keyboard_protocol) {
457 usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb);
459 usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL);
466 case HID_SET_PROTOCOL:
467 if((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */
468 keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */
470 keymap_config.nkro = !!keyboard_protocol;
471 if(!keymap_config.nkro && keyboard_idle) {
472 #else /* NKRO_ENABLE */
474 #endif /* NKRO_ENABLE */
475 /* arm the idle timer if boot protocol & idle */
476 osalSysLockFromISR();
477 chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
478 osalSysUnlockFromISR();
481 usbSetupTransfer(usbp, NULL, 0, NULL);
486 keyboard_idle = usbp->setup[3]; /* MSB(wValue) */
489 if(!keymap_config.nkro && keyboard_idle) {
490 #else /* NKRO_ENABLE */
492 #endif /* NKRO_ENABLE */
493 osalSysLockFromISR();
494 chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
495 osalSysUnlockFromISR();
497 usbSetupTransfer(usbp, NULL, 0, NULL);
505 /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */
506 if((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) {
507 dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4]));
510 usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL);
514 for (int i=0;i<NUM_USB_DRIVERS;i++) {
515 if (drivers.array[i].config.int_in) {
516 // NOTE: Assumes that we only have one serial driver
517 return qmkusbRequestsHook(usbp);
524 /* Start-of-frame callback */
525 static void usb_sof_cb(USBDriver *usbp) {
527 osalSysLockFromISR();
528 for (int i=0; i<NUM_USB_DRIVERS;i++) {
529 qmkusbSOFHookI(&drivers.array[i].driver);
531 osalSysUnlockFromISR();
535 /* USB driver configuration */
536 static const USBConfig usbcfg = {
537 usb_event_cb, /* USB events callback */
538 usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */
539 usb_request_hook_cb, /* Requests hook callback */
540 usb_sof_cb /* Start Of Frame callback */
544 * Initialize the USB driver
546 void init_usb_driver(USBDriver *usbp) {
547 for (int i=0; i<NUM_USB_DRIVERS;i++) {
548 QMKUSBDriver* driver = &drivers.array[i].driver;
549 drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state;
550 drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state;
551 drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state;
552 qmkusbObjectInit(driver, &drivers.array[i].config);
553 qmkusbStart(driver, &drivers.array[i].config);
557 * Activates the USB driver and then the USB bus pull-up on D+.
558 * Note, a delay is inserted in order to not have to disconnect the cable
561 usbDisconnectBus(usbp);
563 usbStart(usbp, &usbcfg);
566 chVTObjectInit(&keyboard_idle_timer);
569 /* ---------------------------------------------------------
571 * ---------------------------------------------------------
573 /* keyboard IN callback hander (a kbd report has made it IN) */
574 #ifndef KEYBOARD_SHARED_EP
575 void kbd_in_cb(USBDriver *usbp, usbep_t ep) {
582 /* start-of-frame handler
583 * TODO: i guess it would be better to re-implement using timers,
584 * so that this is not going to have to be checked every 1ms */
585 void kbd_sof_cb(USBDriver *usbp) {
589 /* Idle requests timer code
590 * callback (called from ISR, unlocked state) */
591 static void keyboard_idle_timer_cb(void *arg) {
592 USBDriver *usbp = (USBDriver *)arg;
594 osalSysLockFromISR();
596 /* check that the states of things are as they're supposed to */
597 if(usbGetDriverStateI(usbp) != USB_ACTIVE) {
598 /* do not rearm the timer, should be enabled on IDLE request */
599 osalSysUnlockFromISR();
604 if(!keymap_config.nkro && keyboard_idle && keyboard_protocol) {
605 #else /* NKRO_ENABLE */
606 if(keyboard_idle && keyboard_protocol) {
607 #endif /* NKRO_ENABLE */
608 /* TODO: are we sure we want the KBD_ENDPOINT? */
609 if(!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) {
610 usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE);
612 /* rearm the timer */
613 chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
616 /* do not rearm the timer if the condition above fails
617 * it should be enabled again on either IDLE or SET_PROTOCOL requests */
618 osalSysUnlockFromISR();
622 uint8_t keyboard_leds(void) {
623 return (uint8_t)(keyboard_led_stats & 0xFF);
626 /* prepare and start sending a report IN
627 * not callable from ISR or locked state */
628 void send_keyboard(report_keyboard_t *report) {
630 if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
637 if(keymap_config.nkro && keyboard_protocol) { /* NKRO protocol */
638 /* need to wait until the previous packet has made it through */
639 /* can rewrite this using the synchronous API, then would wait
640 * until *after* the packet has been transmitted. I think
641 * this is more efficient */
642 /* busy wait, should be short and not very common */
644 if(usbGetTransmitStatusI(&USB_DRIVER, SHARED_IN_EPNUM)) {
645 /* Need to either suspend, or loop and call unlock/lock during
646 * every iteration - otherwise the system will remain locked,
647 * no interrupts served, so USB not going through as well.
648 * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
649 osalThreadSuspendS(&(&USB_DRIVER)->epc[SHARED_IN_EPNUM]->in_state->thread);
651 usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report));
654 #endif /* NKRO_ENABLE */
655 { /* regular protocol */
656 /* need to wait until the previous packet has made it through */
657 /* busy wait, should be short and not very common */
659 if(usbGetTransmitStatusI(&USB_DRIVER, KEYBOARD_IN_EPNUM)) {
660 /* Need to either suspend, or loop and call unlock/lock during
661 * every iteration - otherwise the system will remain locked,
662 * no interrupts served, so USB not going through as well.
663 * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
664 osalThreadSuspendS(&(&USB_DRIVER)->epc[KEYBOARD_IN_EPNUM]->in_state->thread);
667 if (keyboard_protocol) {
668 data = (uint8_t*)report;
669 size = KEYBOARD_REPORT_SIZE;
670 } else { /* boot protocol */
671 data = &report->mods;
674 usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, data, size);
677 keyboard_report_sent = *report;
680 /* ---------------------------------------------------------
682 * ---------------------------------------------------------
687 #ifndef MOUSE_SHARED_EP
688 /* mouse IN callback hander (a mouse report has made it IN) */
689 void mouse_in_cb(USBDriver *usbp, usbep_t ep) {
695 void send_mouse(report_mouse_t *report) {
697 if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
702 if(usbGetTransmitStatusI(&USB_DRIVER, MOUSE_IN_EPNUM)) {
703 /* Need to either suspend, or loop and call unlock/lock during
704 * every iteration - otherwise the system will remain locked,
705 * no interrupts served, so USB not going through as well.
706 * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
707 if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[MOUSE_IN_EPNUM]->in_state->thread, MS2ST(10))==MSG_TIMEOUT) {
712 usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t));
716 #else /* MOUSE_ENABLE */
717 void send_mouse(report_mouse_t *report) {
720 #endif /* MOUSE_ENABLE */
722 /* ---------------------------------------------------------
723 * Shared EP functions
724 * ---------------------------------------------------------
726 #ifdef SHARED_EP_ENABLE
727 /* shared IN callback hander */
728 void shared_in_cb(USBDriver *usbp, usbep_t ep) {
735 /* ---------------------------------------------------------
737 * ---------------------------------------------------------
740 #ifdef EXTRAKEY_ENABLE
741 static void send_extra_report(uint8_t report_id, uint16_t data) {
743 if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
748 report_extra_t report = {
749 .report_id = report_id,
753 usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t));
757 void send_system(uint16_t data) {
758 send_extra_report(REPORT_ID_SYSTEM, data);
761 void send_consumer(uint16_t data) {
762 send_extra_report(REPORT_ID_CONSUMER, data);
765 #else /* EXTRAKEY_ENABLE */
766 void send_system(uint16_t data) {
769 void send_consumer(uint16_t data) {
772 #endif /* EXTRAKEY_ENABLE */
774 /* ---------------------------------------------------------
776 * ---------------------------------------------------------
779 #ifdef CONSOLE_ENABLE
781 int8_t sendchar(uint8_t c) {
782 // The previous implmentation had timeouts, but I think it's better to just slow down
783 // and make sure that everything is transferred, rather than dropping stuff
784 return chnWrite(&drivers.console_driver.driver, &c, 1);
787 // Just a dummy function for now, this could be exposed as a weak function
788 // Or connected to the actual QMK console
789 static void console_receive( uint8_t *data, uint8_t length ) {
794 void console_task(void) {
795 uint8_t buffer[CONSOLE_EPSIZE];
798 size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
800 console_receive(buffer, size);
805 #else /* CONSOLE_ENABLE */
806 int8_t sendchar(uint8_t c) {
810 #endif /* CONSOLE_ENABLE */
812 void sendchar_pf(void *p, char c) {
814 sendchar((uint8_t)c);
818 void raw_hid_send( uint8_t *data, uint8_t length ) {
819 // TODO: implement variable size packet
820 if ( length != RAW_EPSIZE )
825 chnWrite(&drivers.raw_driver.driver, data, length);
828 __attribute__ ((weak))
829 void raw_hid_receive( uint8_t *data, uint8_t length ) {
830 // Users should #include "raw_hid.h" in their own code
831 // and implement this function there. Leave this as weak linkage
832 // so users can opt to not handle data coming in.
835 void raw_hid_task(void) {
836 uint8_t buffer[RAW_EPSIZE];
839 size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
841 raw_hid_receive(buffer, size);
850 void send_midi_packet(MIDI_EventPacket_t* event) {
851 chnWrite(&drivers.midi_driver.driver, (uint8_t*)event, sizeof(MIDI_EventPacket_t));
854 bool recv_midi_packet(MIDI_EventPacket_t* const event) {
855 size_t size = chnReadTimeout(&drivers.midi_driver.driver, (uint8_t*)event, sizeof(MIDI_EventPacket_t), TIME_IMMEDIATE);
856 return size == sizeof(MIDI_EventPacket_t);
861 #ifdef VIRTSER_ENABLE
863 void virtser_send(const uint8_t byte) {
864 chnWrite(&drivers.serial_driver.driver, &byte, 1);
867 __attribute__ ((weak))
868 void virtser_recv(uint8_t c)
873 void virtser_task(void) {
874 uint8_t numBytesReceived = 0;
877 numBytesReceived = chnReadTimeout(&drivers.serial_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
878 for (int i=0;i<numBytesReceived;i++) {
879 virtser_recv(buffer[i]);
881 } while (numBytesReceived > 0);