2020 February 29 Breaking Changes Update (#8064)
[jackhill/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 /*
19 * Implementation notes:
20 *
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.
26 */
27
28 #include "ch.h"
29 #include "hal.h"
30
31 #include "usb_main.h"
32
33 #include "host.h"
34 #include "debug.h"
35 #include "suspend.h"
36 #ifdef SLEEP_LED_ENABLE
37 # include "sleep_led.h"
38 # include "led.h"
39 #endif
40 #include "wait.h"
41 #include "usb_descriptor.h"
42 #include "usb_driver.h"
43
44 #ifdef NKRO_ENABLE
45 # include "keycode_config.h"
46
47 extern keymap_config_t keymap_config;
48 #endif
49
50 /* ---------------------------------------------------------
51 * Global interface variables and declarations
52 * ---------------------------------------------------------
53 */
54
55 #ifndef usb_lld_connect_bus
56 # define usb_lld_connect_bus(usbp)
57 #endif
58
59 #ifndef usb_lld_disconnect_bus
60 # define usb_lld_disconnect_bus(usbp)
61 #endif
62
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);
69
70 report_keyboard_t keyboard_report_sent = {{0}};
71 #ifdef MOUSE_ENABLE
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 */
77
78 /* ---------------------------------------------------------
79 * Descriptors and USB driver objects
80 * ---------------------------------------------------------
81 */
82
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
90
91 /*
92 * Handles the GET_DESCRIPTOR callback
93 *
94 * Returns the proper descriptor
95 */
96 static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) {
97 (void)usbp;
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)
103 return NULL;
104 else
105 return &desc;
106 }
107
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) */
123 };
124 #endif
125
126 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
127 /* mouse endpoint state structure */
128 static USBInEndpointState mouse_ep_state;
129
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) */
142 };
143 #endif
144
145 #ifdef SHARED_EP_ENABLE
146 /* shared endpoint state structure */
147 static USBInEndpointState shared_ep_state;
148
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) */
161 };
162 #endif
163
164 typedef struct {
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;
174 QMKUSBDriver driver;
175 } usb_driver_config_t;
176
177 /* Reusable initialization structure - see USBEndpointConfig comment at top of file */
178 #define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \
179 { \
180 .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \
181 .in_ep_config = \
182 { \
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) */ \
193 }, \
194 .out_ep_config = \
195 { \
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) */ \
206 }, \
207 .int_ep_config = \
208 { \
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) */ \
219 }, \
220 .config = { \
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)]){}, \
232 } \
233 }
234
235 typedef struct {
236 union {
237 struct {
238 #ifdef CONSOLE_ENABLE
239 usb_driver_config_t console_driver;
240 #endif
241 #ifdef RAW_ENABLE
242 usb_driver_config_t raw_driver;
243 #endif
244 #ifdef MIDI_ENABLE
245 usb_driver_config_t midi_driver;
246 #endif
247 #ifdef VIRTSER_ENABLE
248 usb_driver_config_t serial_driver;
249 #endif
250 };
251 usb_driver_config_t array[0];
252 };
253 } usb_driver_configs_t;
254
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),
262 #endif
263 #ifdef RAW_ENABLE
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),
269 #endif
270
271 #ifdef MIDI_ENABLE
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),
277 #endif
278
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),
285 #endif
286 };
287
288 #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
289
290 /* ---------------------------------------------------------
291 * USB driver functions
292 * ---------------------------------------------------------
293 */
294
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) {
298 switch (event) {
299 case USB_EVENT_ADDRESS:
300 return;
301
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);
307 #endif
308 #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
309 usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config);
310 #endif
311 #ifdef SHARED_EP_ENABLE
312 usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config);
313 #endif
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);
319 }
320 qmkusbConfigureHookI(&drivers.array[i].driver);
321 }
322 osalSysUnlockFromISR();
323 return;
324 case USB_EVENT_SUSPEND:
325 #ifdef SLEEP_LED_ENABLE
326 sleep_led_enable();
327 #endif /* SLEEP_LED_ENABLE */
328 /* Falls into.*/
329 case USB_EVENT_UNCONFIGURED:
330 /* Falls into.*/
331 case USB_EVENT_RESET:
332 for (int i = 0; i < NUM_USB_DRIVERS; i++) {
333 chSysLockFromISR();
334 /* Disconnection event on suspend.*/
335 qmkusbSuspendHookI(&drivers.array[i].driver);
336 chSysUnlockFromISR();
337 }
338 return;
339
340 case USB_EVENT_WAKEUP:
341 // TODO: from ISR! print("[W]");
342 for (int i = 0; i < NUM_USB_DRIVERS; i++) {
343 chSysLockFromISR();
344 /* Disconnection event on suspend.*/
345 qmkusbWakeupHookI(&drivers.array[i].driver);
346 chSysUnlockFromISR();
347 }
348 suspend_wakeup_init();
349 #ifdef SLEEP_LED_ENABLE
350 sleep_led_disable();
351 // NOTE: converters may not accept this
352 led_set(host_keyboard_leds());
353 #endif /* SLEEP_LED_ENABLE */
354 return;
355
356 case USB_EVENT_STALLED:
357 return;
358 }
359 }
360
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) {
364 uint16_t hw;
365
366 hw = (uint16_t)*p++;
367 hw |= (uint16_t)*p << 8U;
368 return hw;
369 }
370
371 /*
372 * Appendix G: HID Request Support Requirements
373 *
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
382 */
383
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];
390 }
391 } else {
392 keyboard_led_stats = set_report_buf[0];
393 }
394 }
395
396 /* Callback for SETUP request on the endpoint 0 (control) */
397 static bool usb_request_hook_cb(USBDriver *usbp) {
398 const USBDescriptor *dp;
399
400 /* usbp->setup fields:
401 * 0: bmRequestType (bitmask)
402 * 1: bRequest
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) */
406
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 */
412 case HID_GET_REPORT:
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);
416 return TRUE;
417 break;
418
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);
422 return TRUE;
423 break;
424 #endif
425
426 default:
427 usbSetupTransfer(usbp, NULL, 0, NULL);
428 return TRUE;
429 break;
430 }
431 break;
432
433 case HID_GET_PROTOCOL:
434 if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */
435 usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL);
436 return TRUE;
437 }
438 break;
439
440 case HID_GET_IDLE:
441 usbSetupTransfer(usbp, &keyboard_idle, 1, NULL);
442 return TRUE;
443 break;
444 }
445 break;
446
447 case USB_RTYPE_DIR_HOST2DEV:
448 switch (usbp->setup[1]) { /* bRequest */
449 case HID_SET_REPORT:
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:
454 #endif
455 usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb);
456 return TRUE;
457 break;
458 }
459 break;
460
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) */
464 #ifdef NKRO_ENABLE
465 keymap_config.nkro = !!keyboard_protocol;
466 if (!keymap_config.nkro && keyboard_idle) {
467 #else /* NKRO_ENABLE */
468 if (keyboard_idle) {
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();
474 }
475 }
476 usbSetupTransfer(usbp, NULL, 0, NULL);
477 return TRUE;
478 break;
479
480 case HID_SET_IDLE:
481 keyboard_idle = usbp->setup[3]; /* MSB(wValue) */
482 /* arm the timer */
483 #ifdef NKRO_ENABLE
484 if (!keymap_config.nkro && keyboard_idle) {
485 #else /* NKRO_ENABLE */
486 if (keyboard_idle) {
487 #endif /* NKRO_ENABLE */
488 osalSysLockFromISR();
489 chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
490 osalSysUnlockFromISR();
491 }
492 usbSetupTransfer(usbp, NULL, 0, NULL);
493 return TRUE;
494 break;
495 }
496 break;
497 }
498 }
499
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);
505 return TRUE;
506 }
507
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);
512 }
513 }
514
515 return FALSE;
516 }
517
518 /* Start-of-frame callback */
519 static void usb_sof_cb(USBDriver *usbp) {
520 kbd_sof_cb(usbp);
521 osalSysLockFromISR();
522 for (int i = 0; i < NUM_USB_DRIVERS; i++) {
523 qmkusbSOFHookI(&drivers.array[i].driver);
524 }
525 osalSysUnlockFromISR();
526 }
527
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 */
534 };
535
536 /*
537 * Initialize the USB driver
538 */
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);
547 }
548
549 /*
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
552 * after a reset.
553 */
554 usbDisconnectBus(usbp);
555 wait_ms(1500);
556 usbStart(usbp, &usbcfg);
557 usbConnectBus(usbp);
558
559 chVTObjectInit(&keyboard_idle_timer);
560 }
561
562 /* ---------------------------------------------------------
563 * Keyboard functions
564 * ---------------------------------------------------------
565 */
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) {
569 /* STUB */
570 (void)usbp;
571 (void)ep;
572 }
573 #endif
574
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; }
579
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;
584
585 osalSysLockFromISR();
586
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();
591 return;
592 }
593
594 #ifdef NKRO_ENABLE
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);
602 }
603 /* rearm the timer */
604 chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
605 }
606
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();
610 }
611
612 /* LED status */
613 uint8_t keyboard_leds(void) { return keyboard_led_stats; }
614
615 /* prepare and start sending a report IN
616 * not callable from ISR or locked state */
617 void send_keyboard(report_keyboard_t *report) {
618 osalSysLock();
619 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
620 goto unlock;
621 }
622
623 #ifdef NKRO_ENABLE
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);
636
637 /* after osalThreadSuspendS returns USB status might have changed */
638 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
639 goto unlock;
640 }
641 }
642 usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report));
643 } else
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);
654
655 /* after osalThreadSuspendS returns USB status might have changed */
656 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
657 goto unlock;
658 }
659 }
660 uint8_t *data, size;
661 if (keyboard_protocol) {
662 data = (uint8_t *)report;
663 size = KEYBOARD_REPORT_SIZE;
664 } else { /* boot protocol */
665 data = &report->mods;
666 size = 8;
667 }
668 usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, data, size);
669 }
670 keyboard_report_sent = *report;
671
672 unlock:
673 osalSysUnlock();
674 }
675
676 /* ---------------------------------------------------------
677 * Mouse functions
678 * ---------------------------------------------------------
679 */
680
681 #ifdef MOUSE_ENABLE
682
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) {
686 (void)usbp;
687 (void)ep;
688 }
689 # endif
690
691 void send_mouse(report_mouse_t *report) {
692 osalSysLock();
693 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
694 osalSysUnlock();
695 return;
696 }
697
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) {
704 osalSysUnlock();
705 return;
706 }
707 }
708 usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t));
709 osalSysUnlock();
710 }
711
712 #else /* MOUSE_ENABLE */
713 void send_mouse(report_mouse_t *report) { (void)report; }
714 #endif /* MOUSE_ENABLE */
715
716 /* ---------------------------------------------------------
717 * Shared EP functions
718 * ---------------------------------------------------------
719 */
720 #ifdef SHARED_EP_ENABLE
721 /* shared IN callback hander */
722 void shared_in_cb(USBDriver *usbp, usbep_t ep) {
723 /* STUB */
724 (void)usbp;
725 (void)ep;
726 }
727 #endif
728
729 /* ---------------------------------------------------------
730 * Extrakey functions
731 * ---------------------------------------------------------
732 */
733
734 #ifdef EXTRAKEY_ENABLE
735 static void send_extra(uint8_t report_id, uint16_t data) {
736 osalSysLock();
737 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
738 osalSysUnlock();
739 return;
740 }
741
742 report_extra_t report = {.report_id = report_id, .usage = data};
743
744 usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t));
745 osalSysUnlock();
746 }
747 #endif
748
749 void send_system(uint16_t data) {
750 #ifdef EXTRAKEY_ENABLE
751 send_extra(REPORT_ID_SYSTEM, data);
752 #endif
753 }
754
755 void send_consumer(uint16_t data) {
756 #ifdef EXTRAKEY_ENABLE
757 send_extra(REPORT_ID_CONSUMER, data);
758 #endif
759 }
760
761 /* ---------------------------------------------------------
762 * Console functions
763 * ---------------------------------------------------------
764 */
765
766 #ifdef CONSOLE_ENABLE
767
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);
772 }
773
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) {
777 (void)data;
778 (void)length;
779 }
780
781 void console_task(void) {
782 uint8_t buffer[CONSOLE_EPSIZE];
783 size_t size = 0;
784 do {
785 size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
786 if (size > 0) {
787 console_receive(buffer, size);
788 }
789 } while (size > 0);
790 }
791
792 #else /* CONSOLE_ENABLE */
793 int8_t sendchar(uint8_t c) {
794 (void)c;
795 return 0;
796 }
797 #endif /* CONSOLE_ENABLE */
798
799 void sendchar_pf(void *p, char c) {
800 (void)p;
801 sendchar((uint8_t)c);
802 }
803
804 #ifdef RAW_ENABLE
805 void raw_hid_send(uint8_t *data, uint8_t length) {
806 // TODO: implement variable size packet
807 if (length != RAW_EPSIZE) {
808 return;
809 }
810 chnWrite(&drivers.raw_driver.driver, data, length);
811 }
812
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.
817 }
818
819 void raw_hid_task(void) {
820 uint8_t buffer[RAW_EPSIZE];
821 size_t size = 0;
822 do {
823 size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
824 if (size > 0) {
825 raw_hid_receive(buffer, size);
826 }
827 } while (size > 0);
828 }
829
830 #endif
831
832 #ifdef MIDI_ENABLE
833
834 void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); }
835
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);
839 }
840 void midi_ep_task(void) {
841 uint8_t buffer[MIDI_STREAM_EPSIZE];
842 size_t size = 0;
843 do {
844 size_t size = chnReadTimeout(&drivers.midi_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
845 if (size > 0) {
846 MIDI_EventPacket_t event;
847 recv_midi_packet(&event);
848 }
849 } while (size > 0);
850 }
851 #endif
852
853 #ifdef VIRTSER_ENABLE
854
855 void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); }
856
857 __attribute__((weak)) void virtser_recv(uint8_t c) {
858 // Ignore by default
859 }
860
861 void virtser_task(void) {
862 uint8_t numBytesReceived = 0;
863 uint8_t buffer[16];
864 do {
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]);
868 }
869 } while (numBytesReceived > 0);
870 }
871
872 #endif