diff options
Diffstat (limited to 'tmk_core/protocol/chibios/usb_main.c')
-rw-r--r-- | tmk_core/protocol/chibios/usb_main.c | 117 |
1 files changed, 96 insertions, 21 deletions
diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 67ae8520fd..990dc1b91c 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -27,6 +27,7 @@ #include <ch.h> #include <hal.h> +#include <string.h> #include "usb_main.h" @@ -50,6 +51,7 @@ extern keymap_config_t keymap_config; #ifdef WEBUSB_ENABLE # include "webusb.h" #endif + #ifdef JOYSTICK_ENABLE # include "joystick.h" #endif @@ -168,6 +170,7 @@ static const USBEndpointConfig shared_ep_config = { }; #endif + #ifdef WEBUSB_ENABLE /** Microsoft OS 2.0 Descriptor. This is used by Windows to select the USB driver for the device. * @@ -397,6 +400,69 @@ static usb_driver_configs_t drivers = { * --------------------------------------------------------- */ +#define USB_EVENT_QUEUE_SIZE 16 +usbevent_t event_queue[USB_EVENT_QUEUE_SIZE]; +uint8_t event_queue_head; +uint8_t event_queue_tail; + +void usb_event_queue_init(void) { + // Initialise the event queue + memset(&event_queue, 0, sizeof(event_queue)); + event_queue_head = 0; + event_queue_tail = 0; +} + +static inline bool usb_event_queue_enqueue(usbevent_t event) { + uint8_t next = (event_queue_head + 1) % USB_EVENT_QUEUE_SIZE; + if (next == event_queue_tail) { + return false; + } + event_queue[event_queue_head] = event; + event_queue_head = next; + return true; +} + +static inline bool usb_event_queue_dequeue(usbevent_t *event) { + if (event_queue_head == event_queue_tail) { + return false; + } + *event = event_queue[event_queue_tail]; + event_queue_tail = (event_queue_tail + 1) % USB_EVENT_QUEUE_SIZE; + return true; +} + +static inline void usb_event_suspend_handler(void) { +#ifdef SLEEP_LED_ENABLE + sleep_led_enable(); +#endif /* SLEEP_LED_ENABLE */ +} + +static inline void usb_event_wakeup_handler(void) { + suspend_wakeup_init(); +#ifdef SLEEP_LED_ENABLE + sleep_led_disable(); + // NOTE: converters may not accept this + led_set(host_keyboard_leds()); +#endif /* SLEEP_LED_ENABLE */ +} + +void usb_event_queue_task(void) { + usbevent_t event; + while (usb_event_queue_dequeue(&event)) { + switch (event) { + case USB_EVENT_SUSPEND: + usb_event_suspend_handler(); + break; + case USB_EVENT_WAKEUP: + usb_event_wakeup_handler(); + break; + default: + // Nothing to do, we don't handle it. + break; + } + } +} + /* Handles the USB driver global events * TODO: maybe disable some things when connection is lost? */ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { @@ -431,9 +497,7 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { osalSysUnlockFromISR(); return; case USB_EVENT_SUSPEND: -#ifdef SLEEP_LED_ENABLE - sleep_led_enable(); -#endif /* SLEEP_LED_ENABLE */ + usb_event_queue_enqueue(USB_EVENT_SUSPEND); /* Falls into.*/ case USB_EVENT_UNCONFIGURED: /* Falls into.*/ @@ -454,12 +518,7 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { qmkusbWakeupHookI(&drivers.array[i].driver); chSysUnlockFromISR(); } - suspend_wakeup_init(); -#ifdef SLEEP_LED_ENABLE - sleep_led_disable(); - // NOTE: converters may not accept this - led_set(host_keyboard_leds()); -#endif /* SLEEP_LED_ENABLE */ + usb_event_queue_enqueue(USB_EVENT_WAKEUP); return; case USB_EVENT_STALLED: @@ -575,7 +634,7 @@ static bool usb_request_hook_cb(USBDriver *usbp) { if (!keymap_config.nkro && keyboard_idle) { #else /* NKRO_ENABLE */ if (keyboard_idle) { -#endif /* NKRO_ENABLE */ +#endif /* NKRO_ENABLE */ /* arm the idle timer if boot protocol & idle */ osalSysLockFromISR(); chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); @@ -867,7 +926,7 @@ void send_mouse(report_mouse_t *report) { } #else /* MOUSE_ENABLE */ -void send_mouse(report_mouse_t *report) { (void)report; } +void send_mouse(report_mouse_t *report) { (void)report; } #endif /* MOUSE_ENABLE */ /* --------------------------------------------------------- @@ -923,9 +982,32 @@ void send_consumer(uint16_t data) { #ifdef CONSOLE_ENABLE int8_t sendchar(uint8_t c) { - // The previous implmentation had timeouts, but I think it's better to just slow down - // and make sure that everything is transferred, rather than dropping stuff - return chnWrite(&drivers.console_driver.driver, &c, 1); + static bool timed_out = false; + /* The `timed_out` state is an approximation of the ideal `is_listener_disconnected?` state. + * + * When a 5ms timeout write has timed out, hid_listen is most likely not running, or not + * listening to this keyboard, so we go into the timed_out state. In this state we assume + * that hid_listen is most likely not gonna be connected to us any time soon, so it would + * be wasteful to write follow-up characters with a 5ms timeout, it would all add up and + * unncecessarily slow down the firmware. However instead of just dropping the characters, + * we write them with a TIME_IMMEDIATE timeout, which is a zero timeout, + * and this will succeed only if hid_listen gets connected again. When a write with + * TIME_IMMEDIATE timeout succeeds, we know that hid_listen is listening to us again, and + * we can go back to the timed_out = false state, and following writes will be executed + * with a 5ms timeout. The reason we don't just send all characters with the TIME_IMMEDIATE + * timeout is that this could cause bytes to be lost even if hid_listen is running, if there + * is a lot of data being sent over the console. + * + * This logic will work correctly as long as hid_listen is able to receive at least 200 + * bytes per second. On a heavily overloaded machine that's so overloaded that it's + * unusable, and constantly swapping, hid_listen might have trouble receiving 200 bytes per + * second, so some bytes might be lost on the console. + */ + + const sysinterval_t timeout = timed_out ? TIME_IMMEDIATE : TIME_MS2I(5); + const size_t result = chnWriteTimeout(&drivers.console_driver.driver, &c, 1, timeout); + timed_out = (result == 0); + return result; } // Just a dummy function for now, this could be exposed as a weak function @@ -946,15 +1028,8 @@ void console_task(void) { } while (size > 0); } -#else /* CONSOLE_ENABLE */ -int8_t sendchar(uint8_t c) { - (void)c; - return 0; -} #endif /* CONSOLE_ENABLE */ -void _putchar(char character) { sendchar(character); } - #ifdef RAW_ENABLE void raw_hid_send(uint8_t *data, uint8_t length) { // TODO: implement variable size packet |