2020 February 29 Breaking Changes Update (#8064)
[jackhill/qmk/firmware.git] / tmk_core / protocol / chibios / usb_main.c
CommitLineData
4d4f7684 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
80c2e267
JC
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
4d4f7684 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
b624f32f 37# include "sleep_led.h"
38# include "led.h"
4d4f7684 39#endif
5fd68266 40#include "wait.h"
53ff8a31 41#include "usb_descriptor.h"
e9d32b60 42#include "usb_driver.h"
4d4f7684 43
558f3ec1 44#ifdef NKRO_ENABLE
b624f32f 45# include "keycode_config.h"
558f3ec1 46
b624f32f 47extern keymap_config_t keymap_config;
558f3ec1
I
48#endif
49
4d4f7684 50/* ---------------------------------------------------------
51 * Global interface variables and declarations
52 * ---------------------------------------------------------
53 */
54
5fd68266 55#ifndef usb_lld_connect_bus
b624f32f 56# define usb_lld_connect_bus(usbp)
5fd68266 57#endif
58
59#ifndef usb_lld_disconnect_bus
b624f32f 60# define usb_lld_disconnect_bus(usbp)
5fd68266 61#endif
62
dee1d68d
QB
63uint8_t keyboard_idle __attribute__((aligned(2))) = 0;
64uint8_t keyboard_protocol __attribute__((aligned(2))) = 1;
65uint8_t keyboard_led_stats = 0;
66volatile uint16_t keyboard_idle_count = 0;
4d4f7684 67static virtual_timer_t keyboard_idle_timer;
b624f32f 68static void keyboard_idle_timer_cb(void *arg);
4d4f7684 69
70report_keyboard_t keyboard_report_sent = {{0}};
71#ifdef MOUSE_ENABLE
72report_mouse_t mouse_report_blank = {0};
73#endif /* MOUSE_ENABLE */
74#ifdef EXTRAKEY_ENABLE
75uint8_t extra_report_blank[3] = {0};
76#endif /* EXTRAKEY_ENABLE */
77
4d4f7684 78/* ---------------------------------------------------------
79 * Descriptors and USB driver objects
80 * ---------------------------------------------------------
81 */
82
83/* HID specific constants */
4d4f7684 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
4d4f7684 91/*
92 * Handles the GET_DESCRIPTOR callback
93 *
94 * Returns the proper descriptor
95 */
53ff8a31 96static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) {
b624f32f 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;
4d4f7684 106}
107
39bd760f 108#ifndef KEYBOARD_SHARED_EP
4d4f7684 109/* keyboard endpoint state structure */
110static USBInEndpointState kbd_ep_state;
80c2e267 111/* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
4d4f7684 112static const USBEndpointConfig kbd_ep_config = {
b624f32f 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) */
4d4f7684 123};
39bd760f 124#endif
4d4f7684 125
39bd760f 126#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
4d4f7684 127/* mouse endpoint state structure */
128static USBInEndpointState mouse_ep_state;
129
80c2e267 130/* mouse endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
4d4f7684 131static const USBEndpointConfig mouse_ep_config = {
b624f32f 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) */
4d4f7684 142};
39bd760f 143#endif
4d4f7684 144
39bd760f
JLW
145#ifdef SHARED_EP_ENABLE
146/* shared endpoint state structure */
147static USBInEndpointState shared_ep_state;
4d4f7684 148
80c2e267 149/* shared endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
39bd760f 150static const USBEndpointConfig shared_ep_config = {
b624f32f 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) */
4d4f7684 161};
39bd760f 162#endif
4d4f7684 163
53ff8a31 164typedef struct {
b624f32f 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;
e9d32b60 175} usb_driver_config_t;
53ff8a31 176
80c2e267 177/* Reusable initialization structure - see USBEndpointConfig comment at top of file */
09370a95
QB
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, \
e9ffc534
GH
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)]){}, \
09370a95 232 } \
b624f32f 233 }
53ff8a31 234
235typedef struct {
b624f32f 236 union {
237 struct {
53ff8a31 238#ifdef CONSOLE_ENABLE
b624f32f 239 usb_driver_config_t console_driver;
53ff8a31 240#endif
241#ifdef RAW_ENABLE
b624f32f 242 usb_driver_config_t raw_driver;
53ff8a31 243#endif
244#ifdef MIDI_ENABLE
b624f32f 245 usb_driver_config_t midi_driver;
53ff8a31 246#endif
247#ifdef VIRTSER_ENABLE
b624f32f 248 usb_driver_config_t serial_driver;
53ff8a31 249#endif
b624f32f 250 };
251 usb_driver_config_t array[0];
53ff8a31 252 };
e9d32b60 253} usb_driver_configs_t;
53ff8a31 254
e9d32b60 255static usb_driver_configs_t drivers = {
53ff8a31 256#ifdef CONSOLE_ENABLE
b624f32f 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),
53ff8a31 262#endif
263#ifdef RAW_ENABLE
b624f32f 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),
53ff8a31 269#endif
270
271#ifdef MIDI_ENABLE
b624f32f 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),
53ff8a31 277#endif
278
279#ifdef VIRTSER_ENABLE
b624f32f 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),
53ff8a31 285#endif
286};
287
e9d32b60 288#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
53ff8a31 289
4d4f7684 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? */
297static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
b624f32f 298 switch (event) {
299 case USB_EVENT_ADDRESS:
300 return;
4d4f7684 301
b624f32f 302 case USB_EVENT_CONFIGURED:
303 osalSysLockFromISR();
304 /* Enable the endpoints specified into the configuration. */
39bd760f 305#ifndef KEYBOARD_SHARED_EP
b624f32f 306 usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config);
39bd760f
JLW
307#endif
308#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
b624f32f 309 usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config);
39bd760f
JLW
310#endif
311#ifdef SHARED_EP_ENABLE
b624f32f 312 usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config);
39bd760f 313#endif
b624f32f 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:
4d4f7684 325#ifdef SLEEP_LED_ENABLE
b624f32f 326 sleep_led_enable();
a91c0c47 327#endif /* SLEEP_LED_ENABLE */
b624f32f 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();
4d4f7684 349#ifdef SLEEP_LED_ENABLE
b624f32f 350 sleep_led_disable();
351 // NOTE: converters may not accept this
352 led_set(host_keyboard_leds());
4d4f7684 353#endif /* SLEEP_LED_ENABLE */
b624f32f 354 return;
4d4f7684 355
b624f32f 356 case USB_EVENT_STALLED:
357 return;
358 }
4d4f7684 359}
360
361/* Function used locally in os/hal/src/usb.c for getting descriptors
362 * need it here for HID descriptor */
363static uint16_t get_hword(uint8_t *p) {
b624f32f 364 uint16_t hw;
4d4f7684 365
b624f32f 366 hw = (uint16_t)*p++;
367 hw |= (uint16_t)*p << 8U;
368 return hw;
4d4f7684 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
39bd760f 384static uint8_t set_report_buf[2] __attribute__((aligned(2)));
b624f32f 385static void set_led_transfer_cb(USBDriver *usbp) {
123ae73e 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];
b624f32f 393 }
39bd760f 394}
39bd760f 395
4d4f7684 396/* Callback for SETUP request on the endpoint 0 (control) */
397static bool usb_request_hook_cb(USBDriver *usbp) {
b624f32f 398 const USBDescriptor *dp;
b624f32f 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;
4d4f7684 418
39bd760f 419#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
b624f32f 420 case MOUSE_INTERFACE:
421 usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL);
422 return TRUE;
423 break;
39bd760f 424#endif
4d4f7684 425
b624f32f 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:
123ae73e 450 switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */
451 case KEYBOARD_INTERFACE:
39bd760f 452#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
b624f32f 453 case SHARED_INTERFACE:
39bd760f 454#endif
123ae73e 455 usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb);
b624f32f 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) */
4d4f7684 464#ifdef NKRO_ENABLE
b624f32f 465 keymap_config.nkro = !!keyboard_protocol;
466 if (!keymap_config.nkro && keyboard_idle) {
a91c0c47 467#else /* NKRO_ENABLE */
b624f32f 468 if (keyboard_idle) {
a91c0c47 469#endif /* NKRO_ENABLE */
b624f32f 470 /* arm the idle timer if boot protocol & idle */
471 osalSysLockFromISR();
26eef35f 472 chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
b624f32f 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 */
4d4f7684 483#ifdef NKRO_ENABLE
b624f32f 484 if (!keymap_config.nkro && keyboard_idle) {
485#else /* NKRO_ENABLE */
486 if (keyboard_idle) {
4d4f7684 487#endif /* NKRO_ENABLE */
b624f32f 488 osalSysLockFromISR();
26eef35f 489 chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
b624f32f 490 osalSysUnlockFromISR();
491 }
492 usbSetupTransfer(usbp, NULL, 0, NULL);
493 return TRUE;
494 break;
495 }
496 break;
4d4f7684 497 }
b624f32f 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);
4d4f7684 505 return TRUE;
4d4f7684 506 }
b624f32f 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 }
53ff8a31 513 }
53ff8a31 514
b624f32f 515 return FALSE;
4d4f7684 516}
517
518/* Start-of-frame callback */
519static void usb_sof_cb(USBDriver *usbp) {
b624f32f 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();
4d4f7684 526}
527
4d4f7684 528/* USB driver configuration */
529static const USBConfig usbcfg = {
b624f32f 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 */
4d4f7684 534};
535
536/*
537 * Initialize the USB driver
538 */
539void init_usb_driver(USBDriver *usbp) {
b624f32f 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);
4d4f7684 560}
561
4d4f7684 562/* ---------------------------------------------------------
563 * Keyboard functions
564 * ---------------------------------------------------------
565 */
4d4f7684 566/* keyboard IN callback hander (a kbd report has made it IN) */
39bd760f 567#ifndef KEYBOARD_SHARED_EP
4d4f7684 568void kbd_in_cb(USBDriver *usbp, usbep_t ep) {
b624f32f 569 /* STUB */
570 (void)usbp;
571 (void)ep;
4d4f7684 572}
39bd760f 573#endif
4d4f7684 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 */
b624f32f 578void kbd_sof_cb(USBDriver *usbp) { (void)usbp; }
4d4f7684 579
580/* Idle requests timer code
581 * callback (called from ISR, unlocked state) */
582static void keyboard_idle_timer_cb(void *arg) {
b624f32f 583 USBDriver *usbp = (USBDriver *)arg;
4d4f7684 584
b624f32f 585 osalSysLockFromISR();
4d4f7684 586
b624f32f 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 }
4d4f7684 593
594#ifdef NKRO_ENABLE
b624f32f 595 if (!keymap_config.nkro && keyboard_idle && keyboard_protocol) {
596#else /* NKRO_ENABLE */
597 if (keyboard_idle && keyboard_protocol) {
4d4f7684 598#endif /* NKRO_ENABLE */
b624f32f 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 */
26eef35f 604 chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
4d4f7684 605 }
4d4f7684 606
b624f32f 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();
4d4f7684 610}
611
612/* LED status */
123ae73e 613uint8_t keyboard_leds(void) { return keyboard_led_stats; }
4d4f7684 614
615/* prepare and start sending a report IN
616 * not callable from ISR or locked state */
617void send_keyboard(report_keyboard_t *report) {
4d4f7684 618 osalSysLock();
b624f32f 619 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
83be1aed 620 goto unlock;
4d4f7684 621 }
b624f32f 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 */
b624f32f 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);
83be1aed 636
637 /* after osalThreadSuspendS returns USB status might have changed */
638 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
639 goto unlock;
640 }
b624f32f 641 }
642 usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report));
b624f32f 643 } else
4d4f7684 644#endif /* NKRO_ENABLE */
b624f32f 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 */
b624f32f 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);
83be1aed 654
655 /* after osalThreadSuspendS returns USB status might have changed */
656 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
657 goto unlock;
658 }
b624f32f 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);
39bd760f 669 }
b624f32f 670 keyboard_report_sent = *report;
83be1aed 671
672unlock:
673 osalSysUnlock();
4d4f7684 674}
675
676/* ---------------------------------------------------------
677 * Mouse functions
678 * ---------------------------------------------------------
679 */
680
681#ifdef MOUSE_ENABLE
682
b624f32f 683# ifndef MOUSE_SHARED_EP
4d4f7684 684/* mouse IN callback hander (a mouse report has made it IN) */
685void mouse_in_cb(USBDriver *usbp, usbep_t ep) {
b624f32f 686 (void)usbp;
687 (void)ep;
4d4f7684 688}
b624f32f 689# endif
4d4f7684 690
691void send_mouse(report_mouse_t *report) {
b624f32f 692 osalSysLock();
693 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
694 osalSysUnlock();
695 return;
b1bf0879 696 }
4d4f7684 697
b624f32f 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 */
26eef35f 703 if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[MOUSE_IN_EPNUM]->in_state->thread, TIME_MS2I(10)) == MSG_TIMEOUT) {
b624f32f 704 osalSysUnlock();
705 return;
706 }
707 }
708 usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t));
709 osalSysUnlock();
4d4f7684 710}
b624f32f 711
712#else /* MOUSE_ENABLE */
713void send_mouse(report_mouse_t *report) { (void)report; }
4d4f7684 714#endif /* MOUSE_ENABLE */
715
716/* ---------------------------------------------------------
39bd760f 717 * Shared EP functions
4d4f7684 718 * ---------------------------------------------------------
719 */
39bd760f
JLW
720#ifdef SHARED_EP_ENABLE
721/* shared IN callback hander */
722void shared_in_cb(USBDriver *usbp, usbep_t ep) {
b624f32f 723 /* STUB */
724 (void)usbp;
725 (void)ep;
4d4f7684 726}
39bd760f
JLW
727#endif
728
729/* ---------------------------------------------------------
730 * Extrakey functions
731 * ---------------------------------------------------------
732 */
4d4f7684 733
39bd760f 734#ifdef EXTRAKEY_ENABLE
088b64ab 735static void send_extra(uint8_t report_id, uint16_t data) {
b624f32f 736 osalSysLock();
737 if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
738 osalSysUnlock();
739 return;
740 }
4d4f7684 741
b624f32f 742 report_extra_t report = {.report_id = report_id, .usage = data};
4d4f7684 743
b624f32f 744 usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t));
745 osalSysUnlock();
4d4f7684 746}
088b64ab 747#endif
4d4f7684 748
088b64ab
R
749void send_system(uint16_t data) {
750#ifdef EXTRAKEY_ENABLE
751 send_extra(REPORT_ID_SYSTEM, data);
752#endif
753}
4d4f7684 754
088b64ab
R
755void send_consumer(uint16_t data) {
756#ifdef EXTRAKEY_ENABLE
757 send_extra(REPORT_ID_CONSUMER, data);
758#endif
759}
4d4f7684 760
761/* ---------------------------------------------------------
762 * Console functions
763 * ---------------------------------------------------------
764 */
765
766#ifdef CONSOLE_ENABLE
767
53ff8a31 768int8_t sendchar(uint8_t c) {
b624f32f 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);
53ff8a31 772}
4d4f7684 773
53ff8a31 774// Just a dummy function for now, this could be exposed as a weak function
775// Or connected to the actual QMK console
b624f32f 776static void console_receive(uint8_t *data, uint8_t length) {
777 (void)data;
778 (void)length;
53ff8a31 779}
4d4f7684 780
53ff8a31 781void console_task(void) {
b624f32f 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);
53ff8a31 790}
4d4f7684 791
b624f32f 792#else /* CONSOLE_ENABLE */
53ff8a31 793int8_t sendchar(uint8_t c) {
b624f32f 794 (void)c;
795 return 0;
53ff8a31 796}
797#endif /* CONSOLE_ENABLE */
4d4f7684 798
53ff8a31 799void sendchar_pf(void *p, char c) {
b624f32f 800 (void)p;
801 sendchar((uint8_t)c);
53ff8a31 802}
4d4f7684 803
53ff8a31 804#ifdef RAW_ENABLE
b624f32f 805void 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);
4d4f7684 811}
812
b624f32f 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.
53ff8a31 817}
4d4f7684 818
53ff8a31 819void raw_hid_task(void) {
b624f32f 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);
4d4f7684 828}
829
53ff8a31 830#endif
4d4f7684 831
53ff8a31 832#ifdef MIDI_ENABLE
4d4f7684 833
b624f32f 834void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); }
4d4f7684 835
b624f32f 836bool 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);
53ff8a31 839}
0010d0c4
DJ
840void 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}
53ff8a31 851#endif
4d4f7684 852
53ff8a31 853#ifdef VIRTSER_ENABLE
4d4f7684 854
b624f32f 855void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); }
4d4f7684 856
b624f32f 857__attribute__((weak)) void virtser_recv(uint8_t c) {
858 // Ignore by default
4d4f7684 859}
860
53ff8a31 861void virtser_task(void) {
b624f32f 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);
4d4f7684 870}
4d4f7684 871
53ff8a31 872#endif