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) \
169 .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \
170 .in_ep_config = {.ep_mode = stream##_IN_MODE, \
172 .in_cb = qmkusbDataTransmitted, \
174 .in_maxsize = stream##_EPSIZE, \
175 .out_maxsize = 0, /* The pointer to the states will be filled during initialization */ \
179 .setup_buf = NULL}, \
182 .ep_mode = stream##_OUT_MODE, \
185 .out_cb = qmkusbDataReceived, \
187 .out_maxsize = stream##_EPSIZE, /* The pointer to the states will be filled during initialization */ \
195 .ep_mode = USB_EP_MODE_TYPE_INTR, \
197 .in_cb = qmkusbInterruptTransmitted, \
199 .in_maxsize = CDC_NOTIFICATION_EPSIZE, \
200 .out_maxsize = 0, /* The pointer to the states will be filled during initialization */ \
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)]){}, \
224 #ifdef CONSOLE_ENABLE
225 usb_driver_config_t console_driver;
228 usb_driver_config_t raw_driver;
231 usb_driver_config_t midi_driver;
233 #ifdef VIRTSER_ENABLE
234 usb_driver_config_t serial_driver;
237 usb_driver_config_t array[0];
239 } usb_driver_configs_t;
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),
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),
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),
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),
274 #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
276 /* ---------------------------------------------------------
277 * USB driver functions
278 * ---------------------------------------------------------
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) {
285 case USB_EVENT_ADDRESS:
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);
294 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
295 usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config);
297 #ifdef SHARED_EP_ENABLE
298 usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config);
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);
306 qmkusbConfigureHookI(&drivers.array[i].driver);
308 osalSysUnlockFromISR();
310 case USB_EVENT_SUSPEND:
311 #ifdef SLEEP_LED_ENABLE
313 #endif /* SLEEP_LED_ENABLE */
315 case USB_EVENT_UNCONFIGURED:
317 case USB_EVENT_RESET:
318 for (int i = 0; i < NUM_USB_DRIVERS; i++) {
320 /* Disconnection event on suspend.*/
321 qmkusbSuspendHookI(&drivers.array[i].driver);
322 chSysUnlockFromISR();
326 case USB_EVENT_WAKEUP:
327 // TODO: from ISR! print("[W]");
328 for (int i = 0; i < NUM_USB_DRIVERS; i++) {
330 /* Disconnection event on suspend.*/
331 qmkusbWakeupHookI(&drivers.array[i].driver);
332 chSysUnlockFromISR();
334 suspend_wakeup_init();
335 #ifdef SLEEP_LED_ENABLE
337 // NOTE: converters may not accept this
338 led_set(host_keyboard_leds());
339 #endif /* SLEEP_LED_ENABLE */
342 case USB_EVENT_STALLED:
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) {
353 hw |= (uint16_t)*p << 8U;
358 * Appendix G: HID Request Support Requirements
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
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];
379 /* Callback for SETUP request on the endpoint 0 (control) */
380 static bool usb_request_hook_cb(USBDriver *usbp) {
381 const USBDescriptor *dp;
383 /* usbp->setup fields:
384 * 0: bmRequestType (bitmask)
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) */
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 */
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);
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);
410 usbSetupTransfer(usbp, NULL, 0, NULL);
416 case HID_GET_PROTOCOL:
417 if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */
418 usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL);
424 usbSetupTransfer(usbp, &keyboard_idle, 1, NULL);
430 case USB_RTYPE_DIR_HOST2DEV:
431 switch (usbp->setup[1]) { /* bRequest */
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);
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);
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) */
454 keymap_config.nkro = !!keyboard_protocol;
455 if (!keymap_config.nkro && keyboard_idle) {
456 #else /* NKRO_ENABLE */
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();
465 usbSetupTransfer(usbp, NULL, 0, NULL);
470 keyboard_idle = usbp->setup[3]; /* MSB(wValue) */
473 if (!keymap_config.nkro && keyboard_idle) {
474 #else /* NKRO_ENABLE */
476 #endif /* NKRO_ENABLE */
477 osalSysLockFromISR();
478 chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
479 osalSysUnlockFromISR();
481 usbSetupTransfer(usbp, NULL, 0, NULL);
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);
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);
507 /* Start-of-frame callback */
508 static void usb_sof_cb(USBDriver *usbp) {
510 osalSysLockFromISR();
511 for (int i = 0; i < NUM_USB_DRIVERS; i++) {
512 qmkusbSOFHookI(&drivers.array[i].driver);
514 osalSysUnlockFromISR();
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 */
526 * Initialize the USB driver
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);
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
543 usbDisconnectBus(usbp);
545 usbStart(usbp, &usbcfg);
548 chVTObjectInit(&keyboard_idle_timer);
551 /* ---------------------------------------------------------
553 * ---------------------------------------------------------
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) {
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; }
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;
574 osalSysLockFromISR();
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();
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);
592 /* rearm the timer */
593 chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
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();
602 uint8_t keyboard_leds(void) { return (uint8_t)(keyboard_led_stats & 0xFF); }
604 /* prepare and start sending a report IN
605 * not callable from ISR or locked state */
606 void send_keyboard(report_keyboard_t *report) {
608 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
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 */
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);
629 usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report));
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 */
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);
645 if (keyboard_protocol) {
646 data = (uint8_t *)report;
647 size = KEYBOARD_REPORT_SIZE;
648 } else { /* boot protocol */
649 data = &report->mods;
652 usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, data, size);
655 keyboard_report_sent = *report;
658 /* ---------------------------------------------------------
660 * ---------------------------------------------------------
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) {
673 void send_mouse(report_mouse_t *report) {
675 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
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) {
690 usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t));
694 #else /* MOUSE_ENABLE */
695 void send_mouse(report_mouse_t *report) { (void)report; }
696 #endif /* MOUSE_ENABLE */
698 /* ---------------------------------------------------------
699 * Shared EP functions
700 * ---------------------------------------------------------
702 #ifdef SHARED_EP_ENABLE
703 /* shared IN callback hander */
704 void shared_in_cb(USBDriver *usbp, usbep_t ep) {
711 /* ---------------------------------------------------------
713 * ---------------------------------------------------------
716 #ifdef EXTRAKEY_ENABLE
717 static void send_extra_report(uint8_t report_id, uint16_t data) {
719 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
724 report_extra_t report = {.report_id = report_id, .usage = data};
726 usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t));
730 void send_system(uint16_t data) { send_extra_report(REPORT_ID_SYSTEM, data); }
732 void send_consumer(uint16_t data) { send_extra_report(REPORT_ID_CONSUMER, data); }
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 */
739 /* ---------------------------------------------------------
741 * ---------------------------------------------------------
744 #ifdef CONSOLE_ENABLE
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);
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) {
759 void console_task(void) {
760 uint8_t buffer[CONSOLE_EPSIZE];
763 size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
765 console_receive(buffer, size);
770 #else /* CONSOLE_ENABLE */
771 int8_t sendchar(uint8_t c) {
775 #endif /* CONSOLE_ENABLE */
777 void sendchar_pf(void *p, char c) {
779 sendchar((uint8_t)c);
783 void raw_hid_send(uint8_t *data, uint8_t length) {
784 // TODO: implement variable size packet
785 if (length != RAW_EPSIZE) {
788 chnWrite(&drivers.raw_driver.driver, data, length);
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.
797 void raw_hid_task(void) {
798 uint8_t buffer[RAW_EPSIZE];
801 size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
803 raw_hid_receive(buffer, size);
812 void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); }
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);
821 #ifdef VIRTSER_ENABLE
823 void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); }
825 __attribute__((weak)) void virtser_recv(uint8_t c) {
829 void virtser_task(void) {
830 uint8_t numBytesReceived = 0;
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]);
837 } while (numBytesReceived > 0);