From 3132971dc8f6ea54f5d9900384fb58324dd36ff2 Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Thu, 23 Apr 2020 22:57:37 +0800 Subject: [PATCH 1/9] add support for otg --- tmk_core/protocol/chibios/usb_main.c | 74 ++ tmk_core/protocol/chibios/usb_main.new.c | 934 +++++++++++++++++++++++ tmk_core/protocol/usb_descriptor.h | 17 + 3 files changed, 1025 insertions(+) create mode 100644 tmk_core/protocol/chibios/usb_main.new.c diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index ecc83d9ecc94..b9068e510742 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -161,6 +161,19 @@ static const USBEndpointConfig shared_ep_config = { }; #endif +#if STM32_USB_USE_OTG1 +typedef struct { + size_t queue_capacity_in; + size_t queue_capacity_out; + USBInEndpointState in_ep_state; + USBOutEndpointState out_ep_state; + USBInEndpointState int_ep_state; + USBEndpointConfig inout_ep_config; + USBEndpointConfig int_ep_config; + const QMKUSBConfig config; + QMKUSBDriver driver; +} usb_driver_config_t; +#else typedef struct { size_t queue_capacity_in; size_t queue_capacity_out; @@ -173,7 +186,54 @@ typedef struct { const QMKUSBConfig config; QMKUSBDriver driver; } usb_driver_config_t; +#endif +#if STM32_USB_USE_OTG1 +/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ +#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ + { \ + .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ + .inout_ep_config = \ + { \ + stream##_IN_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbDataTransmitted, /* IN notification callback */ \ + qmkusbDataReceived, /* OUT notification callback */ \ + stream##_EPSIZE, /* IN maximum packet size */ \ + stream##_EPSIZE, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .int_ep_config = \ + { \ + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbInterruptTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .bulk_in = stream##_IN_EPNUM, \ + .bulk_out = stream##_OUT_EPNUM, \ + .int_in = notification, \ + .in_buffers = stream##_IN_CAPACITY, \ + .out_buffers = stream##_OUT_CAPACITY, \ + .in_size = stream##_EPSIZE, \ + .out_size = stream##_EPSIZE, \ + .fixed_size = fixedsize, \ + .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + } \ + } +#else /* Reusable initialization structure - see USBEndpointConfig comment at top of file */ #define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ { \ @@ -231,6 +291,7 @@ typedef struct { .ob = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ } \ } +#endif typedef struct { union { @@ -312,8 +373,12 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); #endif for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].inout_ep_config); + #else usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config); usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config); + #endif if (drivers.array[i].config.int_in) { usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config); } @@ -538,12 +603,21 @@ static const USBConfig usbcfg = { */ void init_usb_driver(USBDriver *usbp) { for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + QMKUSBDriver *driver = &drivers.array[i].driver; + drivers.array[i].inout_ep_config.in_state = &drivers.array[i].in_ep_state; + drivers.array[i].inout_ep_config.out_state = &drivers.array[i].out_ep_state; + drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; + qmkusbObjectInit(driver, &drivers.array[i].config); + qmkusbStart(driver, &drivers.array[i].config); + #else QMKUSBDriver *driver = &drivers.array[i].driver; drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state; drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state; drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; qmkusbObjectInit(driver, &drivers.array[i].config); qmkusbStart(driver, &drivers.array[i].config); + #endif } /* diff --git a/tmk_core/protocol/chibios/usb_main.new.c b/tmk_core/protocol/chibios/usb_main.new.c new file mode 100644 index 000000000000..fa08f54a1df7 --- /dev/null +++ b/tmk_core/protocol/chibios/usb_main.new.c @@ -0,0 +1,934 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + +/* + * Implementation notes: + * + * USBEndpointConfig - Configured using explicit order instead of struct member name. + * This is due to ChibiOS hal LLD differences, which is dependent on hardware, + * "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`. + * Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file + * makes the assumption this is safe to avoid littering with preprocessor directives. + */ + +#include "ch.h" +#include "hal.h" + +#include "usb_main.h" + +#include "host.h" +#include "debug.h" +#include "suspend.h" +#ifdef SLEEP_LED_ENABLE +# include "sleep_led.h" +# include "led.h" +#endif +#include "wait.h" +#include "usb_descriptor.h" +#include "usb_driver.h" + +#ifdef NKRO_ENABLE +# include "keycode_config.h" + +extern keymap_config_t keymap_config; +#endif + +/* --------------------------------------------------------- + * Global interface variables and declarations + * --------------------------------------------------------- + */ + +#ifndef usb_lld_connect_bus +# define usb_lld_connect_bus(usbp) +#endif + +#ifndef usb_lld_disconnect_bus +# define usb_lld_disconnect_bus(usbp) +#endif + +uint8_t keyboard_idle __attribute__((aligned(2))) = 0; +uint8_t keyboard_protocol __attribute__((aligned(2))) = 1; +uint16_t keyboard_led_stats __attribute__((aligned(2))) = 0; +volatile uint16_t keyboard_idle_count = 0; +static virtual_timer_t keyboard_idle_timer; +static void keyboard_idle_timer_cb(void *arg); + +report_keyboard_t keyboard_report_sent = {{0}}; +#ifdef MOUSE_ENABLE +report_mouse_t mouse_report_blank = {0}; +#endif /* MOUSE_ENABLE */ +#ifdef EXTRAKEY_ENABLE +uint8_t extra_report_blank[3] = {0}; +#endif /* EXTRAKEY_ENABLE */ + +/* --------------------------------------------------------- + * Descriptors and USB driver objects + * --------------------------------------------------------- + */ + +/* HID specific constants */ +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +/* + * Handles the GET_DESCRIPTOR callback + * + * Returns the proper descriptor + */ +static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) { + (void)usbp; + static USBDescriptor desc; + uint16_t wValue = ((uint16_t)dtype << 8) | dindex; + desc.ud_string = NULL; + desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const) & desc.ud_string); + if (desc.ud_string == NULL) + return NULL; + else + return &desc; +} + +#ifndef KEYBOARD_SHARED_EP +/* keyboard endpoint state structure */ +static USBInEndpointState kbd_ep_state; +/* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ +static const USBEndpointConfig kbd_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + kbd_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + KEYBOARD_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &kbd_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif + +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) +/* mouse endpoint state structure */ +static USBInEndpointState mouse_ep_state; + +/* mouse endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ +static const USBEndpointConfig mouse_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + mouse_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + MOUSE_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &mouse_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif + +#ifdef SHARED_EP_ENABLE +/* shared endpoint state structure */ +static USBInEndpointState shared_ep_state; + +/* shared endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ +static const USBEndpointConfig shared_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + shared_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + SHARED_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &shared_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif + +#if STM32_USB_USE_OTG1 +typedef struct { + size_t queue_capacity_in; + size_t queue_capacity_out; + USBInEndpointState in_ep_state; + USBOutEndpointState out_ep_state; + USBInEndpointState int_ep_state; + USBEndpointConfig inout_ep_config; + USBEndpointConfig int_ep_config; + const QMKUSBConfig config; + QMKUSBDriver driver; +} usb_driver_config_t; +#else +typedef struct { + size_t queue_capacity_in; + size_t queue_capacity_out; + USBInEndpointState in_ep_state; + USBOutEndpointState out_ep_state; + USBInEndpointState int_ep_state; + USBEndpointConfig in_ep_config; + USBEndpointConfig out_ep_config; + USBEndpointConfig int_ep_config; + const QMKUSBConfig config; + QMKUSBDriver driver; +} usb_driver_config_t; +#endif + +#if STM32_USB_USE_OTG1 +/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ +#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ + { \ + .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ + .inout_ep_config = \ + { \ + stream##_IN_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbDataTransmitted, /* IN notification callback */ \ + qmkusbDataReceived, /* OUT notification callback */ \ + stream##_EPSIZE, /* IN maximum packet size */ \ + stream##_EPSIZE, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .int_ep_config = \ + { \ + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbInterruptTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .bulk_in = stream##_IN_EPNUM, \ + .bulk_out = stream##_OUT_EPNUM, \ + .int_in = notification, \ + .in_buffers = stream##_IN_CAPACITY, \ + .out_buffers = stream##_OUT_CAPACITY, \ + .in_size = stream##_EPSIZE, \ + .out_size = stream##_EPSIZE, \ + .fixed_size = fixedsize, \ + .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + } \ + } +#else +/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ +#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ + { \ + .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ + .in_ep_config = \ + { \ + stream##_IN_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbDataTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + stream##_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .out_ep_config = \ + { \ + stream##_OUT_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + NULL, /* IN notification callback */ \ + qmkusbDataReceived, /* OUT notification callback */ \ + 0, /* IN maximum packet size */ \ + stream##_EPSIZE, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .int_ep_config = \ + { \ + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbInterruptTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .bulk_in = stream##_IN_EPNUM, \ + .bulk_out = stream##_OUT_EPNUM, \ + .int_in = notification, \ + .in_buffers = stream##_IN_CAPACITY, \ + .out_buffers = stream##_OUT_CAPACITY, \ + .in_size = stream##_EPSIZE, \ + .out_size = stream##_EPSIZE, \ + .fixed_size = fixedsize, \ + .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + } \ + } +#endif + +typedef struct { + union { + struct { +#ifdef CONSOLE_ENABLE + usb_driver_config_t console_driver; +#endif +#if defined(RAW_ENABLE) + usb_driver_config_t raw_driver; +#endif +#ifdef MIDI_ENABLE + usb_driver_config_t midi_driver; +#endif +#ifdef VIRTSER_ENABLE + usb_driver_config_t serial_driver; +#endif + }; + usb_driver_config_t array[0]; + }; +} usb_driver_configs_t; + +static usb_driver_configs_t drivers = { +#ifdef CONSOLE_ENABLE +# define CONSOLE_IN_CAPACITY 4 +# define CONSOLE_OUT_CAPACITY 4 +# define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR +# define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR + .console_driver = QMK_USB_DRIVER_CONFIG(CONSOLE, 0, true), +#endif +#if defined(RAW_ENABLE) +# define RAW_IN_CAPACITY 4 +# define RAW_OUT_CAPACITY 4 +# define RAW_IN_MODE USB_EP_MODE_TYPE_INTR +# define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR + .raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false), + +#endif + +#ifdef MIDI_ENABLE +# define MIDI_STREAM_IN_CAPACITY 4 +# define MIDI_STREAM_OUT_CAPACITY 4 +# define MIDI_STREAM_IN_MODE USB_EP_MODE_TYPE_BULK +# define MIDI_STREAM_OUT_MODE USB_EP_MODE_TYPE_BULK + .midi_driver = QMK_USB_DRIVER_CONFIG(MIDI_STREAM, 0, false), +#endif + +#ifdef VIRTSER_ENABLE +# define CDC_IN_CAPACITY 4 +# define CDC_OUT_CAPACITY 4 +# define CDC_IN_MODE USB_EP_MODE_TYPE_BULK +# define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK + .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false), +#endif +}; + +#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t)) + +/* --------------------------------------------------------- + * USB driver functions + * --------------------------------------------------------- + */ + +/* 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) { + switch (event) { + case USB_EVENT_ADDRESS: + return; + + case USB_EVENT_CONFIGURED: + osalSysLockFromISR(); + /* Enable the endpoints specified into the configuration. */ +#ifndef KEYBOARD_SHARED_EP + usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config); +#endif +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) + usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config); +#endif +#ifdef SHARED_EP_ENABLE + usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); +#endif + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].inout_ep_config); + #else + usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config); + usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config); + #endif + if (drivers.array[i].config.int_in) { + usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config); + } + qmkusbConfigureHookI(&drivers.array[i].driver); + } + osalSysUnlockFromISR(); + return; + case USB_EVENT_SUSPEND: +#ifdef SLEEP_LED_ENABLE + sleep_led_enable(); +#endif /* SLEEP_LED_ENABLE */ + /* Falls into.*/ + case USB_EVENT_UNCONFIGURED: + /* Falls into.*/ + case USB_EVENT_RESET: + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + chSysLockFromISR(); + /* Disconnection event on suspend.*/ + qmkusbSuspendHookI(&drivers.array[i].driver); + chSysUnlockFromISR(); + } + return; + + case USB_EVENT_WAKEUP: + // TODO: from ISR! print("[W]"); + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + chSysLockFromISR(); + /* Disconnection event on suspend.*/ + 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 */ + return; + + case USB_EVENT_STALLED: + return; + } +} + +/* Function used locally in os/hal/src/usb.c for getting descriptors + * need it here for HID descriptor */ +static uint16_t get_hword(uint8_t *p) { + uint16_t hw; + + hw = (uint16_t)*p++; + hw |= (uint16_t)*p << 8U; + return hw; +} + +/* + * Appendix G: HID Request Support Requirements + * + * The following table enumerates the requests that need to be supported by various types of HID class devices. + * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol + * ------------------------------------------------------------------------------------------ + * Boot Mouse Required Optional Optional Optional Required Required + * Non-Boot Mouse Required Optional Optional Optional Optional Optional + * Boot Keyboard Required Optional Required Required Required Required + * Non-Boot Keybrd Required Optional Required Required Optional Optional + * Other Device Required Optional Optional Optional Optional Optional + */ + +#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) +static uint8_t set_report_buf[2] __attribute__((aligned(2))); +static void set_led_transfer_cb(USBDriver *usbp) { + if ((set_report_buf[0] == REPORT_ID_KEYBOARD) || (set_report_buf[0] == REPORT_ID_NKRO)) { + keyboard_led_stats = set_report_buf[1]; + } +} +#endif + +/* Callback for SETUP request on the endpoint 0 (control) */ +static bool usb_request_hook_cb(USBDriver *usbp) { + const USBDescriptor *dp; + + /* usbp->setup fields: + * 0: bmRequestType (bitmask) + * 1: bRequest + * 2,3: (LSB,MSB) wValue + * 4,5: (LSB,MSB) wIndex + * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ + + /* Handle HID class specific requests */ + if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { + switch (usbp->setup[0] & USB_RTYPE_DIR_MASK) { + case USB_RTYPE_DIR_DEV2HOST: + switch (usbp->setup[1]) { /* bRequest */ + case HID_GET_REPORT: + switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ + case KEYBOARD_INTERFACE: + usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); + return TRUE; + break; + +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) + case MOUSE_INTERFACE: + usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); + return TRUE; + break; +#endif + + default: + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + + case HID_GET_PROTOCOL: + if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); + return TRUE; + } + break; + + case HID_GET_IDLE: + usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); + return TRUE; + break; + } + break; + + case USB_RTYPE_DIR_HOST2DEV: + switch (usbp->setup[1]) { /* bRequest */ + case HID_SET_REPORT: + switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */ +#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) + case SHARED_INTERFACE: + usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb); + return TRUE; + break; +#endif + case KEYBOARD_INTERFACE: + /* keyboard_led_stats = + * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */ + usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL); + return TRUE; + break; + } + break; + + case HID_SET_PROTOCOL: + if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ +#ifdef NKRO_ENABLE + keymap_config.nkro = !!keyboard_protocol; + if (!keymap_config.nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if (keyboard_idle) { +#endif /* NKRO_ENABLE */ + /* arm the idle timer if boot protocol & idle */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + osalSysUnlockFromISR(); + } + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + + case HID_SET_IDLE: + keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ + /* arm the timer */ +#ifdef NKRO_ENABLE + if (!keymap_config.nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if (keyboard_idle) { +#endif /* NKRO_ENABLE */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + osalSysUnlockFromISR(); + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + } + } + + /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ + if ((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { + dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); + if (dp == NULL) return FALSE; + usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); + return TRUE; + } + + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + if (drivers.array[i].config.int_in) { + // NOTE: Assumes that we only have one serial driver + return qmkusbRequestsHook(usbp); + } + } + + return FALSE; +} + +/* Start-of-frame callback */ +static void usb_sof_cb(USBDriver *usbp) { + kbd_sof_cb(usbp); + osalSysLockFromISR(); + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + qmkusbSOFHookI(&drivers.array[i].driver); + } + osalSysUnlockFromISR(); +} + +/* USB driver configuration */ +static const USBConfig usbcfg = { + usb_event_cb, /* USB events callback */ + usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ + usb_request_hook_cb, /* Requests hook callback */ + usb_sof_cb /* Start Of Frame callback */ +}; + +/* + * Initialize the USB driver + */ +void init_usb_driver(USBDriver *usbp) { + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + QMKUSBDriver *driver = &drivers.array[i].driver; + drivers.array[i].inout_ep_config.in_state = &drivers.array[i].in_ep_state; + drivers.array[i].inout_ep_config.out_state = &drivers.array[i].out_ep_state; + drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; + qmkusbObjectInit(driver, &drivers.array[i].config); + qmkusbStart(driver, &drivers.array[i].config); + #else + QMKUSBDriver *driver = &drivers.array[i].driver; + drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state; + drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state; + drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; + qmkusbObjectInit(driver, &drivers.array[i].config); + qmkusbStart(driver, &drivers.array[i].config); + #endif + } + /* + * Activates the USB driver and then the USB bus pull-up on D+. + * Note, a delay is inserted in order to not have to disconnect the cable + * after a reset. + */ + usbDisconnectBus(usbp); + wait_ms(1500); + usbStart(usbp, &usbcfg); + usbConnectBus(usbp); + + chVTObjectInit(&keyboard_idle_timer); +} + +/* --------------------------------------------------------- + * Keyboard functions + * --------------------------------------------------------- + */ +/* keyboard IN callback hander (a kbd report has made it IN) */ +#ifndef KEYBOARD_SHARED_EP +void kbd_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} +#endif + +/* start-of-frame handler + * TODO: i guess it would be better to re-implement using timers, + * so that this is not going to have to be checked every 1ms */ +void kbd_sof_cb(USBDriver *usbp) { (void)usbp; } + +/* Idle requests timer code + * callback (called from ISR, unlocked state) */ +static void keyboard_idle_timer_cb(void *arg) { + USBDriver *usbp = (USBDriver *)arg; + + osalSysLockFromISR(); + + /* check that the states of things are as they're supposed to */ + if (usbGetDriverStateI(usbp) != USB_ACTIVE) { + /* do not rearm the timer, should be enabled on IDLE request */ + osalSysUnlockFromISR(); + return; + } + +#ifdef NKRO_ENABLE + if (!keymap_config.nkro && keyboard_idle && keyboard_protocol) { +#else /* NKRO_ENABLE */ + if (keyboard_idle && keyboard_protocol) { +#endif /* NKRO_ENABLE */ + /* TODO: are we sure we want the KBD_ENDPOINT? */ + if (!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) { + usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE); + } + /* rearm the timer */ + chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + } + + /* do not rearm the timer if the condition above fails + * it should be enabled again on either IDLE or SET_PROTOCOL requests */ + osalSysUnlockFromISR(); +} + +/* LED status */ +uint8_t keyboard_leds(void) { return (uint8_t)(keyboard_led_stats & 0xFF); } + +/* prepare and start sending a report IN + * not callable from ISR or locked state */ +void send_keyboard(report_keyboard_t *report) { + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + goto unlock; + } + +#ifdef NKRO_ENABLE + if (keymap_config.nkro && keyboard_protocol) { /* NKRO protocol */ + /* need to wait until the previous packet has made it through */ + /* can rewrite this using the synchronous API, then would wait + * until *after* the packet has been transmitted. I think + * this is more efficient */ + /* busy wait, should be short and not very common */ + if (usbGetTransmitStatusI(&USB_DRIVER, SHARED_IN_EPNUM)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[SHARED_IN_EPNUM]->in_state->thread); + + /* after osalThreadSuspendS returns USB status might have changed */ + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + goto unlock; + } + } + usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report)); + } else +#endif /* NKRO_ENABLE */ + { /* regular protocol */ + /* need to wait until the previous packet has made it through */ + /* busy wait, should be short and not very common */ + if (usbGetTransmitStatusI(&USB_DRIVER, KEYBOARD_IN_EPNUM)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[KEYBOARD_IN_EPNUM]->in_state->thread); + + /* after osalThreadSuspendS returns USB status might have changed */ + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + goto unlock; + } + } + uint8_t *data, size; + if (keyboard_protocol) { + data = (uint8_t *)report; + size = KEYBOARD_REPORT_SIZE; + } else { /* boot protocol */ + data = &report->mods; + size = 8; + } + usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, data, size); + } + keyboard_report_sent = *report; + +unlock: + osalSysUnlock(); +} + +/* --------------------------------------------------------- + * Mouse functions + * --------------------------------------------------------- + */ + +#ifdef MOUSE_ENABLE + +# ifndef MOUSE_SHARED_EP +/* mouse IN callback hander (a mouse report has made it IN) */ +void mouse_in_cb(USBDriver *usbp, usbep_t ep) { + (void)usbp; + (void)ep; +} +# endif + +void send_mouse(report_mouse_t *report) { + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + if (usbGetTransmitStatusI(&USB_DRIVER, MOUSE_IN_EPNUM)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[MOUSE_IN_EPNUM]->in_state->thread, MS2ST(10)) == MSG_TIMEOUT) { + osalSysUnlock(); + return; + } + } + usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t)); + osalSysUnlock(); +} + +#else /* MOUSE_ENABLE */ +void send_mouse(report_mouse_t *report) { (void)report; } +#endif /* MOUSE_ENABLE */ + +/* --------------------------------------------------------- + * Shared EP functions + * --------------------------------------------------------- + */ +#ifdef SHARED_EP_ENABLE +/* shared IN callback hander */ +void shared_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} +#endif + +/* --------------------------------------------------------- + * Extrakey functions + * --------------------------------------------------------- + */ + +#ifdef EXTRAKEY_ENABLE +static void send_extra_report(uint8_t report_id, uint16_t data) { + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + report_extra_t report = {.report_id = report_id, .usage = data}; + + usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t)); + osalSysUnlock(); +} + +void send_system(uint16_t data) { send_extra_report(REPORT_ID_SYSTEM, data); } + +void send_consumer(uint16_t data) { send_extra_report(REPORT_ID_CONSUMER, data); } + +#else /* EXTRAKEY_ENABLE */ +void send_system(uint16_t data) { (void)data; } +void send_consumer(uint16_t data) { (void)data; } +#endif /* EXTRAKEY_ENABLE */ + +/* --------------------------------------------------------- + * Console functions + * --------------------------------------------------------- + */ + +#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); +} + +// Just a dummy function for now, this could be exposed as a weak function +// Or connected to the actual QMK console +static void console_receive(uint8_t *data, uint8_t length) { + (void)data; + (void)length; +} + +void console_task(void) { + uint8_t buffer[CONSOLE_EPSIZE]; + size_t size = 0; + do { + size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); + if (size > 0) { + console_receive(buffer, size); + } + } while (size > 0); +} + +#else /* CONSOLE_ENABLE */ +int8_t sendchar(uint8_t c) { + (void)c; + return 0; +} +#endif /* CONSOLE_ENABLE */ + +void sendchar_pf(void *p, char c) { + (void)p; + sendchar((uint8_t)c); +} + +#ifdef RAW_ENABLE +void raw_hid_send(uint8_t *data, uint8_t length) { + // TODO: implement variable size packet + if (length != RAW_EPSIZE) { + return; + } + chnWrite(&drivers.raw_driver.driver, data, length); +} + +__attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) { + // Users should #include "raw_hid.h" in their own code + // and implement this function there. Leave this as weak linkage + // so users can opt to not handle data coming in. +} + +void raw_hid_task(void) { + uint8_t buffer[RAW_EPSIZE]; + size_t size = 0; + do { + size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); + if (size > 0) { + raw_hid_receive(buffer, size); + } + } while (size > 0); +} + +#endif + +#ifdef MIDI_ENABLE + +void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); } + +bool recv_midi_packet(MIDI_EventPacket_t *const event) { + size_t size = chnReadTimeout(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t), TIME_IMMEDIATE); + return size == sizeof(MIDI_EventPacket_t); +} + +#endif + +#ifdef VIRTSER_ENABLE + +void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); } + +__attribute__((weak)) void virtser_recv(uint8_t c) { + // Ignore by default +} + +void virtser_task(void) { + uint8_t numBytesReceived = 0; + uint8_t buffer[16]; + do { + numBytesReceived = chnReadTimeout(&drivers.serial_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); + for (int i = 0; i < numBytesReceived; i++) { + virtser_recv(buffer[i]); + } + } while (numBytesReceived > 0); +} + +#endif diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index b2423fa7e662..89dfd01f67b6 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -189,7 +189,12 @@ enum usb_endpoints { #ifdef RAW_ENABLE RAW_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + #define RAW_OUT_EPNUM RAW_IN_EPNUM + #else RAW_OUT_EPNUM = NEXT_EPNUM, + #endif + #endif #ifdef SHARED_EP_ENABLE @@ -203,7 +208,11 @@ enum usb_endpoints { // ChibiOS has enough memory and descriptor to actually enable the endpoint // It could use the same endpoint numbers, as that's supported by ChibiOS // But the QMK code currently assumes that the endpoint numbers are different + #if STM32_USB_USE_OTG1 + CONSOLE_OUT_EPNUM = CONSOLE_IN_EPNUM, + #else CONSOLE_OUT_EPNUM = NEXT_EPNUM, + #endif # else # define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM # endif @@ -211,7 +220,11 @@ enum usb_endpoints { #ifdef MIDI_ENABLE MIDI_STREAM_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + MIDI_STREAM_OUT_EPNUM = MIDI_STREAM_IN_EPNUM, + #else MIDI_STREAM_OUT_EPNUM = NEXT_EPNUM, + #endif # define MIDI_STREAM_IN_EPADDR (ENDPOINT_DIR_IN | MIDI_STREAM_IN_EPNUM) # define MIDI_STREAM_OUT_EPADDR (ENDPOINT_DIR_OUT | MIDI_STREAM_OUT_EPNUM) #endif @@ -219,7 +232,11 @@ enum usb_endpoints { #ifdef VIRTSER_ENABLE CDC_NOTIFICATION_EPNUM = NEXT_EPNUM, CDC_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + CDC_OUT_EPNUM = CDC_IN_EPNUM, + #else CDC_OUT_EPNUM = NEXT_EPNUM, + #endif # define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | CDC_NOTIFICATION_EPNUM) # define CDC_IN_EPADDR (ENDPOINT_DIR_IN | CDC_IN_EPNUM) # define CDC_OUT_EPADDR (ENDPOINT_DIR_OUT | CDC_OUT_EPNUM) From d97b5287855bf782bd53512487b257d67efa3b44 Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Thu, 23 Apr 2020 23:45:18 +0800 Subject: [PATCH 2/9] update endpoint numbering for stm32f4 --- tmk_core/protocol/chibios/usb_main.c | 4 ++-- tmk_core/protocol/usb_descriptor.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index b9068e510742..15be9ae1949d 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -229,8 +229,8 @@ typedef struct { .in_size = stream##_EPSIZE, \ .out_size = stream##_EPSIZE, \ .fixed_size = fixedsize, \ - .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ - .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + .ib = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ } \ } #else diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 89dfd01f67b6..72d8e87cf36f 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -209,7 +209,7 @@ enum usb_endpoints { // It could use the same endpoint numbers, as that's supported by ChibiOS // But the QMK code currently assumes that the endpoint numbers are different #if STM32_USB_USE_OTG1 - CONSOLE_OUT_EPNUM = CONSOLE_IN_EPNUM, + #define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM #else CONSOLE_OUT_EPNUM = NEXT_EPNUM, #endif @@ -221,7 +221,7 @@ enum usb_endpoints { #ifdef MIDI_ENABLE MIDI_STREAM_IN_EPNUM = NEXT_EPNUM, #if STM32_USB_USE_OTG1 - MIDI_STREAM_OUT_EPNUM = MIDI_STREAM_IN_EPNUM, + #define MIDI_STREAM_OUT_EPNUM MIDI_STREAM_IN_EPNUM #else MIDI_STREAM_OUT_EPNUM = NEXT_EPNUM, #endif @@ -233,7 +233,7 @@ enum usb_endpoints { CDC_NOTIFICATION_EPNUM = NEXT_EPNUM, CDC_IN_EPNUM = NEXT_EPNUM, #if STM32_USB_USE_OTG1 - CDC_OUT_EPNUM = CDC_IN_EPNUM, + #define CDC_OUT_EPNUM CDC_IN_EPNUM #else CDC_OUT_EPNUM = NEXT_EPNUM, #endif From 870ed2ff57b58dbc7f7a77f8cca499903a6efa61 Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Thu, 23 Apr 2020 23:56:22 +0800 Subject: [PATCH 3/9] removed testing file --- tmk_core/protocol/chibios/usb_main.new.c | 934 ----------------------- 1 file changed, 934 deletions(-) delete mode 100644 tmk_core/protocol/chibios/usb_main.new.c diff --git a/tmk_core/protocol/chibios/usb_main.new.c b/tmk_core/protocol/chibios/usb_main.new.c deleted file mode 100644 index fa08f54a1df7..000000000000 --- a/tmk_core/protocol/chibios/usb_main.new.c +++ /dev/null @@ -1,934 +0,0 @@ -/* - * (c) 2015 flabberast - * - * Based on the following work: - * - Guillaume Duc's raw hid example (MIT License) - * https://github.com/guiduc/usb-hid-chibios-example - * - PJRC Teensy examples (MIT License) - * https://www.pjrc.com/teensy/usb_keyboard.html - * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) - * https://github.com/tmk/tmk_keyboard/ - * - ChibiOS demo code (Apache 2.0 License) - * http://www.chibios.org - * - * Since some GPL'd code is used, this work is licensed under - * GPL v2 or later. - */ - -/* - * Implementation notes: - * - * USBEndpointConfig - Configured using explicit order instead of struct member name. - * This is due to ChibiOS hal LLD differences, which is dependent on hardware, - * "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`. - * Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file - * makes the assumption this is safe to avoid littering with preprocessor directives. - */ - -#include "ch.h" -#include "hal.h" - -#include "usb_main.h" - -#include "host.h" -#include "debug.h" -#include "suspend.h" -#ifdef SLEEP_LED_ENABLE -# include "sleep_led.h" -# include "led.h" -#endif -#include "wait.h" -#include "usb_descriptor.h" -#include "usb_driver.h" - -#ifdef NKRO_ENABLE -# include "keycode_config.h" - -extern keymap_config_t keymap_config; -#endif - -/* --------------------------------------------------------- - * Global interface variables and declarations - * --------------------------------------------------------- - */ - -#ifndef usb_lld_connect_bus -# define usb_lld_connect_bus(usbp) -#endif - -#ifndef usb_lld_disconnect_bus -# define usb_lld_disconnect_bus(usbp) -#endif - -uint8_t keyboard_idle __attribute__((aligned(2))) = 0; -uint8_t keyboard_protocol __attribute__((aligned(2))) = 1; -uint16_t keyboard_led_stats __attribute__((aligned(2))) = 0; -volatile uint16_t keyboard_idle_count = 0; -static virtual_timer_t keyboard_idle_timer; -static void keyboard_idle_timer_cb(void *arg); - -report_keyboard_t keyboard_report_sent = {{0}}; -#ifdef MOUSE_ENABLE -report_mouse_t mouse_report_blank = {0}; -#endif /* MOUSE_ENABLE */ -#ifdef EXTRAKEY_ENABLE -uint8_t extra_report_blank[3] = {0}; -#endif /* EXTRAKEY_ENABLE */ - -/* --------------------------------------------------------- - * Descriptors and USB driver objects - * --------------------------------------------------------- - */ - -/* HID specific constants */ -#define HID_GET_REPORT 0x01 -#define HID_GET_IDLE 0x02 -#define HID_GET_PROTOCOL 0x03 -#define HID_SET_REPORT 0x09 -#define HID_SET_IDLE 0x0A -#define HID_SET_PROTOCOL 0x0B - -/* - * Handles the GET_DESCRIPTOR callback - * - * Returns the proper descriptor - */ -static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) { - (void)usbp; - static USBDescriptor desc; - uint16_t wValue = ((uint16_t)dtype << 8) | dindex; - desc.ud_string = NULL; - desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const) & desc.ud_string); - if (desc.ud_string == NULL) - return NULL; - else - return &desc; -} - -#ifndef KEYBOARD_SHARED_EP -/* keyboard endpoint state structure */ -static USBInEndpointState kbd_ep_state; -/* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig kbd_ep_config = { - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ - NULL, /* SETUP packet notification callback */ - kbd_in_cb, /* IN notification callback */ - NULL, /* OUT notification callback */ - KEYBOARD_EPSIZE, /* IN maximum packet size */ - 0, /* OUT maximum packet size */ - &kbd_ep_state, /* IN Endpoint state */ - NULL, /* OUT endpoint state */ - 2, /* IN multiplier */ - NULL /* SETUP buffer (not a SETUP endpoint) */ -}; -#endif - -#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) -/* mouse endpoint state structure */ -static USBInEndpointState mouse_ep_state; - -/* mouse endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig mouse_ep_config = { - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ - NULL, /* SETUP packet notification callback */ - mouse_in_cb, /* IN notification callback */ - NULL, /* OUT notification callback */ - MOUSE_EPSIZE, /* IN maximum packet size */ - 0, /* OUT maximum packet size */ - &mouse_ep_state, /* IN Endpoint state */ - NULL, /* OUT endpoint state */ - 2, /* IN multiplier */ - NULL /* SETUP buffer (not a SETUP endpoint) */ -}; -#endif - -#ifdef SHARED_EP_ENABLE -/* shared endpoint state structure */ -static USBInEndpointState shared_ep_state; - -/* shared endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig shared_ep_config = { - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ - NULL, /* SETUP packet notification callback */ - shared_in_cb, /* IN notification callback */ - NULL, /* OUT notification callback */ - SHARED_EPSIZE, /* IN maximum packet size */ - 0, /* OUT maximum packet size */ - &shared_ep_state, /* IN Endpoint state */ - NULL, /* OUT endpoint state */ - 2, /* IN multiplier */ - NULL /* SETUP buffer (not a SETUP endpoint) */ -}; -#endif - -#if STM32_USB_USE_OTG1 -typedef struct { - size_t queue_capacity_in; - size_t queue_capacity_out; - USBInEndpointState in_ep_state; - USBOutEndpointState out_ep_state; - USBInEndpointState int_ep_state; - USBEndpointConfig inout_ep_config; - USBEndpointConfig int_ep_config; - const QMKUSBConfig config; - QMKUSBDriver driver; -} usb_driver_config_t; -#else -typedef struct { - size_t queue_capacity_in; - size_t queue_capacity_out; - USBInEndpointState in_ep_state; - USBOutEndpointState out_ep_state; - USBInEndpointState int_ep_state; - USBEndpointConfig in_ep_config; - USBEndpointConfig out_ep_config; - USBEndpointConfig int_ep_config; - const QMKUSBConfig config; - QMKUSBDriver driver; -} usb_driver_config_t; -#endif - -#if STM32_USB_USE_OTG1 -/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ -#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ - { \ - .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ - .inout_ep_config = \ - { \ - stream##_IN_MODE, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - qmkusbDataTransmitted, /* IN notification callback */ \ - qmkusbDataReceived, /* OUT notification callback */ \ - stream##_EPSIZE, /* IN maximum packet size */ \ - stream##_EPSIZE, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .int_ep_config = \ - { \ - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - qmkusbInterruptTransmitted, /* IN notification callback */ \ - NULL, /* OUT notification callback */ \ - CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ - 0, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL, /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .config = { \ - .usbp = &USB_DRIVER, \ - .bulk_in = stream##_IN_EPNUM, \ - .bulk_out = stream##_OUT_EPNUM, \ - .int_in = notification, \ - .in_buffers = stream##_IN_CAPACITY, \ - .out_buffers = stream##_OUT_CAPACITY, \ - .in_size = stream##_EPSIZE, \ - .out_size = stream##_EPSIZE, \ - .fixed_size = fixedsize, \ - .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ - .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ - } \ - } -#else -/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ -#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ - { \ - .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ - .in_ep_config = \ - { \ - stream##_IN_MODE, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - qmkusbDataTransmitted, /* IN notification callback */ \ - NULL, /* OUT notification callback */ \ - stream##_EPSIZE, /* IN maximum packet size */ \ - 0, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .out_ep_config = \ - { \ - stream##_OUT_MODE, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - NULL, /* IN notification callback */ \ - qmkusbDataReceived, /* OUT notification callback */ \ - 0, /* IN maximum packet size */ \ - stream##_EPSIZE, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL, /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .int_ep_config = \ - { \ - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - qmkusbInterruptTransmitted, /* IN notification callback */ \ - NULL, /* OUT notification callback */ \ - CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ - 0, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL, /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .config = { \ - .usbp = &USB_DRIVER, \ - .bulk_in = stream##_IN_EPNUM, \ - .bulk_out = stream##_OUT_EPNUM, \ - .int_in = notification, \ - .in_buffers = stream##_IN_CAPACITY, \ - .out_buffers = stream##_OUT_CAPACITY, \ - .in_size = stream##_EPSIZE, \ - .out_size = stream##_EPSIZE, \ - .fixed_size = fixedsize, \ - .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ - .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ - } \ - } -#endif - -typedef struct { - union { - struct { -#ifdef CONSOLE_ENABLE - usb_driver_config_t console_driver; -#endif -#if defined(RAW_ENABLE) - usb_driver_config_t raw_driver; -#endif -#ifdef MIDI_ENABLE - usb_driver_config_t midi_driver; -#endif -#ifdef VIRTSER_ENABLE - usb_driver_config_t serial_driver; -#endif - }; - usb_driver_config_t array[0]; - }; -} usb_driver_configs_t; - -static usb_driver_configs_t drivers = { -#ifdef CONSOLE_ENABLE -# define CONSOLE_IN_CAPACITY 4 -# define CONSOLE_OUT_CAPACITY 4 -# define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR -# define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR - .console_driver = QMK_USB_DRIVER_CONFIG(CONSOLE, 0, true), -#endif -#if defined(RAW_ENABLE) -# define RAW_IN_CAPACITY 4 -# define RAW_OUT_CAPACITY 4 -# define RAW_IN_MODE USB_EP_MODE_TYPE_INTR -# define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR - .raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false), - -#endif - -#ifdef MIDI_ENABLE -# define MIDI_STREAM_IN_CAPACITY 4 -# define MIDI_STREAM_OUT_CAPACITY 4 -# define MIDI_STREAM_IN_MODE USB_EP_MODE_TYPE_BULK -# define MIDI_STREAM_OUT_MODE USB_EP_MODE_TYPE_BULK - .midi_driver = QMK_USB_DRIVER_CONFIG(MIDI_STREAM, 0, false), -#endif - -#ifdef VIRTSER_ENABLE -# define CDC_IN_CAPACITY 4 -# define CDC_OUT_CAPACITY 4 -# define CDC_IN_MODE USB_EP_MODE_TYPE_BULK -# define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK - .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false), -#endif -}; - -#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t)) - -/* --------------------------------------------------------- - * USB driver functions - * --------------------------------------------------------- - */ - -/* 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) { - switch (event) { - case USB_EVENT_ADDRESS: - return; - - case USB_EVENT_CONFIGURED: - osalSysLockFromISR(); - /* Enable the endpoints specified into the configuration. */ -#ifndef KEYBOARD_SHARED_EP - usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config); -#endif -#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) - usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config); -#endif -#ifdef SHARED_EP_ENABLE - usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); -#endif - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - #if STM32_USB_USE_OTG1 - usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].inout_ep_config); - #else - usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config); - usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config); - #endif - if (drivers.array[i].config.int_in) { - usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config); - } - qmkusbConfigureHookI(&drivers.array[i].driver); - } - osalSysUnlockFromISR(); - return; - case USB_EVENT_SUSPEND: -#ifdef SLEEP_LED_ENABLE - sleep_led_enable(); -#endif /* SLEEP_LED_ENABLE */ - /* Falls into.*/ - case USB_EVENT_UNCONFIGURED: - /* Falls into.*/ - case USB_EVENT_RESET: - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - chSysLockFromISR(); - /* Disconnection event on suspend.*/ - qmkusbSuspendHookI(&drivers.array[i].driver); - chSysUnlockFromISR(); - } - return; - - case USB_EVENT_WAKEUP: - // TODO: from ISR! print("[W]"); - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - chSysLockFromISR(); - /* Disconnection event on suspend.*/ - 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 */ - return; - - case USB_EVENT_STALLED: - return; - } -} - -/* Function used locally in os/hal/src/usb.c for getting descriptors - * need it here for HID descriptor */ -static uint16_t get_hword(uint8_t *p) { - uint16_t hw; - - hw = (uint16_t)*p++; - hw |= (uint16_t)*p << 8U; - return hw; -} - -/* - * Appendix G: HID Request Support Requirements - * - * The following table enumerates the requests that need to be supported by various types of HID class devices. - * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol - * ------------------------------------------------------------------------------------------ - * Boot Mouse Required Optional Optional Optional Required Required - * Non-Boot Mouse Required Optional Optional Optional Optional Optional - * Boot Keyboard Required Optional Required Required Required Required - * Non-Boot Keybrd Required Optional Required Required Optional Optional - * Other Device Required Optional Optional Optional Optional Optional - */ - -#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) -static uint8_t set_report_buf[2] __attribute__((aligned(2))); -static void set_led_transfer_cb(USBDriver *usbp) { - if ((set_report_buf[0] == REPORT_ID_KEYBOARD) || (set_report_buf[0] == REPORT_ID_NKRO)) { - keyboard_led_stats = set_report_buf[1]; - } -} -#endif - -/* Callback for SETUP request on the endpoint 0 (control) */ -static bool usb_request_hook_cb(USBDriver *usbp) { - const USBDescriptor *dp; - - /* usbp->setup fields: - * 0: bmRequestType (bitmask) - * 1: bRequest - * 2,3: (LSB,MSB) wValue - * 4,5: (LSB,MSB) wIndex - * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ - - /* Handle HID class specific requests */ - if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { - switch (usbp->setup[0] & USB_RTYPE_DIR_MASK) { - case USB_RTYPE_DIR_DEV2HOST: - switch (usbp->setup[1]) { /* bRequest */ - case HID_GET_REPORT: - switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ - case KEYBOARD_INTERFACE: - usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); - return TRUE; - break; - -#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) - case MOUSE_INTERFACE: - usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); - return TRUE; - break; -#endif - - default: - usbSetupTransfer(usbp, NULL, 0, NULL); - return TRUE; - break; - } - break; - - case HID_GET_PROTOCOL: - if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ - usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); - return TRUE; - } - break; - - case HID_GET_IDLE: - usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); - return TRUE; - break; - } - break; - - case USB_RTYPE_DIR_HOST2DEV: - switch (usbp->setup[1]) { /* bRequest */ - case HID_SET_REPORT: - switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */ -#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) - case SHARED_INTERFACE: - usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb); - return TRUE; - break; -#endif - case KEYBOARD_INTERFACE: - /* keyboard_led_stats = - * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */ - usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL); - return TRUE; - break; - } - break; - - case HID_SET_PROTOCOL: - if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ - keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ -#ifdef NKRO_ENABLE - keymap_config.nkro = !!keyboard_protocol; - if (!keymap_config.nkro && keyboard_idle) { -#else /* NKRO_ENABLE */ - if (keyboard_idle) { -#endif /* NKRO_ENABLE */ - /* arm the idle timer if boot protocol & idle */ - osalSysLockFromISR(); - chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); - osalSysUnlockFromISR(); - } - } - usbSetupTransfer(usbp, NULL, 0, NULL); - return TRUE; - break; - - case HID_SET_IDLE: - keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ - /* arm the timer */ -#ifdef NKRO_ENABLE - if (!keymap_config.nkro && keyboard_idle) { -#else /* NKRO_ENABLE */ - if (keyboard_idle) { -#endif /* NKRO_ENABLE */ - osalSysLockFromISR(); - chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); - osalSysUnlockFromISR(); - } - usbSetupTransfer(usbp, NULL, 0, NULL); - return TRUE; - break; - } - break; - } - } - - /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ - if ((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { - dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); - if (dp == NULL) return FALSE; - usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); - return TRUE; - } - - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - if (drivers.array[i].config.int_in) { - // NOTE: Assumes that we only have one serial driver - return qmkusbRequestsHook(usbp); - } - } - - return FALSE; -} - -/* Start-of-frame callback */ -static void usb_sof_cb(USBDriver *usbp) { - kbd_sof_cb(usbp); - osalSysLockFromISR(); - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - qmkusbSOFHookI(&drivers.array[i].driver); - } - osalSysUnlockFromISR(); -} - -/* USB driver configuration */ -static const USBConfig usbcfg = { - usb_event_cb, /* USB events callback */ - usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ - usb_request_hook_cb, /* Requests hook callback */ - usb_sof_cb /* Start Of Frame callback */ -}; - -/* - * Initialize the USB driver - */ -void init_usb_driver(USBDriver *usbp) { - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - #if STM32_USB_USE_OTG1 - QMKUSBDriver *driver = &drivers.array[i].driver; - drivers.array[i].inout_ep_config.in_state = &drivers.array[i].in_ep_state; - drivers.array[i].inout_ep_config.out_state = &drivers.array[i].out_ep_state; - drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; - qmkusbObjectInit(driver, &drivers.array[i].config); - qmkusbStart(driver, &drivers.array[i].config); - #else - QMKUSBDriver *driver = &drivers.array[i].driver; - drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state; - drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state; - drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; - qmkusbObjectInit(driver, &drivers.array[i].config); - qmkusbStart(driver, &drivers.array[i].config); - #endif - } - /* - * Activates the USB driver and then the USB bus pull-up on D+. - * Note, a delay is inserted in order to not have to disconnect the cable - * after a reset. - */ - usbDisconnectBus(usbp); - wait_ms(1500); - usbStart(usbp, &usbcfg); - usbConnectBus(usbp); - - chVTObjectInit(&keyboard_idle_timer); -} - -/* --------------------------------------------------------- - * Keyboard functions - * --------------------------------------------------------- - */ -/* keyboard IN callback hander (a kbd report has made it IN) */ -#ifndef KEYBOARD_SHARED_EP -void kbd_in_cb(USBDriver *usbp, usbep_t ep) { - /* STUB */ - (void)usbp; - (void)ep; -} -#endif - -/* start-of-frame handler - * TODO: i guess it would be better to re-implement using timers, - * so that this is not going to have to be checked every 1ms */ -void kbd_sof_cb(USBDriver *usbp) { (void)usbp; } - -/* Idle requests timer code - * callback (called from ISR, unlocked state) */ -static void keyboard_idle_timer_cb(void *arg) { - USBDriver *usbp = (USBDriver *)arg; - - osalSysLockFromISR(); - - /* check that the states of things are as they're supposed to */ - if (usbGetDriverStateI(usbp) != USB_ACTIVE) { - /* do not rearm the timer, should be enabled on IDLE request */ - osalSysUnlockFromISR(); - return; - } - -#ifdef NKRO_ENABLE - if (!keymap_config.nkro && keyboard_idle && keyboard_protocol) { -#else /* NKRO_ENABLE */ - if (keyboard_idle && keyboard_protocol) { -#endif /* NKRO_ENABLE */ - /* TODO: are we sure we want the KBD_ENDPOINT? */ - if (!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) { - usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE); - } - /* rearm the timer */ - chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); - } - - /* do not rearm the timer if the condition above fails - * it should be enabled again on either IDLE or SET_PROTOCOL requests */ - osalSysUnlockFromISR(); -} - -/* LED status */ -uint8_t keyboard_leds(void) { return (uint8_t)(keyboard_led_stats & 0xFF); } - -/* prepare and start sending a report IN - * not callable from ISR or locked state */ -void send_keyboard(report_keyboard_t *report) { - osalSysLock(); - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - goto unlock; - } - -#ifdef NKRO_ENABLE - if (keymap_config.nkro && keyboard_protocol) { /* NKRO protocol */ - /* need to wait until the previous packet has made it through */ - /* can rewrite this using the synchronous API, then would wait - * until *after* the packet has been transmitted. I think - * this is more efficient */ - /* busy wait, should be short and not very common */ - if (usbGetTransmitStatusI(&USB_DRIVER, SHARED_IN_EPNUM)) { - /* Need to either suspend, or loop and call unlock/lock during - * every iteration - otherwise the system will remain locked, - * no interrupts served, so USB not going through as well. - * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ - osalThreadSuspendS(&(&USB_DRIVER)->epc[SHARED_IN_EPNUM]->in_state->thread); - - /* after osalThreadSuspendS returns USB status might have changed */ - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - goto unlock; - } - } - usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report)); - } else -#endif /* NKRO_ENABLE */ - { /* regular protocol */ - /* need to wait until the previous packet has made it through */ - /* busy wait, should be short and not very common */ - if (usbGetTransmitStatusI(&USB_DRIVER, KEYBOARD_IN_EPNUM)) { - /* Need to either suspend, or loop and call unlock/lock during - * every iteration - otherwise the system will remain locked, - * no interrupts served, so USB not going through as well. - * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ - osalThreadSuspendS(&(&USB_DRIVER)->epc[KEYBOARD_IN_EPNUM]->in_state->thread); - - /* after osalThreadSuspendS returns USB status might have changed */ - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - goto unlock; - } - } - uint8_t *data, size; - if (keyboard_protocol) { - data = (uint8_t *)report; - size = KEYBOARD_REPORT_SIZE; - } else { /* boot protocol */ - data = &report->mods; - size = 8; - } - usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, data, size); - } - keyboard_report_sent = *report; - -unlock: - osalSysUnlock(); -} - -/* --------------------------------------------------------- - * Mouse functions - * --------------------------------------------------------- - */ - -#ifdef MOUSE_ENABLE - -# ifndef MOUSE_SHARED_EP -/* mouse IN callback hander (a mouse report has made it IN) */ -void mouse_in_cb(USBDriver *usbp, usbep_t ep) { - (void)usbp; - (void)ep; -} -# endif - -void send_mouse(report_mouse_t *report) { - osalSysLock(); - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - osalSysUnlock(); - return; - } - - if (usbGetTransmitStatusI(&USB_DRIVER, MOUSE_IN_EPNUM)) { - /* Need to either suspend, or loop and call unlock/lock during - * every iteration - otherwise the system will remain locked, - * no interrupts served, so USB not going through as well. - * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ - if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[MOUSE_IN_EPNUM]->in_state->thread, MS2ST(10)) == MSG_TIMEOUT) { - osalSysUnlock(); - return; - } - } - usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t)); - osalSysUnlock(); -} - -#else /* MOUSE_ENABLE */ -void send_mouse(report_mouse_t *report) { (void)report; } -#endif /* MOUSE_ENABLE */ - -/* --------------------------------------------------------- - * Shared EP functions - * --------------------------------------------------------- - */ -#ifdef SHARED_EP_ENABLE -/* shared IN callback hander */ -void shared_in_cb(USBDriver *usbp, usbep_t ep) { - /* STUB */ - (void)usbp; - (void)ep; -} -#endif - -/* --------------------------------------------------------- - * Extrakey functions - * --------------------------------------------------------- - */ - -#ifdef EXTRAKEY_ENABLE -static void send_extra_report(uint8_t report_id, uint16_t data) { - osalSysLock(); - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - osalSysUnlock(); - return; - } - - report_extra_t report = {.report_id = report_id, .usage = data}; - - usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t)); - osalSysUnlock(); -} - -void send_system(uint16_t data) { send_extra_report(REPORT_ID_SYSTEM, data); } - -void send_consumer(uint16_t data) { send_extra_report(REPORT_ID_CONSUMER, data); } - -#else /* EXTRAKEY_ENABLE */ -void send_system(uint16_t data) { (void)data; } -void send_consumer(uint16_t data) { (void)data; } -#endif /* EXTRAKEY_ENABLE */ - -/* --------------------------------------------------------- - * Console functions - * --------------------------------------------------------- - */ - -#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); -} - -// Just a dummy function for now, this could be exposed as a weak function -// Or connected to the actual QMK console -static void console_receive(uint8_t *data, uint8_t length) { - (void)data; - (void)length; -} - -void console_task(void) { - uint8_t buffer[CONSOLE_EPSIZE]; - size_t size = 0; - do { - size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); - if (size > 0) { - console_receive(buffer, size); - } - } while (size > 0); -} - -#else /* CONSOLE_ENABLE */ -int8_t sendchar(uint8_t c) { - (void)c; - return 0; -} -#endif /* CONSOLE_ENABLE */ - -void sendchar_pf(void *p, char c) { - (void)p; - sendchar((uint8_t)c); -} - -#ifdef RAW_ENABLE -void raw_hid_send(uint8_t *data, uint8_t length) { - // TODO: implement variable size packet - if (length != RAW_EPSIZE) { - return; - } - chnWrite(&drivers.raw_driver.driver, data, length); -} - -__attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) { - // Users should #include "raw_hid.h" in their own code - // and implement this function there. Leave this as weak linkage - // so users can opt to not handle data coming in. -} - -void raw_hid_task(void) { - uint8_t buffer[RAW_EPSIZE]; - size_t size = 0; - do { - size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); - if (size > 0) { - raw_hid_receive(buffer, size); - } - } while (size > 0); -} - -#endif - -#ifdef MIDI_ENABLE - -void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); } - -bool recv_midi_packet(MIDI_EventPacket_t *const event) { - size_t size = chnReadTimeout(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t), TIME_IMMEDIATE); - return size == sizeof(MIDI_EventPacket_t); -} - -#endif - -#ifdef VIRTSER_ENABLE - -void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); } - -__attribute__((weak)) void virtser_recv(uint8_t c) { - // Ignore by default -} - -void virtser_task(void) { - uint8_t numBytesReceived = 0; - uint8_t buffer[16]; - do { - numBytesReceived = chnReadTimeout(&drivers.serial_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); - for (int i = 0; i < numBytesReceived; i++) { - virtser_recv(buffer[i]); - } - } while (numBytesReceived > 0); -} - -#endif From 28d6b9d7be53844d44b9fb94967fd202e2659d3a Mon Sep 17 00:00:00 2001 From: Takeshi ISHII <2170248+mtei@users.noreply.github.com> Date: Tue, 10 Nov 2020 17:03:15 +0900 Subject: [PATCH 4/9] add DEBUG_MATRIX_SCAN_RATE_ENABLE to common_features.mk (#10824) Add a Make variable to easily enable DEBUG_MATRIX_SCAN_RATE on the command line. eg. ``` make DEBUG_MATRIX_SCAN_RATE_ENABLE=yes KEYBOARD:KEYMAP ``` --- common_features.mk | 5 +++++ tmk_core/common/keyboard.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/common_features.mk b/common_features.mk index b49c08c50b25..c821dee01856 100644 --- a/common_features.mk +++ b/common_features.mk @@ -20,6 +20,11 @@ QUANTUM_SRC += \ $(QUANTUM_DIR)/keymap_common.c \ $(QUANTUM_DIR)/keycode_config.c +ifeq ($(strip $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), yes) + OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE + CONSOLE_ENABLE = yes +endif + ifeq ($(strip $(API_SYSEX_ENABLE)), yes) OPT_DEFS += -DAPI_SYSEX_ENABLE OPT_DEFS += -DAPI_ENABLE diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 2c762ae235ca..18bd56a8ca3f 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -299,6 +299,10 @@ void keyboard_init(void) { dip_switch_init(); #endif +#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE) + debug_enable = true; +#endif + keyboard_post_init_kb(); /* Always keep this last */ } From 3dcb0463ad2ea14322098903440104d0dc6c8513 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Tue, 10 Nov 2020 10:27:35 +0100 Subject: [PATCH 5/9] [Core] Added `add_oneshot_mods` & `del_oneshot_mods` (#10549) * Added `add_oneshot_mods` & `del_oneshot_mods` Deleted undefined and unused prototypes: - void oneshot_enable(void) - void oneshot_disable(void) - void oneshot_toggle(void) Reordered the oneshot functions to follow the same order as other mod functions, that is to say : get, add, del, set, clear * Stricter conditions on add_oneshot_mods & del_oneshot_mods Prevent extending the one shot timer if the called add_oneshot_mods or del_oneshot_mods do not change anything to the current one shot mod state. Co-authored-by: David Kosorin Co-authored-by: David Kosorin --- tmk_core/common/action_util.c | 31 ++++++++++++++++++++++++++----- tmk_core/common/action_util.h | 7 +++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c index fd0e4409f0a3..000503b0821a 100644 --- a/tmk_core/common/action_util.c +++ b/tmk_core/common/action_util.c @@ -290,6 +290,32 @@ void set_macro_mods(uint8_t mods) { macro_mods = mods; } void clear_macro_mods(void) { macro_mods = 0; } #ifndef NO_ACTION_ONESHOT +/** \brief get oneshot mods + * + * FIXME: needs doc + */ +uint8_t get_oneshot_mods(void) { return oneshot_mods; } + +void add_oneshot_mods(uint8_t mods) { + if ((oneshot_mods & mods) != mods) { +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = timer_read(); +# endif + oneshot_mods |= mods; + oneshot_mods_changed_kb(mods); + } +} + +void del_oneshot_mods(uint8_t mods) { + if (oneshot_mods & mods) { + oneshot_mods &= ~mods; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = oneshot_mods ? timer_read() : 0; +# endif + oneshot_mods_changed_kb(oneshot_mods); + } +} + /** \brief set oneshot mods * * FIXME: needs doc @@ -316,11 +342,6 @@ void clear_oneshot_mods(void) { oneshot_mods_changed_kb(oneshot_mods); } } -/** \brief get oneshot mods - * - * FIXME: needs doc - */ -uint8_t get_oneshot_mods(void) { return oneshot_mods; } #endif /** \brief Called when the one shot modifiers have been changed. diff --git a/tmk_core/common/action_util.h b/tmk_core/common/action_util.h index 5dd8393da48e..743ff1406b46 100644 --- a/tmk_core/common/action_util.h +++ b/tmk_core/common/action_util.h @@ -57,12 +57,11 @@ void set_macro_mods(uint8_t mods); void clear_macro_mods(void); /* oneshot modifier */ -void set_oneshot_mods(uint8_t mods); uint8_t get_oneshot_mods(void); +void add_oneshot_mods(uint8_t mods); +void del_oneshot_mods(uint8_t mods); +void set_oneshot_mods(uint8_t mods); void clear_oneshot_mods(void); -void oneshot_toggle(void); -void oneshot_enable(void); -void oneshot_disable(void); bool has_oneshot_mods_timed_out(void); uint8_t get_oneshot_locked_mods(void); From 63d3f3b149b853995b80090f46a58a7263df85d0 Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Thu, 23 Apr 2020 22:57:37 +0800 Subject: [PATCH 6/9] add support for otg --- tmk_core/protocol/chibios/usb_main.c | 74 ++ tmk_core/protocol/chibios/usb_main.new.c | 934 +++++++++++++++++++++++ tmk_core/protocol/usb_descriptor.h | 19 + 3 files changed, 1027 insertions(+) create mode 100644 tmk_core/protocol/chibios/usb_main.new.c diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 59f6feb9519a..ae219a7a35cd 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -165,6 +165,19 @@ static const USBEndpointConfig shared_ep_config = { }; #endif +#if STM32_USB_USE_OTG1 +typedef struct { + size_t queue_capacity_in; + size_t queue_capacity_out; + USBInEndpointState in_ep_state; + USBOutEndpointState out_ep_state; + USBInEndpointState int_ep_state; + USBEndpointConfig inout_ep_config; + USBEndpointConfig int_ep_config; + const QMKUSBConfig config; + QMKUSBDriver driver; +} usb_driver_config_t; +#else typedef struct { size_t queue_capacity_in; size_t queue_capacity_out; @@ -177,7 +190,54 @@ typedef struct { const QMKUSBConfig config; QMKUSBDriver driver; } usb_driver_config_t; +#endif +#if STM32_USB_USE_OTG1 +/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ +#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ + { \ + .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ + .inout_ep_config = \ + { \ + stream##_IN_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbDataTransmitted, /* IN notification callback */ \ + qmkusbDataReceived, /* OUT notification callback */ \ + stream##_EPSIZE, /* IN maximum packet size */ \ + stream##_EPSIZE, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .int_ep_config = \ + { \ + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbInterruptTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .bulk_in = stream##_IN_EPNUM, \ + .bulk_out = stream##_OUT_EPNUM, \ + .int_in = notification, \ + .in_buffers = stream##_IN_CAPACITY, \ + .out_buffers = stream##_OUT_CAPACITY, \ + .in_size = stream##_EPSIZE, \ + .out_size = stream##_EPSIZE, \ + .fixed_size = fixedsize, \ + .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + } \ + } +#else /* Reusable initialization structure - see USBEndpointConfig comment at top of file */ #define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ { \ @@ -235,6 +295,7 @@ typedef struct { .ob = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ } \ } +#endif typedef struct { union { @@ -327,8 +388,12 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); #endif for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].inout_ep_config); + #else usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config); usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config); + #endif if (drivers.array[i].config.int_in) { usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config); } @@ -553,12 +618,21 @@ static const USBConfig usbcfg = { */ void init_usb_driver(USBDriver *usbp) { for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + QMKUSBDriver *driver = &drivers.array[i].driver; + drivers.array[i].inout_ep_config.in_state = &drivers.array[i].in_ep_state; + drivers.array[i].inout_ep_config.out_state = &drivers.array[i].out_ep_state; + drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; + qmkusbObjectInit(driver, &drivers.array[i].config); + qmkusbStart(driver, &drivers.array[i].config); + #else QMKUSBDriver *driver = &drivers.array[i].driver; drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state; drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state; drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; qmkusbObjectInit(driver, &drivers.array[i].config); qmkusbStart(driver, &drivers.array[i].config); + #endif } /* diff --git a/tmk_core/protocol/chibios/usb_main.new.c b/tmk_core/protocol/chibios/usb_main.new.c new file mode 100644 index 000000000000..fa08f54a1df7 --- /dev/null +++ b/tmk_core/protocol/chibios/usb_main.new.c @@ -0,0 +1,934 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + +/* + * Implementation notes: + * + * USBEndpointConfig - Configured using explicit order instead of struct member name. + * This is due to ChibiOS hal LLD differences, which is dependent on hardware, + * "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`. + * Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file + * makes the assumption this is safe to avoid littering with preprocessor directives. + */ + +#include "ch.h" +#include "hal.h" + +#include "usb_main.h" + +#include "host.h" +#include "debug.h" +#include "suspend.h" +#ifdef SLEEP_LED_ENABLE +# include "sleep_led.h" +# include "led.h" +#endif +#include "wait.h" +#include "usb_descriptor.h" +#include "usb_driver.h" + +#ifdef NKRO_ENABLE +# include "keycode_config.h" + +extern keymap_config_t keymap_config; +#endif + +/* --------------------------------------------------------- + * Global interface variables and declarations + * --------------------------------------------------------- + */ + +#ifndef usb_lld_connect_bus +# define usb_lld_connect_bus(usbp) +#endif + +#ifndef usb_lld_disconnect_bus +# define usb_lld_disconnect_bus(usbp) +#endif + +uint8_t keyboard_idle __attribute__((aligned(2))) = 0; +uint8_t keyboard_protocol __attribute__((aligned(2))) = 1; +uint16_t keyboard_led_stats __attribute__((aligned(2))) = 0; +volatile uint16_t keyboard_idle_count = 0; +static virtual_timer_t keyboard_idle_timer; +static void keyboard_idle_timer_cb(void *arg); + +report_keyboard_t keyboard_report_sent = {{0}}; +#ifdef MOUSE_ENABLE +report_mouse_t mouse_report_blank = {0}; +#endif /* MOUSE_ENABLE */ +#ifdef EXTRAKEY_ENABLE +uint8_t extra_report_blank[3] = {0}; +#endif /* EXTRAKEY_ENABLE */ + +/* --------------------------------------------------------- + * Descriptors and USB driver objects + * --------------------------------------------------------- + */ + +/* HID specific constants */ +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +/* + * Handles the GET_DESCRIPTOR callback + * + * Returns the proper descriptor + */ +static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) { + (void)usbp; + static USBDescriptor desc; + uint16_t wValue = ((uint16_t)dtype << 8) | dindex; + desc.ud_string = NULL; + desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const) & desc.ud_string); + if (desc.ud_string == NULL) + return NULL; + else + return &desc; +} + +#ifndef KEYBOARD_SHARED_EP +/* keyboard endpoint state structure */ +static USBInEndpointState kbd_ep_state; +/* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ +static const USBEndpointConfig kbd_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + kbd_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + KEYBOARD_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &kbd_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif + +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) +/* mouse endpoint state structure */ +static USBInEndpointState mouse_ep_state; + +/* mouse endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ +static const USBEndpointConfig mouse_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + mouse_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + MOUSE_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &mouse_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif + +#ifdef SHARED_EP_ENABLE +/* shared endpoint state structure */ +static USBInEndpointState shared_ep_state; + +/* shared endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ +static const USBEndpointConfig shared_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + shared_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + SHARED_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &shared_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif + +#if STM32_USB_USE_OTG1 +typedef struct { + size_t queue_capacity_in; + size_t queue_capacity_out; + USBInEndpointState in_ep_state; + USBOutEndpointState out_ep_state; + USBInEndpointState int_ep_state; + USBEndpointConfig inout_ep_config; + USBEndpointConfig int_ep_config; + const QMKUSBConfig config; + QMKUSBDriver driver; +} usb_driver_config_t; +#else +typedef struct { + size_t queue_capacity_in; + size_t queue_capacity_out; + USBInEndpointState in_ep_state; + USBOutEndpointState out_ep_state; + USBInEndpointState int_ep_state; + USBEndpointConfig in_ep_config; + USBEndpointConfig out_ep_config; + USBEndpointConfig int_ep_config; + const QMKUSBConfig config; + QMKUSBDriver driver; +} usb_driver_config_t; +#endif + +#if STM32_USB_USE_OTG1 +/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ +#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ + { \ + .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ + .inout_ep_config = \ + { \ + stream##_IN_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbDataTransmitted, /* IN notification callback */ \ + qmkusbDataReceived, /* OUT notification callback */ \ + stream##_EPSIZE, /* IN maximum packet size */ \ + stream##_EPSIZE, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .int_ep_config = \ + { \ + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbInterruptTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .bulk_in = stream##_IN_EPNUM, \ + .bulk_out = stream##_OUT_EPNUM, \ + .int_in = notification, \ + .in_buffers = stream##_IN_CAPACITY, \ + .out_buffers = stream##_OUT_CAPACITY, \ + .in_size = stream##_EPSIZE, \ + .out_size = stream##_EPSIZE, \ + .fixed_size = fixedsize, \ + .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + } \ + } +#else +/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ +#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ + { \ + .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ + .in_ep_config = \ + { \ + stream##_IN_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbDataTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + stream##_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .out_ep_config = \ + { \ + stream##_OUT_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + NULL, /* IN notification callback */ \ + qmkusbDataReceived, /* OUT notification callback */ \ + 0, /* IN maximum packet size */ \ + stream##_EPSIZE, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .int_ep_config = \ + { \ + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbInterruptTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .bulk_in = stream##_IN_EPNUM, \ + .bulk_out = stream##_OUT_EPNUM, \ + .int_in = notification, \ + .in_buffers = stream##_IN_CAPACITY, \ + .out_buffers = stream##_OUT_CAPACITY, \ + .in_size = stream##_EPSIZE, \ + .out_size = stream##_EPSIZE, \ + .fixed_size = fixedsize, \ + .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + } \ + } +#endif + +typedef struct { + union { + struct { +#ifdef CONSOLE_ENABLE + usb_driver_config_t console_driver; +#endif +#if defined(RAW_ENABLE) + usb_driver_config_t raw_driver; +#endif +#ifdef MIDI_ENABLE + usb_driver_config_t midi_driver; +#endif +#ifdef VIRTSER_ENABLE + usb_driver_config_t serial_driver; +#endif + }; + usb_driver_config_t array[0]; + }; +} usb_driver_configs_t; + +static usb_driver_configs_t drivers = { +#ifdef CONSOLE_ENABLE +# define CONSOLE_IN_CAPACITY 4 +# define CONSOLE_OUT_CAPACITY 4 +# define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR +# define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR + .console_driver = QMK_USB_DRIVER_CONFIG(CONSOLE, 0, true), +#endif +#if defined(RAW_ENABLE) +# define RAW_IN_CAPACITY 4 +# define RAW_OUT_CAPACITY 4 +# define RAW_IN_MODE USB_EP_MODE_TYPE_INTR +# define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR + .raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false), + +#endif + +#ifdef MIDI_ENABLE +# define MIDI_STREAM_IN_CAPACITY 4 +# define MIDI_STREAM_OUT_CAPACITY 4 +# define MIDI_STREAM_IN_MODE USB_EP_MODE_TYPE_BULK +# define MIDI_STREAM_OUT_MODE USB_EP_MODE_TYPE_BULK + .midi_driver = QMK_USB_DRIVER_CONFIG(MIDI_STREAM, 0, false), +#endif + +#ifdef VIRTSER_ENABLE +# define CDC_IN_CAPACITY 4 +# define CDC_OUT_CAPACITY 4 +# define CDC_IN_MODE USB_EP_MODE_TYPE_BULK +# define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK + .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false), +#endif +}; + +#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t)) + +/* --------------------------------------------------------- + * USB driver functions + * --------------------------------------------------------- + */ + +/* 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) { + switch (event) { + case USB_EVENT_ADDRESS: + return; + + case USB_EVENT_CONFIGURED: + osalSysLockFromISR(); + /* Enable the endpoints specified into the configuration. */ +#ifndef KEYBOARD_SHARED_EP + usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config); +#endif +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) + usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config); +#endif +#ifdef SHARED_EP_ENABLE + usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); +#endif + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].inout_ep_config); + #else + usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config); + usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config); + #endif + if (drivers.array[i].config.int_in) { + usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config); + } + qmkusbConfigureHookI(&drivers.array[i].driver); + } + osalSysUnlockFromISR(); + return; + case USB_EVENT_SUSPEND: +#ifdef SLEEP_LED_ENABLE + sleep_led_enable(); +#endif /* SLEEP_LED_ENABLE */ + /* Falls into.*/ + case USB_EVENT_UNCONFIGURED: + /* Falls into.*/ + case USB_EVENT_RESET: + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + chSysLockFromISR(); + /* Disconnection event on suspend.*/ + qmkusbSuspendHookI(&drivers.array[i].driver); + chSysUnlockFromISR(); + } + return; + + case USB_EVENT_WAKEUP: + // TODO: from ISR! print("[W]"); + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + chSysLockFromISR(); + /* Disconnection event on suspend.*/ + 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 */ + return; + + case USB_EVENT_STALLED: + return; + } +} + +/* Function used locally in os/hal/src/usb.c for getting descriptors + * need it here for HID descriptor */ +static uint16_t get_hword(uint8_t *p) { + uint16_t hw; + + hw = (uint16_t)*p++; + hw |= (uint16_t)*p << 8U; + return hw; +} + +/* + * Appendix G: HID Request Support Requirements + * + * The following table enumerates the requests that need to be supported by various types of HID class devices. + * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol + * ------------------------------------------------------------------------------------------ + * Boot Mouse Required Optional Optional Optional Required Required + * Non-Boot Mouse Required Optional Optional Optional Optional Optional + * Boot Keyboard Required Optional Required Required Required Required + * Non-Boot Keybrd Required Optional Required Required Optional Optional + * Other Device Required Optional Optional Optional Optional Optional + */ + +#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) +static uint8_t set_report_buf[2] __attribute__((aligned(2))); +static void set_led_transfer_cb(USBDriver *usbp) { + if ((set_report_buf[0] == REPORT_ID_KEYBOARD) || (set_report_buf[0] == REPORT_ID_NKRO)) { + keyboard_led_stats = set_report_buf[1]; + } +} +#endif + +/* Callback for SETUP request on the endpoint 0 (control) */ +static bool usb_request_hook_cb(USBDriver *usbp) { + const USBDescriptor *dp; + + /* usbp->setup fields: + * 0: bmRequestType (bitmask) + * 1: bRequest + * 2,3: (LSB,MSB) wValue + * 4,5: (LSB,MSB) wIndex + * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ + + /* Handle HID class specific requests */ + if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { + switch (usbp->setup[0] & USB_RTYPE_DIR_MASK) { + case USB_RTYPE_DIR_DEV2HOST: + switch (usbp->setup[1]) { /* bRequest */ + case HID_GET_REPORT: + switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ + case KEYBOARD_INTERFACE: + usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); + return TRUE; + break; + +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) + case MOUSE_INTERFACE: + usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); + return TRUE; + break; +#endif + + default: + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + + case HID_GET_PROTOCOL: + if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); + return TRUE; + } + break; + + case HID_GET_IDLE: + usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); + return TRUE; + break; + } + break; + + case USB_RTYPE_DIR_HOST2DEV: + switch (usbp->setup[1]) { /* bRequest */ + case HID_SET_REPORT: + switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */ +#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) + case SHARED_INTERFACE: + usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb); + return TRUE; + break; +#endif + case KEYBOARD_INTERFACE: + /* keyboard_led_stats = + * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */ + usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL); + return TRUE; + break; + } + break; + + case HID_SET_PROTOCOL: + if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ +#ifdef NKRO_ENABLE + keymap_config.nkro = !!keyboard_protocol; + if (!keymap_config.nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if (keyboard_idle) { +#endif /* NKRO_ENABLE */ + /* arm the idle timer if boot protocol & idle */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + osalSysUnlockFromISR(); + } + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + + case HID_SET_IDLE: + keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ + /* arm the timer */ +#ifdef NKRO_ENABLE + if (!keymap_config.nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if (keyboard_idle) { +#endif /* NKRO_ENABLE */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + osalSysUnlockFromISR(); + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + } + } + + /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ + if ((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { + dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); + if (dp == NULL) return FALSE; + usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); + return TRUE; + } + + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + if (drivers.array[i].config.int_in) { + // NOTE: Assumes that we only have one serial driver + return qmkusbRequestsHook(usbp); + } + } + + return FALSE; +} + +/* Start-of-frame callback */ +static void usb_sof_cb(USBDriver *usbp) { + kbd_sof_cb(usbp); + osalSysLockFromISR(); + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + qmkusbSOFHookI(&drivers.array[i].driver); + } + osalSysUnlockFromISR(); +} + +/* USB driver configuration */ +static const USBConfig usbcfg = { + usb_event_cb, /* USB events callback */ + usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ + usb_request_hook_cb, /* Requests hook callback */ + usb_sof_cb /* Start Of Frame callback */ +}; + +/* + * Initialize the USB driver + */ +void init_usb_driver(USBDriver *usbp) { + for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + QMKUSBDriver *driver = &drivers.array[i].driver; + drivers.array[i].inout_ep_config.in_state = &drivers.array[i].in_ep_state; + drivers.array[i].inout_ep_config.out_state = &drivers.array[i].out_ep_state; + drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; + qmkusbObjectInit(driver, &drivers.array[i].config); + qmkusbStart(driver, &drivers.array[i].config); + #else + QMKUSBDriver *driver = &drivers.array[i].driver; + drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state; + drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state; + drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; + qmkusbObjectInit(driver, &drivers.array[i].config); + qmkusbStart(driver, &drivers.array[i].config); + #endif + } + /* + * Activates the USB driver and then the USB bus pull-up on D+. + * Note, a delay is inserted in order to not have to disconnect the cable + * after a reset. + */ + usbDisconnectBus(usbp); + wait_ms(1500); + usbStart(usbp, &usbcfg); + usbConnectBus(usbp); + + chVTObjectInit(&keyboard_idle_timer); +} + +/* --------------------------------------------------------- + * Keyboard functions + * --------------------------------------------------------- + */ +/* keyboard IN callback hander (a kbd report has made it IN) */ +#ifndef KEYBOARD_SHARED_EP +void kbd_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} +#endif + +/* start-of-frame handler + * TODO: i guess it would be better to re-implement using timers, + * so that this is not going to have to be checked every 1ms */ +void kbd_sof_cb(USBDriver *usbp) { (void)usbp; } + +/* Idle requests timer code + * callback (called from ISR, unlocked state) */ +static void keyboard_idle_timer_cb(void *arg) { + USBDriver *usbp = (USBDriver *)arg; + + osalSysLockFromISR(); + + /* check that the states of things are as they're supposed to */ + if (usbGetDriverStateI(usbp) != USB_ACTIVE) { + /* do not rearm the timer, should be enabled on IDLE request */ + osalSysUnlockFromISR(); + return; + } + +#ifdef NKRO_ENABLE + if (!keymap_config.nkro && keyboard_idle && keyboard_protocol) { +#else /* NKRO_ENABLE */ + if (keyboard_idle && keyboard_protocol) { +#endif /* NKRO_ENABLE */ + /* TODO: are we sure we want the KBD_ENDPOINT? */ + if (!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) { + usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE); + } + /* rearm the timer */ + chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + } + + /* do not rearm the timer if the condition above fails + * it should be enabled again on either IDLE or SET_PROTOCOL requests */ + osalSysUnlockFromISR(); +} + +/* LED status */ +uint8_t keyboard_leds(void) { return (uint8_t)(keyboard_led_stats & 0xFF); } + +/* prepare and start sending a report IN + * not callable from ISR or locked state */ +void send_keyboard(report_keyboard_t *report) { + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + goto unlock; + } + +#ifdef NKRO_ENABLE + if (keymap_config.nkro && keyboard_protocol) { /* NKRO protocol */ + /* need to wait until the previous packet has made it through */ + /* can rewrite this using the synchronous API, then would wait + * until *after* the packet has been transmitted. I think + * this is more efficient */ + /* busy wait, should be short and not very common */ + if (usbGetTransmitStatusI(&USB_DRIVER, SHARED_IN_EPNUM)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[SHARED_IN_EPNUM]->in_state->thread); + + /* after osalThreadSuspendS returns USB status might have changed */ + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + goto unlock; + } + } + usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report)); + } else +#endif /* NKRO_ENABLE */ + { /* regular protocol */ + /* need to wait until the previous packet has made it through */ + /* busy wait, should be short and not very common */ + if (usbGetTransmitStatusI(&USB_DRIVER, KEYBOARD_IN_EPNUM)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[KEYBOARD_IN_EPNUM]->in_state->thread); + + /* after osalThreadSuspendS returns USB status might have changed */ + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + goto unlock; + } + } + uint8_t *data, size; + if (keyboard_protocol) { + data = (uint8_t *)report; + size = KEYBOARD_REPORT_SIZE; + } else { /* boot protocol */ + data = &report->mods; + size = 8; + } + usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, data, size); + } + keyboard_report_sent = *report; + +unlock: + osalSysUnlock(); +} + +/* --------------------------------------------------------- + * Mouse functions + * --------------------------------------------------------- + */ + +#ifdef MOUSE_ENABLE + +# ifndef MOUSE_SHARED_EP +/* mouse IN callback hander (a mouse report has made it IN) */ +void mouse_in_cb(USBDriver *usbp, usbep_t ep) { + (void)usbp; + (void)ep; +} +# endif + +void send_mouse(report_mouse_t *report) { + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + if (usbGetTransmitStatusI(&USB_DRIVER, MOUSE_IN_EPNUM)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[MOUSE_IN_EPNUM]->in_state->thread, MS2ST(10)) == MSG_TIMEOUT) { + osalSysUnlock(); + return; + } + } + usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t)); + osalSysUnlock(); +} + +#else /* MOUSE_ENABLE */ +void send_mouse(report_mouse_t *report) { (void)report; } +#endif /* MOUSE_ENABLE */ + +/* --------------------------------------------------------- + * Shared EP functions + * --------------------------------------------------------- + */ +#ifdef SHARED_EP_ENABLE +/* shared IN callback hander */ +void shared_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} +#endif + +/* --------------------------------------------------------- + * Extrakey functions + * --------------------------------------------------------- + */ + +#ifdef EXTRAKEY_ENABLE +static void send_extra_report(uint8_t report_id, uint16_t data) { + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + report_extra_t report = {.report_id = report_id, .usage = data}; + + usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t)); + osalSysUnlock(); +} + +void send_system(uint16_t data) { send_extra_report(REPORT_ID_SYSTEM, data); } + +void send_consumer(uint16_t data) { send_extra_report(REPORT_ID_CONSUMER, data); } + +#else /* EXTRAKEY_ENABLE */ +void send_system(uint16_t data) { (void)data; } +void send_consumer(uint16_t data) { (void)data; } +#endif /* EXTRAKEY_ENABLE */ + +/* --------------------------------------------------------- + * Console functions + * --------------------------------------------------------- + */ + +#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); +} + +// Just a dummy function for now, this could be exposed as a weak function +// Or connected to the actual QMK console +static void console_receive(uint8_t *data, uint8_t length) { + (void)data; + (void)length; +} + +void console_task(void) { + uint8_t buffer[CONSOLE_EPSIZE]; + size_t size = 0; + do { + size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); + if (size > 0) { + console_receive(buffer, size); + } + } while (size > 0); +} + +#else /* CONSOLE_ENABLE */ +int8_t sendchar(uint8_t c) { + (void)c; + return 0; +} +#endif /* CONSOLE_ENABLE */ + +void sendchar_pf(void *p, char c) { + (void)p; + sendchar((uint8_t)c); +} + +#ifdef RAW_ENABLE +void raw_hid_send(uint8_t *data, uint8_t length) { + // TODO: implement variable size packet + if (length != RAW_EPSIZE) { + return; + } + chnWrite(&drivers.raw_driver.driver, data, length); +} + +__attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) { + // Users should #include "raw_hid.h" in their own code + // and implement this function there. Leave this as weak linkage + // so users can opt to not handle data coming in. +} + +void raw_hid_task(void) { + uint8_t buffer[RAW_EPSIZE]; + size_t size = 0; + do { + size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); + if (size > 0) { + raw_hid_receive(buffer, size); + } + } while (size > 0); +} + +#endif + +#ifdef MIDI_ENABLE + +void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); } + +bool recv_midi_packet(MIDI_EventPacket_t *const event) { + size_t size = chnReadTimeout(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t), TIME_IMMEDIATE); + return size == sizeof(MIDI_EventPacket_t); +} + +#endif + +#ifdef VIRTSER_ENABLE + +void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); } + +__attribute__((weak)) void virtser_recv(uint8_t c) { + // Ignore by default +} + +void virtser_task(void) { + uint8_t numBytesReceived = 0; + uint8_t buffer[16]; + do { + numBytesReceived = chnReadTimeout(&drivers.serial_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); + for (int i = 0; i < numBytesReceived; i++) { + virtser_recv(buffer[i]); + } + } while (numBytesReceived > 0); +} + +#endif diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 02a4b1ce5923..71d45cd4b169 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -205,7 +205,12 @@ enum usb_endpoints { #ifdef RAW_ENABLE RAW_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + #define RAW_OUT_EPNUM RAW_IN_EPNUM + #else RAW_OUT_EPNUM = NEXT_EPNUM, + #endif + #endif #ifdef SHARED_EP_ENABLE @@ -219,7 +224,11 @@ enum usb_endpoints { // ChibiOS has enough memory and descriptor to actually enable the endpoint // It could use the same endpoint numbers, as that's supported by ChibiOS // But the QMK code currently assumes that the endpoint numbers are different + #if STM32_USB_USE_OTG1 + CONSOLE_OUT_EPNUM = CONSOLE_IN_EPNUM, + #else CONSOLE_OUT_EPNUM = NEXT_EPNUM, + #endif # else # define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM # endif @@ -227,17 +236,27 @@ enum usb_endpoints { #ifdef MIDI_ENABLE MIDI_STREAM_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + MIDI_STREAM_OUT_EPNUM = MIDI_STREAM_IN_EPNUM, + #else MIDI_STREAM_OUT_EPNUM = NEXT_EPNUM, #endif #ifdef VIRTSER_ENABLE CDC_NOTIFICATION_EPNUM = NEXT_EPNUM, CDC_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + CDC_OUT_EPNUM = CDC_IN_EPNUM, + #else CDC_OUT_EPNUM = NEXT_EPNUM, #endif #ifdef JOYSTICK_ENABLE JOYSTICK_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + JOYSTICK_OUT_EPNUM = JOYSTICK_IN_EPNUM, + #else JOYSTICK_OUT_EPNUM = NEXT_EPNUM, + #endif #endif }; From 2a5a42661a37798ad33e07be47eef1a07d2a1002 Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Thu, 23 Apr 2020 23:45:18 +0800 Subject: [PATCH 7/9] update endpoint numbering for stm32f4 --- tmk_core/protocol/chibios/usb_main.c | 4 ++-- tmk_core/protocol/usb_descriptor.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index ae219a7a35cd..bb4bf6a58079 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -233,8 +233,8 @@ typedef struct { .in_size = stream##_EPSIZE, \ .out_size = stream##_EPSIZE, \ .fixed_size = fixedsize, \ - .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ - .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + .ib = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ } \ } #else diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 71d45cd4b169..61d3f72d1aa0 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -225,7 +225,7 @@ enum usb_endpoints { // It could use the same endpoint numbers, as that's supported by ChibiOS // But the QMK code currently assumes that the endpoint numbers are different #if STM32_USB_USE_OTG1 - CONSOLE_OUT_EPNUM = CONSOLE_IN_EPNUM, + #define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM #else CONSOLE_OUT_EPNUM = NEXT_EPNUM, #endif @@ -237,7 +237,7 @@ enum usb_endpoints { #ifdef MIDI_ENABLE MIDI_STREAM_IN_EPNUM = NEXT_EPNUM, #if STM32_USB_USE_OTG1 - MIDI_STREAM_OUT_EPNUM = MIDI_STREAM_IN_EPNUM, + #define MIDI_STREAM_OUT_EPNUM MIDI_STREAM_IN_EPNUM #else MIDI_STREAM_OUT_EPNUM = NEXT_EPNUM, #endif @@ -246,7 +246,7 @@ enum usb_endpoints { CDC_NOTIFICATION_EPNUM = NEXT_EPNUM, CDC_IN_EPNUM = NEXT_EPNUM, #if STM32_USB_USE_OTG1 - CDC_OUT_EPNUM = CDC_IN_EPNUM, + #define CDC_OUT_EPNUM CDC_IN_EPNUM #else CDC_OUT_EPNUM = NEXT_EPNUM, #endif From dcdade7c765689dc0bc02d02b143b15c05556c95 Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Thu, 23 Apr 2020 23:56:22 +0800 Subject: [PATCH 8/9] removed testing file --- tmk_core/protocol/chibios/usb_main.new.c | 934 ----------------------- 1 file changed, 934 deletions(-) delete mode 100644 tmk_core/protocol/chibios/usb_main.new.c diff --git a/tmk_core/protocol/chibios/usb_main.new.c b/tmk_core/protocol/chibios/usb_main.new.c deleted file mode 100644 index fa08f54a1df7..000000000000 --- a/tmk_core/protocol/chibios/usb_main.new.c +++ /dev/null @@ -1,934 +0,0 @@ -/* - * (c) 2015 flabberast - * - * Based on the following work: - * - Guillaume Duc's raw hid example (MIT License) - * https://github.com/guiduc/usb-hid-chibios-example - * - PJRC Teensy examples (MIT License) - * https://www.pjrc.com/teensy/usb_keyboard.html - * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) - * https://github.com/tmk/tmk_keyboard/ - * - ChibiOS demo code (Apache 2.0 License) - * http://www.chibios.org - * - * Since some GPL'd code is used, this work is licensed under - * GPL v2 or later. - */ - -/* - * Implementation notes: - * - * USBEndpointConfig - Configured using explicit order instead of struct member name. - * This is due to ChibiOS hal LLD differences, which is dependent on hardware, - * "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`. - * Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file - * makes the assumption this is safe to avoid littering with preprocessor directives. - */ - -#include "ch.h" -#include "hal.h" - -#include "usb_main.h" - -#include "host.h" -#include "debug.h" -#include "suspend.h" -#ifdef SLEEP_LED_ENABLE -# include "sleep_led.h" -# include "led.h" -#endif -#include "wait.h" -#include "usb_descriptor.h" -#include "usb_driver.h" - -#ifdef NKRO_ENABLE -# include "keycode_config.h" - -extern keymap_config_t keymap_config; -#endif - -/* --------------------------------------------------------- - * Global interface variables and declarations - * --------------------------------------------------------- - */ - -#ifndef usb_lld_connect_bus -# define usb_lld_connect_bus(usbp) -#endif - -#ifndef usb_lld_disconnect_bus -# define usb_lld_disconnect_bus(usbp) -#endif - -uint8_t keyboard_idle __attribute__((aligned(2))) = 0; -uint8_t keyboard_protocol __attribute__((aligned(2))) = 1; -uint16_t keyboard_led_stats __attribute__((aligned(2))) = 0; -volatile uint16_t keyboard_idle_count = 0; -static virtual_timer_t keyboard_idle_timer; -static void keyboard_idle_timer_cb(void *arg); - -report_keyboard_t keyboard_report_sent = {{0}}; -#ifdef MOUSE_ENABLE -report_mouse_t mouse_report_blank = {0}; -#endif /* MOUSE_ENABLE */ -#ifdef EXTRAKEY_ENABLE -uint8_t extra_report_blank[3] = {0}; -#endif /* EXTRAKEY_ENABLE */ - -/* --------------------------------------------------------- - * Descriptors and USB driver objects - * --------------------------------------------------------- - */ - -/* HID specific constants */ -#define HID_GET_REPORT 0x01 -#define HID_GET_IDLE 0x02 -#define HID_GET_PROTOCOL 0x03 -#define HID_SET_REPORT 0x09 -#define HID_SET_IDLE 0x0A -#define HID_SET_PROTOCOL 0x0B - -/* - * Handles the GET_DESCRIPTOR callback - * - * Returns the proper descriptor - */ -static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) { - (void)usbp; - static USBDescriptor desc; - uint16_t wValue = ((uint16_t)dtype << 8) | dindex; - desc.ud_string = NULL; - desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const) & desc.ud_string); - if (desc.ud_string == NULL) - return NULL; - else - return &desc; -} - -#ifndef KEYBOARD_SHARED_EP -/* keyboard endpoint state structure */ -static USBInEndpointState kbd_ep_state; -/* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig kbd_ep_config = { - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ - NULL, /* SETUP packet notification callback */ - kbd_in_cb, /* IN notification callback */ - NULL, /* OUT notification callback */ - KEYBOARD_EPSIZE, /* IN maximum packet size */ - 0, /* OUT maximum packet size */ - &kbd_ep_state, /* IN Endpoint state */ - NULL, /* OUT endpoint state */ - 2, /* IN multiplier */ - NULL /* SETUP buffer (not a SETUP endpoint) */ -}; -#endif - -#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) -/* mouse endpoint state structure */ -static USBInEndpointState mouse_ep_state; - -/* mouse endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig mouse_ep_config = { - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ - NULL, /* SETUP packet notification callback */ - mouse_in_cb, /* IN notification callback */ - NULL, /* OUT notification callback */ - MOUSE_EPSIZE, /* IN maximum packet size */ - 0, /* OUT maximum packet size */ - &mouse_ep_state, /* IN Endpoint state */ - NULL, /* OUT endpoint state */ - 2, /* IN multiplier */ - NULL /* SETUP buffer (not a SETUP endpoint) */ -}; -#endif - -#ifdef SHARED_EP_ENABLE -/* shared endpoint state structure */ -static USBInEndpointState shared_ep_state; - -/* shared endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig shared_ep_config = { - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ - NULL, /* SETUP packet notification callback */ - shared_in_cb, /* IN notification callback */ - NULL, /* OUT notification callback */ - SHARED_EPSIZE, /* IN maximum packet size */ - 0, /* OUT maximum packet size */ - &shared_ep_state, /* IN Endpoint state */ - NULL, /* OUT endpoint state */ - 2, /* IN multiplier */ - NULL /* SETUP buffer (not a SETUP endpoint) */ -}; -#endif - -#if STM32_USB_USE_OTG1 -typedef struct { - size_t queue_capacity_in; - size_t queue_capacity_out; - USBInEndpointState in_ep_state; - USBOutEndpointState out_ep_state; - USBInEndpointState int_ep_state; - USBEndpointConfig inout_ep_config; - USBEndpointConfig int_ep_config; - const QMKUSBConfig config; - QMKUSBDriver driver; -} usb_driver_config_t; -#else -typedef struct { - size_t queue_capacity_in; - size_t queue_capacity_out; - USBInEndpointState in_ep_state; - USBOutEndpointState out_ep_state; - USBInEndpointState int_ep_state; - USBEndpointConfig in_ep_config; - USBEndpointConfig out_ep_config; - USBEndpointConfig int_ep_config; - const QMKUSBConfig config; - QMKUSBDriver driver; -} usb_driver_config_t; -#endif - -#if STM32_USB_USE_OTG1 -/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ -#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ - { \ - .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ - .inout_ep_config = \ - { \ - stream##_IN_MODE, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - qmkusbDataTransmitted, /* IN notification callback */ \ - qmkusbDataReceived, /* OUT notification callback */ \ - stream##_EPSIZE, /* IN maximum packet size */ \ - stream##_EPSIZE, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .int_ep_config = \ - { \ - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - qmkusbInterruptTransmitted, /* IN notification callback */ \ - NULL, /* OUT notification callback */ \ - CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ - 0, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL, /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .config = { \ - .usbp = &USB_DRIVER, \ - .bulk_in = stream##_IN_EPNUM, \ - .bulk_out = stream##_OUT_EPNUM, \ - .int_in = notification, \ - .in_buffers = stream##_IN_CAPACITY, \ - .out_buffers = stream##_OUT_CAPACITY, \ - .in_size = stream##_EPSIZE, \ - .out_size = stream##_EPSIZE, \ - .fixed_size = fixedsize, \ - .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ - .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ - } \ - } -#else -/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ -#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ - { \ - .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ - .in_ep_config = \ - { \ - stream##_IN_MODE, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - qmkusbDataTransmitted, /* IN notification callback */ \ - NULL, /* OUT notification callback */ \ - stream##_EPSIZE, /* IN maximum packet size */ \ - 0, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .out_ep_config = \ - { \ - stream##_OUT_MODE, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - NULL, /* IN notification callback */ \ - qmkusbDataReceived, /* OUT notification callback */ \ - 0, /* IN maximum packet size */ \ - stream##_EPSIZE, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL, /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .int_ep_config = \ - { \ - USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ - NULL, /* SETUP packet notification callback */ \ - qmkusbInterruptTransmitted, /* IN notification callback */ \ - NULL, /* OUT notification callback */ \ - CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ - 0, /* OUT maximum packet size */ \ - NULL, /* IN Endpoint state */ \ - NULL, /* OUT endpoint state */ \ - 2, /* IN multiplier */ \ - NULL, /* SETUP buffer (not a SETUP endpoint) */ \ - }, \ - .config = { \ - .usbp = &USB_DRIVER, \ - .bulk_in = stream##_IN_EPNUM, \ - .bulk_out = stream##_OUT_EPNUM, \ - .int_in = notification, \ - .in_buffers = stream##_IN_CAPACITY, \ - .out_buffers = stream##_OUT_CAPACITY, \ - .in_size = stream##_EPSIZE, \ - .out_size = stream##_EPSIZE, \ - .fixed_size = fixedsize, \ - .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ - .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ - } \ - } -#endif - -typedef struct { - union { - struct { -#ifdef CONSOLE_ENABLE - usb_driver_config_t console_driver; -#endif -#if defined(RAW_ENABLE) - usb_driver_config_t raw_driver; -#endif -#ifdef MIDI_ENABLE - usb_driver_config_t midi_driver; -#endif -#ifdef VIRTSER_ENABLE - usb_driver_config_t serial_driver; -#endif - }; - usb_driver_config_t array[0]; - }; -} usb_driver_configs_t; - -static usb_driver_configs_t drivers = { -#ifdef CONSOLE_ENABLE -# define CONSOLE_IN_CAPACITY 4 -# define CONSOLE_OUT_CAPACITY 4 -# define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR -# define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR - .console_driver = QMK_USB_DRIVER_CONFIG(CONSOLE, 0, true), -#endif -#if defined(RAW_ENABLE) -# define RAW_IN_CAPACITY 4 -# define RAW_OUT_CAPACITY 4 -# define RAW_IN_MODE USB_EP_MODE_TYPE_INTR -# define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR - .raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false), - -#endif - -#ifdef MIDI_ENABLE -# define MIDI_STREAM_IN_CAPACITY 4 -# define MIDI_STREAM_OUT_CAPACITY 4 -# define MIDI_STREAM_IN_MODE USB_EP_MODE_TYPE_BULK -# define MIDI_STREAM_OUT_MODE USB_EP_MODE_TYPE_BULK - .midi_driver = QMK_USB_DRIVER_CONFIG(MIDI_STREAM, 0, false), -#endif - -#ifdef VIRTSER_ENABLE -# define CDC_IN_CAPACITY 4 -# define CDC_OUT_CAPACITY 4 -# define CDC_IN_MODE USB_EP_MODE_TYPE_BULK -# define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK - .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false), -#endif -}; - -#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t)) - -/* --------------------------------------------------------- - * USB driver functions - * --------------------------------------------------------- - */ - -/* 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) { - switch (event) { - case USB_EVENT_ADDRESS: - return; - - case USB_EVENT_CONFIGURED: - osalSysLockFromISR(); - /* Enable the endpoints specified into the configuration. */ -#ifndef KEYBOARD_SHARED_EP - usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config); -#endif -#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) - usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config); -#endif -#ifdef SHARED_EP_ENABLE - usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); -#endif - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - #if STM32_USB_USE_OTG1 - usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].inout_ep_config); - #else - usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config); - usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config); - #endif - if (drivers.array[i].config.int_in) { - usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config); - } - qmkusbConfigureHookI(&drivers.array[i].driver); - } - osalSysUnlockFromISR(); - return; - case USB_EVENT_SUSPEND: -#ifdef SLEEP_LED_ENABLE - sleep_led_enable(); -#endif /* SLEEP_LED_ENABLE */ - /* Falls into.*/ - case USB_EVENT_UNCONFIGURED: - /* Falls into.*/ - case USB_EVENT_RESET: - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - chSysLockFromISR(); - /* Disconnection event on suspend.*/ - qmkusbSuspendHookI(&drivers.array[i].driver); - chSysUnlockFromISR(); - } - return; - - case USB_EVENT_WAKEUP: - // TODO: from ISR! print("[W]"); - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - chSysLockFromISR(); - /* Disconnection event on suspend.*/ - 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 */ - return; - - case USB_EVENT_STALLED: - return; - } -} - -/* Function used locally in os/hal/src/usb.c for getting descriptors - * need it here for HID descriptor */ -static uint16_t get_hword(uint8_t *p) { - uint16_t hw; - - hw = (uint16_t)*p++; - hw |= (uint16_t)*p << 8U; - return hw; -} - -/* - * Appendix G: HID Request Support Requirements - * - * The following table enumerates the requests that need to be supported by various types of HID class devices. - * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol - * ------------------------------------------------------------------------------------------ - * Boot Mouse Required Optional Optional Optional Required Required - * Non-Boot Mouse Required Optional Optional Optional Optional Optional - * Boot Keyboard Required Optional Required Required Required Required - * Non-Boot Keybrd Required Optional Required Required Optional Optional - * Other Device Required Optional Optional Optional Optional Optional - */ - -#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) -static uint8_t set_report_buf[2] __attribute__((aligned(2))); -static void set_led_transfer_cb(USBDriver *usbp) { - if ((set_report_buf[0] == REPORT_ID_KEYBOARD) || (set_report_buf[0] == REPORT_ID_NKRO)) { - keyboard_led_stats = set_report_buf[1]; - } -} -#endif - -/* Callback for SETUP request on the endpoint 0 (control) */ -static bool usb_request_hook_cb(USBDriver *usbp) { - const USBDescriptor *dp; - - /* usbp->setup fields: - * 0: bmRequestType (bitmask) - * 1: bRequest - * 2,3: (LSB,MSB) wValue - * 4,5: (LSB,MSB) wIndex - * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ - - /* Handle HID class specific requests */ - if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { - switch (usbp->setup[0] & USB_RTYPE_DIR_MASK) { - case USB_RTYPE_DIR_DEV2HOST: - switch (usbp->setup[1]) { /* bRequest */ - case HID_GET_REPORT: - switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ - case KEYBOARD_INTERFACE: - usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); - return TRUE; - break; - -#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) - case MOUSE_INTERFACE: - usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); - return TRUE; - break; -#endif - - default: - usbSetupTransfer(usbp, NULL, 0, NULL); - return TRUE; - break; - } - break; - - case HID_GET_PROTOCOL: - if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ - usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); - return TRUE; - } - break; - - case HID_GET_IDLE: - usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); - return TRUE; - break; - } - break; - - case USB_RTYPE_DIR_HOST2DEV: - switch (usbp->setup[1]) { /* bRequest */ - case HID_SET_REPORT: - switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */ -#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) - case SHARED_INTERFACE: - usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb); - return TRUE; - break; -#endif - case KEYBOARD_INTERFACE: - /* keyboard_led_stats = - * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */ - usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL); - return TRUE; - break; - } - break; - - case HID_SET_PROTOCOL: - if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ - keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ -#ifdef NKRO_ENABLE - keymap_config.nkro = !!keyboard_protocol; - if (!keymap_config.nkro && keyboard_idle) { -#else /* NKRO_ENABLE */ - if (keyboard_idle) { -#endif /* NKRO_ENABLE */ - /* arm the idle timer if boot protocol & idle */ - osalSysLockFromISR(); - chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); - osalSysUnlockFromISR(); - } - } - usbSetupTransfer(usbp, NULL, 0, NULL); - return TRUE; - break; - - case HID_SET_IDLE: - keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ - /* arm the timer */ -#ifdef NKRO_ENABLE - if (!keymap_config.nkro && keyboard_idle) { -#else /* NKRO_ENABLE */ - if (keyboard_idle) { -#endif /* NKRO_ENABLE */ - osalSysLockFromISR(); - chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); - osalSysUnlockFromISR(); - } - usbSetupTransfer(usbp, NULL, 0, NULL); - return TRUE; - break; - } - break; - } - } - - /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ - if ((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { - dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); - if (dp == NULL) return FALSE; - usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); - return TRUE; - } - - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - if (drivers.array[i].config.int_in) { - // NOTE: Assumes that we only have one serial driver - return qmkusbRequestsHook(usbp); - } - } - - return FALSE; -} - -/* Start-of-frame callback */ -static void usb_sof_cb(USBDriver *usbp) { - kbd_sof_cb(usbp); - osalSysLockFromISR(); - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - qmkusbSOFHookI(&drivers.array[i].driver); - } - osalSysUnlockFromISR(); -} - -/* USB driver configuration */ -static const USBConfig usbcfg = { - usb_event_cb, /* USB events callback */ - usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ - usb_request_hook_cb, /* Requests hook callback */ - usb_sof_cb /* Start Of Frame callback */ -}; - -/* - * Initialize the USB driver - */ -void init_usb_driver(USBDriver *usbp) { - for (int i = 0; i < NUM_USB_DRIVERS; i++) { - #if STM32_USB_USE_OTG1 - QMKUSBDriver *driver = &drivers.array[i].driver; - drivers.array[i].inout_ep_config.in_state = &drivers.array[i].in_ep_state; - drivers.array[i].inout_ep_config.out_state = &drivers.array[i].out_ep_state; - drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; - qmkusbObjectInit(driver, &drivers.array[i].config); - qmkusbStart(driver, &drivers.array[i].config); - #else - QMKUSBDriver *driver = &drivers.array[i].driver; - drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state; - drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state; - drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; - qmkusbObjectInit(driver, &drivers.array[i].config); - qmkusbStart(driver, &drivers.array[i].config); - #endif - } - /* - * Activates the USB driver and then the USB bus pull-up on D+. - * Note, a delay is inserted in order to not have to disconnect the cable - * after a reset. - */ - usbDisconnectBus(usbp); - wait_ms(1500); - usbStart(usbp, &usbcfg); - usbConnectBus(usbp); - - chVTObjectInit(&keyboard_idle_timer); -} - -/* --------------------------------------------------------- - * Keyboard functions - * --------------------------------------------------------- - */ -/* keyboard IN callback hander (a kbd report has made it IN) */ -#ifndef KEYBOARD_SHARED_EP -void kbd_in_cb(USBDriver *usbp, usbep_t ep) { - /* STUB */ - (void)usbp; - (void)ep; -} -#endif - -/* start-of-frame handler - * TODO: i guess it would be better to re-implement using timers, - * so that this is not going to have to be checked every 1ms */ -void kbd_sof_cb(USBDriver *usbp) { (void)usbp; } - -/* Idle requests timer code - * callback (called from ISR, unlocked state) */ -static void keyboard_idle_timer_cb(void *arg) { - USBDriver *usbp = (USBDriver *)arg; - - osalSysLockFromISR(); - - /* check that the states of things are as they're supposed to */ - if (usbGetDriverStateI(usbp) != USB_ACTIVE) { - /* do not rearm the timer, should be enabled on IDLE request */ - osalSysUnlockFromISR(); - return; - } - -#ifdef NKRO_ENABLE - if (!keymap_config.nkro && keyboard_idle && keyboard_protocol) { -#else /* NKRO_ENABLE */ - if (keyboard_idle && keyboard_protocol) { -#endif /* NKRO_ENABLE */ - /* TODO: are we sure we want the KBD_ENDPOINT? */ - if (!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) { - usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE); - } - /* rearm the timer */ - chVTSetI(&keyboard_idle_timer, 4 * MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); - } - - /* do not rearm the timer if the condition above fails - * it should be enabled again on either IDLE or SET_PROTOCOL requests */ - osalSysUnlockFromISR(); -} - -/* LED status */ -uint8_t keyboard_leds(void) { return (uint8_t)(keyboard_led_stats & 0xFF); } - -/* prepare and start sending a report IN - * not callable from ISR or locked state */ -void send_keyboard(report_keyboard_t *report) { - osalSysLock(); - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - goto unlock; - } - -#ifdef NKRO_ENABLE - if (keymap_config.nkro && keyboard_protocol) { /* NKRO protocol */ - /* need to wait until the previous packet has made it through */ - /* can rewrite this using the synchronous API, then would wait - * until *after* the packet has been transmitted. I think - * this is more efficient */ - /* busy wait, should be short and not very common */ - if (usbGetTransmitStatusI(&USB_DRIVER, SHARED_IN_EPNUM)) { - /* Need to either suspend, or loop and call unlock/lock during - * every iteration - otherwise the system will remain locked, - * no interrupts served, so USB not going through as well. - * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ - osalThreadSuspendS(&(&USB_DRIVER)->epc[SHARED_IN_EPNUM]->in_state->thread); - - /* after osalThreadSuspendS returns USB status might have changed */ - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - goto unlock; - } - } - usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(struct nkro_report)); - } else -#endif /* NKRO_ENABLE */ - { /* regular protocol */ - /* need to wait until the previous packet has made it through */ - /* busy wait, should be short and not very common */ - if (usbGetTransmitStatusI(&USB_DRIVER, KEYBOARD_IN_EPNUM)) { - /* Need to either suspend, or loop and call unlock/lock during - * every iteration - otherwise the system will remain locked, - * no interrupts served, so USB not going through as well. - * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ - osalThreadSuspendS(&(&USB_DRIVER)->epc[KEYBOARD_IN_EPNUM]->in_state->thread); - - /* after osalThreadSuspendS returns USB status might have changed */ - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - goto unlock; - } - } - uint8_t *data, size; - if (keyboard_protocol) { - data = (uint8_t *)report; - size = KEYBOARD_REPORT_SIZE; - } else { /* boot protocol */ - data = &report->mods; - size = 8; - } - usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, data, size); - } - keyboard_report_sent = *report; - -unlock: - osalSysUnlock(); -} - -/* --------------------------------------------------------- - * Mouse functions - * --------------------------------------------------------- - */ - -#ifdef MOUSE_ENABLE - -# ifndef MOUSE_SHARED_EP -/* mouse IN callback hander (a mouse report has made it IN) */ -void mouse_in_cb(USBDriver *usbp, usbep_t ep) { - (void)usbp; - (void)ep; -} -# endif - -void send_mouse(report_mouse_t *report) { - osalSysLock(); - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - osalSysUnlock(); - return; - } - - if (usbGetTransmitStatusI(&USB_DRIVER, MOUSE_IN_EPNUM)) { - /* Need to either suspend, or loop and call unlock/lock during - * every iteration - otherwise the system will remain locked, - * no interrupts served, so USB not going through as well. - * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ - if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[MOUSE_IN_EPNUM]->in_state->thread, MS2ST(10)) == MSG_TIMEOUT) { - osalSysUnlock(); - return; - } - } - usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t)); - osalSysUnlock(); -} - -#else /* MOUSE_ENABLE */ -void send_mouse(report_mouse_t *report) { (void)report; } -#endif /* MOUSE_ENABLE */ - -/* --------------------------------------------------------- - * Shared EP functions - * --------------------------------------------------------- - */ -#ifdef SHARED_EP_ENABLE -/* shared IN callback hander */ -void shared_in_cb(USBDriver *usbp, usbep_t ep) { - /* STUB */ - (void)usbp; - (void)ep; -} -#endif - -/* --------------------------------------------------------- - * Extrakey functions - * --------------------------------------------------------- - */ - -#ifdef EXTRAKEY_ENABLE -static void send_extra_report(uint8_t report_id, uint16_t data) { - osalSysLock(); - if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { - osalSysUnlock(); - return; - } - - report_extra_t report = {.report_id = report_id, .usage = data}; - - usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t)); - osalSysUnlock(); -} - -void send_system(uint16_t data) { send_extra_report(REPORT_ID_SYSTEM, data); } - -void send_consumer(uint16_t data) { send_extra_report(REPORT_ID_CONSUMER, data); } - -#else /* EXTRAKEY_ENABLE */ -void send_system(uint16_t data) { (void)data; } -void send_consumer(uint16_t data) { (void)data; } -#endif /* EXTRAKEY_ENABLE */ - -/* --------------------------------------------------------- - * Console functions - * --------------------------------------------------------- - */ - -#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); -} - -// Just a dummy function for now, this could be exposed as a weak function -// Or connected to the actual QMK console -static void console_receive(uint8_t *data, uint8_t length) { - (void)data; - (void)length; -} - -void console_task(void) { - uint8_t buffer[CONSOLE_EPSIZE]; - size_t size = 0; - do { - size_t size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); - if (size > 0) { - console_receive(buffer, size); - } - } while (size > 0); -} - -#else /* CONSOLE_ENABLE */ -int8_t sendchar(uint8_t c) { - (void)c; - return 0; -} -#endif /* CONSOLE_ENABLE */ - -void sendchar_pf(void *p, char c) { - (void)p; - sendchar((uint8_t)c); -} - -#ifdef RAW_ENABLE -void raw_hid_send(uint8_t *data, uint8_t length) { - // TODO: implement variable size packet - if (length != RAW_EPSIZE) { - return; - } - chnWrite(&drivers.raw_driver.driver, data, length); -} - -__attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) { - // Users should #include "raw_hid.h" in their own code - // and implement this function there. Leave this as weak linkage - // so users can opt to not handle data coming in. -} - -void raw_hid_task(void) { - uint8_t buffer[RAW_EPSIZE]; - size_t size = 0; - do { - size_t size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); - if (size > 0) { - raw_hid_receive(buffer, size); - } - } while (size > 0); -} - -#endif - -#ifdef MIDI_ENABLE - -void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); } - -bool recv_midi_packet(MIDI_EventPacket_t *const event) { - size_t size = chnReadTimeout(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t), TIME_IMMEDIATE); - return size == sizeof(MIDI_EventPacket_t); -} - -#endif - -#ifdef VIRTSER_ENABLE - -void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); } - -__attribute__((weak)) void virtser_recv(uint8_t c) { - // Ignore by default -} - -void virtser_task(void) { - uint8_t numBytesReceived = 0; - uint8_t buffer[16]; - do { - numBytesReceived = chnReadTimeout(&drivers.serial_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); - for (int i = 0; i < numBytesReceived; i++) { - virtser_recv(buffer[i]); - } - } while (numBytesReceived > 0); -} - -#endif From 5ea41ac676e4f2c4e4e61431e4880feefbb1e7de Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Tue, 10 Nov 2020 23:13:45 +0800 Subject: [PATCH 9/9] added missing #endif --- tmk_core/protocol/usb_descriptor.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 61d3f72d1aa0..aa8863f43fb3 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -210,7 +210,6 @@ enum usb_endpoints { #else RAW_OUT_EPNUM = NEXT_EPNUM, #endif - #endif #ifdef SHARED_EP_ENABLE @@ -240,6 +239,7 @@ enum usb_endpoints { #define MIDI_STREAM_OUT_EPNUM MIDI_STREAM_IN_EPNUM #else MIDI_STREAM_OUT_EPNUM = NEXT_EPNUM, + #endif #endif #ifdef VIRTSER_ENABLE @@ -249,6 +249,7 @@ enum usb_endpoints { #define CDC_OUT_EPNUM CDC_IN_EPNUM #else CDC_OUT_EPNUM = NEXT_EPNUM, + #endif #endif #ifdef JOYSTICK_ENABLE JOYSTICK_IN_EPNUM = NEXT_EPNUM,