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
19 * Implementation notes:
21 * USBEndpointConfig - Configured using explicit order instead of struct member name.
22 * This is due to ChibiOS hal LLD differences, which is dependent on hardware,
23 * "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`.
24 * Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file
25 * makes the assumption this is safe to avoid littering with preprocessor directives.
36 #ifdef SLEEP_LED_ENABLE
37 # include "sleep_led.h"
41 #include "usb_descriptor.h"
42 #include "usb_driver.h"
45 # include "keycode_config.h"
47 extern keymap_config_t keymap_config
;
50 /* ---------------------------------------------------------
51 * Global interface variables and declarations
52 * ---------------------------------------------------------
55 #ifndef usb_lld_connect_bus
56 # define usb_lld_connect_bus(usbp)
59 #ifndef usb_lld_disconnect_bus
60 # define usb_lld_disconnect_bus(usbp)
63 uint8_t keyboard_idle
__attribute__((aligned(2))) = 0;
64 uint8_t keyboard_protocol
__attribute__((aligned(2))) = 1;
65 uint8_t keyboard_led_stats
= 0;
66 volatile uint16_t keyboard_idle_count
= 0;
67 static virtual_timer_t keyboard_idle_timer
;
68 static void keyboard_idle_timer_cb(void *arg
);
70 report_keyboard_t keyboard_report_sent
= {{0}};
72 report_mouse_t mouse_report_blank
= {0};
73 #endif /* MOUSE_ENABLE */
74 #ifdef EXTRAKEY_ENABLE
75 uint8_t extra_report_blank
[3] = {0};
76 #endif /* EXTRAKEY_ENABLE */
78 /* ---------------------------------------------------------
79 * Descriptors and USB driver objects
80 * ---------------------------------------------------------
83 /* HID specific constants */
84 #define HID_GET_REPORT 0x01
85 #define HID_GET_IDLE 0x02
86 #define HID_GET_PROTOCOL 0x03
87 #define HID_SET_REPORT 0x09
88 #define HID_SET_IDLE 0x0A
89 #define HID_SET_PROTOCOL 0x0B
92 * Handles the GET_DESCRIPTOR callback
94 * Returns the proper descriptor
96 static const USBDescriptor
*usb_get_descriptor_cb(USBDriver
*usbp
, uint8_t dtype
, uint8_t dindex
, uint16_t wIndex
) {
98 static USBDescriptor desc
;
99 uint16_t wValue
= ((uint16_t)dtype
<< 8) | dindex
;
100 desc
.ud_string
= NULL
;
101 desc
.ud_size
= get_usb_descriptor(wValue
, wIndex
, (const void **const) & desc
.ud_string
);
102 if (desc
.ud_string
== NULL
)
108 #ifndef KEYBOARD_SHARED_EP
109 /* keyboard endpoint state structure */
110 static USBInEndpointState kbd_ep_state
;
111 /* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
112 static const USBEndpointConfig kbd_ep_config
= {
113 USB_EP_MODE_TYPE_INTR
, /* Interrupt EP */
114 NULL
, /* SETUP packet notification callback */
115 kbd_in_cb
, /* IN notification callback */
116 NULL
, /* OUT notification callback */
117 KEYBOARD_EPSIZE
, /* IN maximum packet size */
118 0, /* OUT maximum packet size */
119 &kbd_ep_state
, /* IN Endpoint state */
120 NULL
, /* OUT endpoint state */
121 2, /* IN multiplier */
122 NULL
/* SETUP buffer (not a SETUP endpoint) */
126 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
127 /* mouse endpoint state structure */
128 static USBInEndpointState mouse_ep_state
;
130 /* mouse endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
131 static const USBEndpointConfig mouse_ep_config
= {
132 USB_EP_MODE_TYPE_INTR
, /* Interrupt EP */
133 NULL
, /* SETUP packet notification callback */
134 mouse_in_cb
, /* IN notification callback */
135 NULL
, /* OUT notification callback */
136 MOUSE_EPSIZE
, /* IN maximum packet size */
137 0, /* OUT maximum packet size */
138 &mouse_ep_state
, /* IN Endpoint state */
139 NULL
, /* OUT endpoint state */
140 2, /* IN multiplier */
141 NULL
/* SETUP buffer (not a SETUP endpoint) */
145 #ifdef SHARED_EP_ENABLE
146 /* shared endpoint state structure */
147 static USBInEndpointState shared_ep_state
;
149 /* shared endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
150 static const USBEndpointConfig shared_ep_config
= {
151 USB_EP_MODE_TYPE_INTR
, /* Interrupt EP */
152 NULL
, /* SETUP packet notification callback */
153 shared_in_cb
, /* IN notification callback */
154 NULL
, /* OUT notification callback */
155 SHARED_EPSIZE
, /* IN maximum packet size */
156 0, /* OUT maximum packet size */
157 &shared_ep_state
, /* IN Endpoint state */
158 NULL
, /* OUT endpoint state */
159 2, /* IN multiplier */
160 NULL
/* SETUP buffer (not a SETUP endpoint) */
165 size_t queue_capacity_in
;
166 size_t queue_capacity_out
;
167 USBInEndpointState in_ep_state
;
168 USBOutEndpointState out_ep_state
;
169 USBInEndpointState int_ep_state
;
170 USBEndpointConfig in_ep_config
;
171 USBEndpointConfig out_ep_config
;
172 USBEndpointConfig int_ep_config
;
173 const QMKUSBConfig config
;
175 } usb_driver_config_t
;
177 /* Reusable initialization structure - see USBEndpointConfig comment at top of file */
178 #define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \
180 .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \
183 stream##_IN_MODE, /* Interrupt EP */ \
184 NULL, /* SETUP packet notification callback */ \
185 qmkusbDataTransmitted, /* IN notification callback */ \
186 NULL, /* OUT notification callback */ \
187 stream##_EPSIZE, /* IN maximum packet size */ \
188 0, /* OUT maximum packet size */ \
189 NULL, /* IN Endpoint state */ \
190 NULL, /* OUT endpoint state */ \
191 2, /* IN multiplier */ \
192 NULL /* SETUP buffer (not a SETUP endpoint) */ \
196 stream##_OUT_MODE, /* Interrupt EP */ \
197 NULL, /* SETUP packet notification callback */ \
198 NULL, /* IN notification callback */ \
199 qmkusbDataReceived, /* OUT notification callback */ \
200 0, /* IN maximum packet size */ \
201 stream##_EPSIZE, /* OUT maximum packet size */ \
202 NULL, /* IN Endpoint state */ \
203 NULL, /* OUT endpoint state */ \
204 2, /* IN multiplier */ \
205 NULL, /* SETUP buffer (not a SETUP endpoint) */ \
209 USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \
210 NULL, /* SETUP packet notification callback */ \
211 qmkusbInterruptTransmitted, /* IN notification callback */ \
212 NULL, /* OUT notification callback */ \
213 CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \
214 0, /* OUT maximum packet size */ \
215 NULL, /* IN Endpoint state */ \
216 NULL, /* OUT endpoint state */ \
217 2, /* IN multiplier */ \
218 NULL, /* SETUP buffer (not a SETUP endpoint) */ \
221 .usbp = &USB_DRIVER, \
222 .bulk_in = stream##_IN_EPNUM, \
223 .bulk_out = stream##_OUT_EPNUM, \
224 .int_in = notification, \
225 .in_buffers = stream##_IN_CAPACITY, \
226 .out_buffers = stream##_OUT_CAPACITY, \
227 .in_size = stream##_EPSIZE, \
228 .out_size = stream##_EPSIZE, \
229 .fixed_size = fixedsize, \
230 .ib = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \
231 .ob = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \
238 #ifdef CONSOLE_ENABLE
239 usb_driver_config_t console_driver
;
242 usb_driver_config_t raw_driver
;
245 usb_driver_config_t midi_driver
;
247 #ifdef VIRTSER_ENABLE
248 usb_driver_config_t serial_driver
;
251 usb_driver_config_t array
[0];
253 } usb_driver_configs_t
;
255 static usb_driver_configs_t drivers
= {
256 #ifdef CONSOLE_ENABLE
257 # define CONSOLE_IN_CAPACITY 4
258 # define CONSOLE_OUT_CAPACITY 4
259 # define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR
260 # define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR
261 .console_driver
= QMK_USB_DRIVER_CONFIG(CONSOLE
, 0, true),
264 # define RAW_IN_CAPACITY 4
265 # define RAW_OUT_CAPACITY 4
266 # define RAW_IN_MODE USB_EP_MODE_TYPE_INTR
267 # define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR
268 .raw_driver
= QMK_USB_DRIVER_CONFIG(RAW
, 0, false),
272 # define MIDI_STREAM_IN_CAPACITY 4
273 # define MIDI_STREAM_OUT_CAPACITY 4
274 # define MIDI_STREAM_IN_MODE USB_EP_MODE_TYPE_BULK
275 # define MIDI_STREAM_OUT_MODE USB_EP_MODE_TYPE_BULK
276 .midi_driver
= QMK_USB_DRIVER_CONFIG(MIDI_STREAM
, 0, false),
279 #ifdef VIRTSER_ENABLE
280 # define CDC_IN_CAPACITY 4
281 # define CDC_OUT_CAPACITY 4
282 # define CDC_IN_MODE USB_EP_MODE_TYPE_BULK
283 # define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK
284 .serial_driver
= QMK_USB_DRIVER_CONFIG(CDC
, CDC_NOTIFICATION_EPNUM
, false),
288 #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
290 /* ---------------------------------------------------------
291 * USB driver functions
292 * ---------------------------------------------------------
295 /* Handles the USB driver global events
296 * TODO: maybe disable some things when connection is lost? */
297 static void usb_event_cb(USBDriver
*usbp
, usbevent_t event
) {
299 case USB_EVENT_ADDRESS
:
302 case USB_EVENT_CONFIGURED
:
303 osalSysLockFromISR();
304 /* Enable the endpoints specified into the configuration. */
305 #ifndef KEYBOARD_SHARED_EP
306 usbInitEndpointI(usbp
, KEYBOARD_IN_EPNUM
, &kbd_ep_config
);
308 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
309 usbInitEndpointI(usbp
, MOUSE_IN_EPNUM
, &mouse_ep_config
);
311 #ifdef SHARED_EP_ENABLE
312 usbInitEndpointI(usbp
, SHARED_IN_EPNUM
, &shared_ep_config
);
314 for (int i
= 0; i
< NUM_USB_DRIVERS
; i
++) {
315 usbInitEndpointI(usbp
, drivers
.array
[i
].config
.bulk_in
, &drivers
.array
[i
].in_ep_config
);
316 usbInitEndpointI(usbp
, drivers
.array
[i
].config
.bulk_out
, &drivers
.array
[i
].out_ep_config
);
317 if (drivers
.array
[i
].config
.int_in
) {
318 usbInitEndpointI(usbp
, drivers
.array
[i
].config
.int_in
, &drivers
.array
[i
].int_ep_config
);
320 qmkusbConfigureHookI(&drivers
.array
[i
].driver
);
322 osalSysUnlockFromISR();
324 case USB_EVENT_SUSPEND
:
325 #ifdef SLEEP_LED_ENABLE
327 #endif /* SLEEP_LED_ENABLE */
329 case USB_EVENT_UNCONFIGURED
:
331 case USB_EVENT_RESET
:
332 for (int i
= 0; i
< NUM_USB_DRIVERS
; i
++) {
334 /* Disconnection event on suspend.*/
335 qmkusbSuspendHookI(&drivers
.array
[i
].driver
);
336 chSysUnlockFromISR();
340 case USB_EVENT_WAKEUP
:
341 // TODO: from ISR! print("[W]");
342 for (int i
= 0; i
< NUM_USB_DRIVERS
; i
++) {
344 /* Disconnection event on suspend.*/
345 qmkusbWakeupHookI(&drivers
.array
[i
].driver
);
346 chSysUnlockFromISR();
348 suspend_wakeup_init();
349 #ifdef SLEEP_LED_ENABLE
351 // NOTE: converters may not accept this
352 led_set(host_keyboard_leds());
353 #endif /* SLEEP_LED_ENABLE */
356 case USB_EVENT_STALLED
:
361 /* Function used locally in os/hal/src/usb.c for getting descriptors
362 * need it here for HID descriptor */
363 static uint16_t get_hword(uint8_t *p
) {
367 hw
|= (uint16_t)*p
<< 8U;
372 * Appendix G: HID Request Support Requirements
374 * The following table enumerates the requests that need to be supported by various types of HID class devices.
375 * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol
376 * ------------------------------------------------------------------------------------------
377 * Boot Mouse Required Optional Optional Optional Required Required
378 * Non-Boot Mouse Required Optional Optional Optional Optional Optional
379 * Boot Keyboard Required Optional Required Required Required Required
380 * Non-Boot Keybrd Required Optional Required Required Optional Optional
381 * Other Device Required Optional Optional Optional Optional Optional
384 static uint8_t set_report_buf
[2] __attribute__((aligned(2)));
385 static void set_led_transfer_cb(USBDriver
*usbp
) {
386 if (usbp
->setup
[6] == 2) { /* LSB(wLength) */
387 uint8_t report_id
= set_report_buf
[0];
388 if ((report_id
== REPORT_ID_KEYBOARD
) || (report_id
== REPORT_ID_NKRO
)) {
389 keyboard_led_stats
= set_report_buf
[1];
392 keyboard_led_stats
= set_report_buf
[0];
396 /* Callback for SETUP request on the endpoint 0 (control) */
397 static bool usb_request_hook_cb(USBDriver
*usbp
) {
398 const USBDescriptor
*dp
;
400 /* usbp->setup fields:
401 * 0: bmRequestType (bitmask)
403 * 2,3: (LSB,MSB) wValue
404 * 4,5: (LSB,MSB) wIndex
405 * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */
407 /* Handle HID class specific requests */
408 if (((usbp
->setup
[0] & USB_RTYPE_TYPE_MASK
) == USB_RTYPE_TYPE_CLASS
) && ((usbp
->setup
[0] & USB_RTYPE_RECIPIENT_MASK
) == USB_RTYPE_RECIPIENT_INTERFACE
)) {
409 switch (usbp
->setup
[0] & USB_RTYPE_DIR_MASK
) {
410 case USB_RTYPE_DIR_DEV2HOST
:
411 switch (usbp
->setup
[1]) { /* bRequest */
413 switch (usbp
->setup
[4]) { /* LSB(wIndex) (check MSB==0?) */
414 case KEYBOARD_INTERFACE
:
415 usbSetupTransfer(usbp
, (uint8_t *)&keyboard_report_sent
, sizeof(keyboard_report_sent
), NULL
);
419 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
420 case MOUSE_INTERFACE
:
421 usbSetupTransfer(usbp
, (uint8_t *)&mouse_report_blank
, sizeof(mouse_report_blank
), NULL
);
427 usbSetupTransfer(usbp
, NULL
, 0, NULL
);
433 case HID_GET_PROTOCOL
:
434 if ((usbp
->setup
[4] == KEYBOARD_INTERFACE
) && (usbp
->setup
[5] == 0)) { /* wIndex */
435 usbSetupTransfer(usbp
, &keyboard_protocol
, 1, NULL
);
441 usbSetupTransfer(usbp
, &keyboard_idle
, 1, NULL
);
447 case USB_RTYPE_DIR_HOST2DEV
:
448 switch (usbp
->setup
[1]) { /* bRequest */
450 switch (usbp
->setup
[4]) { /* LSB(wIndex) (check MSB==0?) */
451 case KEYBOARD_INTERFACE
:
452 #if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
453 case SHARED_INTERFACE
:
455 usbSetupTransfer(usbp
, set_report_buf
, sizeof(set_report_buf
), set_led_transfer_cb
);
461 case HID_SET_PROTOCOL
:
462 if ((usbp
->setup
[4] == KEYBOARD_INTERFACE
) && (usbp
->setup
[5] == 0)) { /* wIndex */
463 keyboard_protocol
= ((usbp
->setup
[2]) != 0x00); /* LSB(wValue) */
465 keymap_config
.nkro
= !!keyboard_protocol
;
466 if (!keymap_config
.nkro
&& keyboard_idle
) {
467 #else /* NKRO_ENABLE */
469 #endif /* NKRO_ENABLE */
470 /* arm the idle timer if boot protocol & idle */
471 osalSysLockFromISR();
472 chVTSetI(&keyboard_idle_timer
, 4 * TIME_MS2I(keyboard_idle
), keyboard_idle_timer_cb
, (void *)usbp
);
473 osalSysUnlockFromISR();
476 usbSetupTransfer(usbp
, NULL
, 0, NULL
);
481 keyboard_idle
= usbp
->setup
[3]; /* MSB(wValue) */
484 if (!keymap_config
.nkro
&& keyboard_idle
) {
485 #else /* NKRO_ENABLE */
487 #endif /* NKRO_ENABLE */
488 osalSysLockFromISR();
489 chVTSetI(&keyboard_idle_timer
, 4 * TIME_MS2I(keyboard_idle
), keyboard_idle_timer_cb
, (void *)usbp
);
490 osalSysUnlockFromISR();
492 usbSetupTransfer(usbp
, NULL
, 0, NULL
);
500 /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */
501 if ((usbp
->setup
[0] == 0x81) && (usbp
->setup
[1] == USB_REQ_GET_DESCRIPTOR
)) {
502 dp
= usbp
->config
->get_descriptor_cb(usbp
, usbp
->setup
[3], usbp
->setup
[2], get_hword(&usbp
->setup
[4]));
503 if (dp
== NULL
) return FALSE
;
504 usbSetupTransfer(usbp
, (uint8_t *)dp
->ud_string
, dp
->ud_size
, NULL
);
508 for (int i
= 0; i
< NUM_USB_DRIVERS
; i
++) {
509 if (drivers
.array
[i
].config
.int_in
) {
510 // NOTE: Assumes that we only have one serial driver
511 return qmkusbRequestsHook(usbp
);
518 /* Start-of-frame callback */
519 static void usb_sof_cb(USBDriver
*usbp
) {
521 osalSysLockFromISR();
522 for (int i
= 0; i
< NUM_USB_DRIVERS
; i
++) {
523 qmkusbSOFHookI(&drivers
.array
[i
].driver
);
525 osalSysUnlockFromISR();
528 /* USB driver configuration */
529 static const USBConfig usbcfg
= {
530 usb_event_cb
, /* USB events callback */
531 usb_get_descriptor_cb
, /* Device GET_DESCRIPTOR request callback */
532 usb_request_hook_cb
, /* Requests hook callback */
533 usb_sof_cb
/* Start Of Frame callback */
537 * Initialize the USB driver
539 void init_usb_driver(USBDriver
*usbp
) {
540 for (int i
= 0; i
< NUM_USB_DRIVERS
; i
++) {
541 QMKUSBDriver
*driver
= &drivers
.array
[i
].driver
;
542 drivers
.array
[i
].in_ep_config
.in_state
= &drivers
.array
[i
].in_ep_state
;
543 drivers
.array
[i
].out_ep_config
.out_state
= &drivers
.array
[i
].out_ep_state
;
544 drivers
.array
[i
].int_ep_config
.in_state
= &drivers
.array
[i
].int_ep_state
;
545 qmkusbObjectInit(driver
, &drivers
.array
[i
].config
);
546 qmkusbStart(driver
, &drivers
.array
[i
].config
);
550 * Activates the USB driver and then the USB bus pull-up on D+.
551 * Note, a delay is inserted in order to not have to disconnect the cable
554 usbDisconnectBus(usbp
);
556 usbStart(usbp
, &usbcfg
);
559 chVTObjectInit(&keyboard_idle_timer
);
562 /* ---------------------------------------------------------
564 * ---------------------------------------------------------
566 /* keyboard IN callback hander (a kbd report has made it IN) */
567 #ifndef KEYBOARD_SHARED_EP
568 void kbd_in_cb(USBDriver
*usbp
, usbep_t ep
) {
575 /* start-of-frame handler
576 * TODO: i guess it would be better to re-implement using timers,
577 * so that this is not going to have to be checked every 1ms */
578 void kbd_sof_cb(USBDriver
*usbp
) { (void)usbp
; }
580 /* Idle requests timer code
581 * callback (called from ISR, unlocked state) */
582 static void keyboard_idle_timer_cb(void *arg
) {
583 USBDriver
*usbp
= (USBDriver
*)arg
;
585 osalSysLockFromISR();
587 /* check that the states of things are as they're supposed to */
588 if (usbGetDriverStateI(usbp
) != USB_ACTIVE
) {
589 /* do not rearm the timer, should be enabled on IDLE request */
590 osalSysUnlockFromISR();
595 if (!keymap_config
.nkro
&& keyboard_idle
&& keyboard_protocol
) {
596 #else /* NKRO_ENABLE */
597 if (keyboard_idle
&& keyboard_protocol
) {
598 #endif /* NKRO_ENABLE */
599 /* TODO: are we sure we want the KBD_ENDPOINT? */
600 if (!usbGetTransmitStatusI(usbp
, KEYBOARD_IN_EPNUM
)) {
601 usbStartTransmitI(usbp
, KEYBOARD_IN_EPNUM
, (uint8_t *)&keyboard_report_sent
, KEYBOARD_EPSIZE
);
603 /* rearm the timer */
604 chVTSetI(&keyboard_idle_timer
, 4 * TIME_MS2I(keyboard_idle
), keyboard_idle_timer_cb
, (void *)usbp
);
607 /* do not rearm the timer if the condition above fails
608 * it should be enabled again on either IDLE or SET_PROTOCOL requests */
609 osalSysUnlockFromISR();
613 uint8_t keyboard_leds(void) { return keyboard_led_stats
; }
615 /* prepare and start sending a report IN
616 * not callable from ISR or locked state */
617 void send_keyboard(report_keyboard_t
*report
) {
619 if (usbGetDriverStateI(&USB_DRIVER
) != USB_ACTIVE
) {
624 if (keymap_config
.nkro
&& keyboard_protocol
) { /* NKRO protocol */
625 /* need to wait until the previous packet has made it through */
626 /* can rewrite this using the synchronous API, then would wait
627 * until *after* the packet has been transmitted. I think
628 * this is more efficient */
629 /* busy wait, should be short and not very common */
630 if (usbGetTransmitStatusI(&USB_DRIVER
, SHARED_IN_EPNUM
)) {
631 /* Need to either suspend, or loop and call unlock/lock during
632 * every iteration - otherwise the system will remain locked,
633 * no interrupts served, so USB not going through as well.
634 * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
635 osalThreadSuspendS(&(&USB_DRIVER
)->epc
[SHARED_IN_EPNUM
]->in_state
->thread
);
637 /* after osalThreadSuspendS returns USB status might have changed */
638 if (usbGetDriverStateI(&USB_DRIVER
) != USB_ACTIVE
) {
642 usbStartTransmitI(&USB_DRIVER
, SHARED_IN_EPNUM
, (uint8_t *)report
, sizeof(struct nkro_report
));
644 #endif /* NKRO_ENABLE */
645 { /* regular protocol */
646 /* need to wait until the previous packet has made it through */
647 /* busy wait, should be short and not very common */
648 if (usbGetTransmitStatusI(&USB_DRIVER
, KEYBOARD_IN_EPNUM
)) {
649 /* Need to either suspend, or loop and call unlock/lock during
650 * every iteration - otherwise the system will remain locked,
651 * no interrupts served, so USB not going through as well.
652 * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
653 osalThreadSuspendS(&(&USB_DRIVER
)->epc
[KEYBOARD_IN_EPNUM
]->in_state
->thread
);
655 /* after osalThreadSuspendS returns USB status might have changed */
656 if (usbGetDriverStateI(&USB_DRIVER
) != USB_ACTIVE
) {
661 if (keyboard_protocol
) {
662 data
= (uint8_t *)report
;
663 size
= KEYBOARD_REPORT_SIZE
;
664 } else { /* boot protocol */
665 data
= &report
->mods
;
668 usbStartTransmitI(&USB_DRIVER
, KEYBOARD_IN_EPNUM
, data
, size
);
670 keyboard_report_sent
= *report
;
676 /* ---------------------------------------------------------
678 * ---------------------------------------------------------
683 # ifndef MOUSE_SHARED_EP
684 /* mouse IN callback hander (a mouse report has made it IN) */
685 void mouse_in_cb(USBDriver
*usbp
, usbep_t ep
) {
691 void send_mouse(report_mouse_t
*report
) {
693 if (usbGetDriverStateI(&USB_DRIVER
) != USB_ACTIVE
) {
698 if (usbGetTransmitStatusI(&USB_DRIVER
, MOUSE_IN_EPNUM
)) {
699 /* Need to either suspend, or loop and call unlock/lock during
700 * every iteration - otherwise the system will remain locked,
701 * no interrupts served, so USB not going through as well.
702 * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
703 if (osalThreadSuspendTimeoutS(&(&USB_DRIVER
)->epc
[MOUSE_IN_EPNUM
]->in_state
->thread
, TIME_MS2I(10)) == MSG_TIMEOUT
) {
708 usbStartTransmitI(&USB_DRIVER
, MOUSE_IN_EPNUM
, (uint8_t *)report
, sizeof(report_mouse_t
));
712 #else /* MOUSE_ENABLE */
713 void send_mouse(report_mouse_t
*report
) { (void)report
; }
714 #endif /* MOUSE_ENABLE */
716 /* ---------------------------------------------------------
717 * Shared EP functions
718 * ---------------------------------------------------------
720 #ifdef SHARED_EP_ENABLE
721 /* shared IN callback hander */
722 void shared_in_cb(USBDriver
*usbp
, usbep_t ep
) {
729 /* ---------------------------------------------------------
731 * ---------------------------------------------------------
734 #ifdef EXTRAKEY_ENABLE
735 static void send_extra(uint8_t report_id
, uint16_t data
) {
737 if (usbGetDriverStateI(&USB_DRIVER
) != USB_ACTIVE
) {
742 report_extra_t report
= {.report_id
= report_id
, .usage
= data
};
744 usbStartTransmitI(&USB_DRIVER
, SHARED_IN_EPNUM
, (uint8_t *)&report
, sizeof(report_extra_t
));
749 void send_system(uint16_t data
) {
750 #ifdef EXTRAKEY_ENABLE
751 send_extra(REPORT_ID_SYSTEM
, data
);
755 void send_consumer(uint16_t data
) {
756 #ifdef EXTRAKEY_ENABLE
757 send_extra(REPORT_ID_CONSUMER
, data
);
761 /* ---------------------------------------------------------
763 * ---------------------------------------------------------
766 #ifdef CONSOLE_ENABLE
768 int8_t sendchar(uint8_t c
) {
769 // The previous implmentation had timeouts, but I think it's better to just slow down
770 // and make sure that everything is transferred, rather than dropping stuff
771 return chnWrite(&drivers
.console_driver
.driver
, &c
, 1);
774 // Just a dummy function for now, this could be exposed as a weak function
775 // Or connected to the actual QMK console
776 static void console_receive(uint8_t *data
, uint8_t length
) {
781 void console_task(void) {
782 uint8_t buffer
[CONSOLE_EPSIZE
];
785 size_t size
= chnReadTimeout(&drivers
.console_driver
.driver
, buffer
, sizeof(buffer
), TIME_IMMEDIATE
);
787 console_receive(buffer
, size
);
792 #else /* CONSOLE_ENABLE */
793 int8_t sendchar(uint8_t c
) {
797 #endif /* CONSOLE_ENABLE */
799 void sendchar_pf(void *p
, char c
) {
801 sendchar((uint8_t)c
);
805 void raw_hid_send(uint8_t *data
, uint8_t length
) {
806 // TODO: implement variable size packet
807 if (length
!= RAW_EPSIZE
) {
810 chnWrite(&drivers
.raw_driver
.driver
, data
, length
);
813 __attribute__((weak
)) void raw_hid_receive(uint8_t *data
, uint8_t length
) {
814 // Users should #include "raw_hid.h" in their own code
815 // and implement this function there. Leave this as weak linkage
816 // so users can opt to not handle data coming in.
819 void raw_hid_task(void) {
820 uint8_t buffer
[RAW_EPSIZE
];
823 size_t size
= chnReadTimeout(&drivers
.raw_driver
.driver
, buffer
, sizeof(buffer
), TIME_IMMEDIATE
);
825 raw_hid_receive(buffer
, size
);
834 void send_midi_packet(MIDI_EventPacket_t
*event
) { chnWrite(&drivers
.midi_driver
.driver
, (uint8_t *)event
, sizeof(MIDI_EventPacket_t
)); }
836 bool recv_midi_packet(MIDI_EventPacket_t
*const event
) {
837 size_t size
= chnReadTimeout(&drivers
.midi_driver
.driver
, (uint8_t *)event
, sizeof(MIDI_EventPacket_t
), TIME_IMMEDIATE
);
838 return size
== sizeof(MIDI_EventPacket_t
);
840 void midi_ep_task(void) {
841 uint8_t buffer
[MIDI_STREAM_EPSIZE
];
844 size_t size
= chnReadTimeout(&drivers
.midi_driver
.driver
, buffer
, sizeof(buffer
), TIME_IMMEDIATE
);
846 MIDI_EventPacket_t event
;
847 recv_midi_packet(&event
);
853 #ifdef VIRTSER_ENABLE
855 void virtser_send(const uint8_t byte
) { chnWrite(&drivers
.serial_driver
.driver
, &byte
, 1); }
857 __attribute__((weak
)) void virtser_recv(uint8_t c
) {
861 void virtser_task(void) {
862 uint8_t numBytesReceived
= 0;
865 numBytesReceived
= chnReadTimeout(&drivers
.serial_driver
.driver
, buffer
, sizeof(buffer
), TIME_IMMEDIATE
);
866 for (int i
= 0; i
< numBytesReceived
; i
++) {
867 virtser_recv(buffer
[i
]);
869 } while (numBytesReceived
> 0);