diff options
Diffstat (limited to 'tmk_core')
| -rw-r--r-- | tmk_core/protocol.mk | 4 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/chibios.c | 43 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/chibios.mk | 2 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/usb_driver.c | 572 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/usb_driver.h | 312 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/usb_endpoints.c | 152 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/usb_endpoints.h | 137 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/usb_main.c | 811 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/usb_main.h | 41 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/usb_report_handling.c | 296 | ||||
| -rw-r--r-- | tmk_core/protocol/chibios/usb_report_handling.h | 77 | ||||
| -rw-r--r-- | tmk_core/protocol/lufa/lufa.c | 13 | ||||
| -rw-r--r-- | tmk_core/protocol/report.h | 8 | ||||
| -rw-r--r-- | tmk_core/protocol/usb_descriptor.h | 2 | ||||
| -rw-r--r-- | tmk_core/protocol/vusb/protocol.c | 30 | ||||
| -rw-r--r-- | tmk_core/protocol/vusb/vusb.c | 10 | 
16 files changed, 1329 insertions, 1181 deletions
| diff --git a/tmk_core/protocol.mk b/tmk_core/protocol.mk index fd5342d637..796b4e8787 100644 --- a/tmk_core/protocol.mk +++ b/tmk_core/protocol.mk @@ -66,6 +66,10 @@ ifeq ($(strip $(NO_USB_STARTUP_CHECK)), yes)      OPT_DEFS += -DNO_USB_STARTUP_CHECK  endif +ifeq ($(strip $(USB_WAIT_FOR_ENUMERATION)), yes) +    OPT_DEFS += -DUSB_WAIT_FOR_ENUMERATION +endif +  ifeq ($(strip $(JOYSTICK_SHARED_EP)), yes)      OPT_DEFS += -DJOYSTICK_SHARED_EP      SHARED_EP_ENABLE = yes diff --git a/tmk_core/protocol/chibios/chibios.c b/tmk_core/protocol/chibios/chibios.c index 91bb252c7c..a249af8d38 100644 --- a/tmk_core/protocol/chibios/chibios.c +++ b/tmk_core/protocol/chibios/chibios.c @@ -51,6 +51,11 @@  #define USB_GETSTATUS_REMOTE_WAKEUP_ENABLED (2U) +#ifdef WAIT_FOR_USB +// TODO: Remove backwards compatibility with old define +#    define USB_WAIT_FOR_ENUMERATION +#endif +  /* -------------------------   *   TMK host driver defs   * ------------------------- @@ -70,17 +75,6 @@ host_driver_t chibios_driver = {keyboard_leds, send_keyboard, send_nkro, send_mo  void virtser_task(void);  #endif -#ifdef RAW_ENABLE -void raw_hid_task(void); -#endif - -#ifdef CONSOLE_ENABLE -void console_task(void); -#endif -#ifdef MIDI_ENABLE -void midi_ep_task(void); -#endif -  /* TESTING   * Amber LED blinker thread, times are in milliseconds.   */ @@ -154,7 +148,7 @@ void protocol_pre_init(void) {      /* Wait until USB is active */      while (true) { -#if defined(WAIT_FOR_USB) +#if defined(USB_WAIT_FOR_ENUMERATION)          if (USB_DRIVER.state == USB_ACTIVE) {              driver = &chibios_driver;              break; @@ -192,30 +186,25 @@ void protocol_pre_task(void) {              /* Remote wakeup */              if ((USB_DRIVER.status & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED) && suspend_wakeup_condition()) {                  usbWakeupHost(&USB_DRIVER); -                restart_usb_driver(&USB_DRIVER); +#    if USB_SUSPEND_WAKEUP_DELAY > 0 +                // Some hubs, kvm switches, and monitors do +                // weird things, with USB device state bouncing +                // around wildly on wakeup, yielding race +                // conditions that can corrupt the keyboard state. +                // +                // Pause for a while to let things settle... +                wait_ms(USB_SUSPEND_WAKEUP_DELAY); +#    endif              }          }          /* Woken up */ -        // variables has been already cleared by the wakeup hook -        send_keyboard_report(); -#    ifdef MOUSEKEY_ENABLE -        mousekey_send(); -#    endif /* MOUSEKEY_ENABLE */      }  #endif  }  void protocol_post_task(void) { -#ifdef CONSOLE_ENABLE -    console_task(); -#endif -#ifdef MIDI_ENABLE -    midi_ep_task(); -#endif  #ifdef VIRTSER_ENABLE      virtser_task();  #endif -#ifdef RAW_ENABLE -    raw_hid_task(); -#endif +    usb_idle_task();  } diff --git a/tmk_core/protocol/chibios/chibios.mk b/tmk_core/protocol/chibios/chibios.mk index 8eaf5b10d2..aee3f5f056 100644 --- a/tmk_core/protocol/chibios/chibios.mk +++ b/tmk_core/protocol/chibios/chibios.mk @@ -6,6 +6,8 @@ SRC += $(CHIBIOS_DIR)/usb_main.c  SRC += $(CHIBIOS_DIR)/chibios.c  SRC += usb_descriptor.c  SRC += $(CHIBIOS_DIR)/usb_driver.c +SRC += $(CHIBIOS_DIR)/usb_endpoints.c +SRC += $(CHIBIOS_DIR)/usb_report_handling.c  SRC += $(CHIBIOS_DIR)/usb_util.c  SRC += $(LIBSRC) diff --git a/tmk_core/protocol/chibios/usb_driver.c b/tmk_core/protocol/chibios/usb_driver.c index ad45f9b1da..7c3ce44687 100644 --- a/tmk_core/protocol/chibios/usb_driver.c +++ b/tmk_core/protocol/chibios/usb_driver.c @@ -1,127 +1,51 @@ -/* -    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - -    Licensed under the Apache License, Version 2.0 (the "License"); -    you may not use this file except in compliance with the License. -    You may obtain a copy of the License at - -        http://www.apache.org/licenses/LICENSE-2.0 - -    Unless required by applicable law or agreed to in writing, software -    distributed under the License is distributed on an "AS IS" BASIS, -    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -    See the License for the specific language governing permissions and -    limitations under the License. -*/ - -/** - * @file    hal_serial_usb.c - * @brief   Serial over USB Driver code. - * - * @addtogroup SERIAL_USB - * @{ - */ +// Copyright 2023 Stefan Kerkmann (@KarlK90) +// Copyright 2021 Purdea Andrei +// Copyright 2021 Michael Stapelberg +// Copyright 2020 Ryan (@fauxpark) +// Copyright 2016 Fredizzimo +// Copyright 2016 Giovanni Di Sirio +// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0  #include <hal.h> -#include "usb_driver.h"  #include <string.h> -/*===========================================================================*/ -/* Driver local definitions.                                                 */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables.                                                */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types.                                         */ -/*===========================================================================*/ - -/* - * Current Line Coding. - */ -static cdc_linecoding_t linecoding = {{0x00, 0x96, 0x00, 0x00}, /* 38400.                           */ -                                      LC_STOP_1, -                                      LC_PARITY_NONE, -                                      8}; +#include "usb_driver.h" +#include "util.h"  /*===========================================================================*/  /* Driver local functions.                                                   */  /*===========================================================================*/ -static bool qmkusb_start_receive(QMKUSBDriver *qmkusbp) { -    uint8_t *buf; - +static void usb_start_receive(usb_endpoint_out_t *endpoint) {      /* If the USB driver is not in the appropriate state then transactions         must not be started.*/ -    if ((usbGetDriverStateI(qmkusbp->config->usbp) != USB_ACTIVE) || (qmkusbp->state != QMKUSB_READY)) { -        return true; +    if ((usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE)) { +        return;      }      /* Checking if there is already a transaction ongoing on the endpoint.*/ -    if (usbGetReceiveStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_out)) { -        return true; +    if (usbGetReceiveStatusI(endpoint->config.usbp, endpoint->config.ep)) { +        return;      }      /* Checking if there is a buffer ready for incoming data.*/ -    buf = ibqGetEmptyBufferI(&qmkusbp->ibqueue); -    if (buf == NULL) { -        return true; +    uint8_t *buffer = ibqGetEmptyBufferI(&endpoint->ibqueue); +    if (buffer == NULL) { +        return;      }      /* Buffer found, starting a new transaction.*/ -    usbStartReceiveI(qmkusbp->config->usbp, qmkusbp->config->bulk_out, buf, qmkusbp->ibqueue.bsize - sizeof(size_t)); - -    return false; -} - -/* - * Interface implementation. - */ - -static size_t _write(void *ip, const uint8_t *bp, size_t n) { -    return obqWriteTimeout(&((QMKUSBDriver *)ip)->obqueue, bp, n, TIME_INFINITE); -} - -static size_t _read(void *ip, uint8_t *bp, size_t n) { -    return ibqReadTimeout(&((QMKUSBDriver *)ip)->ibqueue, bp, n, TIME_INFINITE); -} - -static msg_t _put(void *ip, uint8_t b) { -    return obqPutTimeout(&((QMKUSBDriver *)ip)->obqueue, b, TIME_INFINITE); -} - -static msg_t _get(void *ip) { -    return ibqGetTimeout(&((QMKUSBDriver *)ip)->ibqueue, TIME_INFINITE); -} - -static msg_t _putt(void *ip, uint8_t b, sysinterval_t timeout) { -    return obqPutTimeout(&((QMKUSBDriver *)ip)->obqueue, b, timeout); -} - -static msg_t _gett(void *ip, sysinterval_t timeout) { -    return ibqGetTimeout(&((QMKUSBDriver *)ip)->ibqueue, timeout); +    usbStartReceiveI(endpoint->config.usbp, endpoint->config.ep, buffer, endpoint->ibqueue.bsize - sizeof(size_t));  } -static size_t _writet(void *ip, const uint8_t *bp, size_t n, sysinterval_t timeout) { -    return obqWriteTimeout(&((QMKUSBDriver *)ip)->obqueue, bp, n, timeout); -} - -static size_t _readt(void *ip, uint8_t *bp, size_t n, sysinterval_t timeout) { -    return ibqReadTimeout(&((QMKUSBDriver *)ip)->ibqueue, bp, n, timeout); -} - -static const struct QMKUSBDriverVMT vmt = {0, _write, _read, _put, _get, _putt, _gett, _writet, _readt}; -  /**   * @brief   Notification of empty buffer released into the input buffers queue.   *   * @param[in] bqp       the buffers queue pointer.   */  static void ibnotify(io_buffers_queue_t *bqp) { -    QMKUSBDriver *qmkusbp = bqGetLinkX(bqp); -    (void)qmkusb_start_receive(qmkusbp); +    usb_endpoint_out_t *endpoint = bqGetLinkX(bqp); +    usb_start_receive(endpoint);  }  /** @@ -130,22 +54,22 @@ static void ibnotify(io_buffers_queue_t *bqp) {   * @param[in] bqp       the buffers queue pointer.   */  static void obnotify(io_buffers_queue_t *bqp) { -    size_t        n; -    QMKUSBDriver *qmkusbp = bqGetLinkX(bqp); +    usb_endpoint_in_t *endpoint = bqGetLinkX(bqp); -    /* If the USB driver is not in the appropriate state then transactions +    /* If the USB endpoint is not in the appropriate state then transactions         must not be started.*/ -    if ((usbGetDriverStateI(qmkusbp->config->usbp) != USB_ACTIVE) || (qmkusbp->state != QMKUSB_READY)) { +    if ((usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE)) {          return;      }      /* Checking if there is already a transaction ongoing on the endpoint.*/ -    if (!usbGetTransmitStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_in)) { +    if (!usbGetTransmitStatusI(endpoint->config.usbp, endpoint->config.ep)) {          /* Trying to get a full buffer.*/ -        uint8_t *buf = obqGetFullBufferI(&qmkusbp->obqueue, &n); -        if (buf != NULL) { +        size_t   n; +        uint8_t *buffer = obqGetFullBufferI(&endpoint->obqueue, &n); +        if (buffer != NULL) {              /* Buffer found, starting a new transaction.*/ -            usbStartTransmitI(qmkusbp->config->usbp, qmkusbp->config->bulk_in, buf, n); +            usbStartTransmitI(endpoint->config.usbp, endpoint->config.ep, buffer, n);          }      }  } @@ -154,264 +78,149 @@ static void obnotify(io_buffers_queue_t *bqp) {  /* Driver exported functions.                                                */  /*===========================================================================*/ -/** - * @brief   Serial Driver initialization. - * @note    This function is implicitly invoked by @p halInit(), there is - *          no need to explicitly initialize the driver. - * - * @init - */ -void qmkusbInit(void) {} +void usb_endpoint_in_init(usb_endpoint_in_t *endpoint) { +    usb_endpoint_config_t *config = &endpoint->config; +    endpoint->ep_config.in_state  = &endpoint->ep_in_state; -/** - * @brief   Initializes a generic full duplex driver object. - * @details The HW dependent part of the initialization has to be performed - *          outside, usually in the hardware initialization code. - * - * @param[out] qmkusbp     pointer to a @p QMKUSBDriver structure - * - * @init - */ -void qmkusbObjectInit(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config) { -    qmkusbp->vmt = &vmt; -    osalEventObjectInit(&qmkusbp->event); -    qmkusbp->state = QMKUSB_STOP; -    // Note that the config uses the USB direction naming -    ibqObjectInit(&qmkusbp->ibqueue, true, config->ob, config->out_size, config->out_buffers, ibnotify, qmkusbp); -    obqObjectInit(&qmkusbp->obqueue, true, config->ib, config->in_size, config->in_buffers, obnotify, qmkusbp); +#if defined(USB_ENDPOINTS_ARE_REORDERABLE) +    if (endpoint->is_shared) { +        endpoint->ep_config.out_state = &endpoint->ep_out_state; +    } +#endif +    obqObjectInit(&endpoint->obqueue, true, config->buffer, config->buffer_size, config->buffer_capacity, obnotify, endpoint);  } -/** - * @brief   Configures and starts the driver. - * - * @param[in] qmkusbp      pointer to a @p QMKUSBDriver object - * @param[in] config    the serial over USB driver configuration - * - * @api - */ -void qmkusbStart(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config) { -    USBDriver *usbp = config->usbp; +void usb_endpoint_out_init(usb_endpoint_out_t *endpoint) { +    usb_endpoint_config_t *config = &endpoint->config; +    endpoint->ep_config.out_state = &endpoint->ep_out_state; +    ibqObjectInit(&endpoint->ibqueue, true, config->buffer, config->buffer_size, config->buffer_capacity, ibnotify, endpoint); +} -    osalDbgCheck(qmkusbp != NULL); +void usb_endpoint_in_start(usb_endpoint_in_t *endpoint) { +    osalDbgCheck(endpoint != NULL);      osalSysLock(); -    osalDbgAssert((qmkusbp->state == QMKUSB_STOP) || (qmkusbp->state == QMKUSB_READY), "invalid state"); -    usbp->in_params[config->bulk_in - 1U]   = qmkusbp; -    usbp->out_params[config->bulk_out - 1U] = qmkusbp; -    if (config->int_in > 0U) { -        usbp->in_params[config->int_in - 1U] = qmkusbp; -    } -    qmkusbp->config = config; -    qmkusbp->state  = QMKUSB_READY; +    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state"); +    endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = endpoint; +    endpoint->timed_out                                        = false;      osalSysUnlock();  } -/** - * @brief   Stops the driver. - * @details Any thread waiting on the driver's queues will be awakened with - *          the message @p MSG_RESET. - * - * @param[in] qmkusbp      pointer to a @p QMKUSBDriver object - * - * @api - */ -void qmkusbStop(QMKUSBDriver *qmkusbp) { -    USBDriver *usbp = qmkusbp->config->usbp; - -    osalDbgCheck(qmkusbp != NULL); +void usb_endpoint_out_start(usb_endpoint_out_t *endpoint) { +    osalDbgCheck(endpoint != NULL);      osalSysLock(); +    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state"); +    endpoint->config.usbp->out_params[endpoint->config.ep - 1U] = endpoint; +    endpoint->timed_out                                         = false; +    osalSysUnlock(); +} -    osalDbgAssert((qmkusbp->state == QMKUSB_STOP) || (qmkusbp->state == QMKUSB_READY), "invalid state"); +void usb_endpoint_in_stop(usb_endpoint_in_t *endpoint) { +    osalDbgCheck(endpoint != NULL); -    /* Driver in stopped state.*/ -    usbp->in_params[qmkusbp->config->bulk_in - 1U]   = NULL; -    usbp->out_params[qmkusbp->config->bulk_out - 1U] = NULL; -    if (qmkusbp->config->int_in > 0U) { -        usbp->in_params[qmkusbp->config->int_in - 1U] = NULL; -    } -    qmkusbp->config = NULL; -    qmkusbp->state  = QMKUSB_STOP; +    osalSysLock(); +    endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = NULL; -    /* Enforces a disconnection.*/ -    chnAddFlagsI(qmkusbp, CHN_DISCONNECTED); -    ibqResetI(&qmkusbp->ibqueue); -    obqResetI(&qmkusbp->obqueue); +    bqSuspendI(&endpoint->obqueue); +    obqResetI(&endpoint->obqueue); +    if (endpoint->report_storage != NULL) { +        endpoint->report_storage->reset_report(endpoint->report_storage->reports); +    }      osalOsRescheduleS(); -      osalSysUnlock();  } -/** - * @brief   USB device suspend handler. - * @details Generates a @p CHN_DISCONNECT event and puts queues in - *          non-blocking mode, this way the application cannot get stuck - *          in the middle of an I/O operations. - * @note    If this function is not called from an ISR then an explicit call - *          to @p osalOsRescheduleS() in necessary afterward. - * - * @param[in] qmkusbp      pointer to a @p QMKUSBDriver object - * - * @iclass - */ -void qmkusbSuspendHookI(QMKUSBDriver *qmkusbp) { -    chnAddFlagsI(qmkusbp, CHN_DISCONNECTED); -    bqSuspendI(&qmkusbp->ibqueue); -    bqSuspendI(&qmkusbp->obqueue); -} +void usb_endpoint_out_stop(usb_endpoint_out_t *endpoint) { +    osalDbgCheck(endpoint != NULL); -/** - * @brief   USB device wakeup handler. - * @details Generates a @p CHN_CONNECT event and resumes normal queues - *          operations. - * - * @note    If this function is not called from an ISR then an explicit call - *          to @p osalOsRescheduleS() in necessary afterward. - * - * @param[in] qmkusbp      pointer to a @p QMKUSBDriver object - * - * @iclass - */ -void qmkusbWakeupHookI(QMKUSBDriver *qmkusbp) { -    chnAddFlagsI(qmkusbp, CHN_CONNECTED); -    bqResumeX(&qmkusbp->ibqueue); -    bqResumeX(&qmkusbp->obqueue); -} +    osalSysLock(); +    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state"); -/** - * @brief   USB device configured handler. - * - * @param[in] qmkusbp      pointer to a @p QMKUSBDriver object - * - * @iclass - */ -void qmkusbConfigureHookI(QMKUSBDriver *qmkusbp) { -    ibqResetI(&qmkusbp->ibqueue); -    bqResumeX(&qmkusbp->ibqueue); -    obqResetI(&qmkusbp->obqueue); -    bqResumeX(&qmkusbp->obqueue); -    chnAddFlagsI(qmkusbp, CHN_CONNECTED); -    (void)qmkusb_start_receive(qmkusbp); +    bqSuspendI(&endpoint->ibqueue); +    ibqResetI(&endpoint->ibqueue); +    osalOsRescheduleS(); +    osalSysUnlock();  } -/** - * @brief   Default requests hook. - * @details Applications wanting to use the Serial over USB driver can use - *          this function as requests hook in the USB configuration. - *          The following requests are emulated: - *          - CDC_GET_LINE_CODING. - *          - CDC_SET_LINE_CODING. - *          - CDC_SET_CONTROL_LINE_STATE. - *          . - * - * @param[in] usbp      pointer to the @p USBDriver object - * @return              The hook status. - * @retval true         Message handled internally. - * @retval false        Message not handled. - */ -bool qmkusbRequestsHook(USBDriver *usbp) { -    if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) { -        switch (usbp->setup[1]) { -            case CDC_GET_LINE_CODING: -                usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL); -                return true; -            case CDC_SET_LINE_CODING: -                usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL); -                return true; -            case CDC_SET_CONTROL_LINE_STATE: -                /* Nothing to do, there are no control lines.*/ -                usbSetupTransfer(usbp, NULL, 0, NULL); -                return true; -            default: -                return false; -        } -    } -    return false; -} +void usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint) { +    bqSuspendI(&endpoint->obqueue); +    obqResetI(&endpoint->obqueue); -/** - * @brief   SOF handler. - * @details The SOF interrupt is used for automatic flushing of incomplete - *          buffers pending in the output queue. - * - * @param[in] qmkusbp      pointer to a @p QMKUSBDriver object - * - * @iclass - */ -void qmkusbSOFHookI(QMKUSBDriver *qmkusbp) { -    /* If the USB driver is not in the appropriate state then transactions -       must not be started.*/ -    if ((usbGetDriverStateI(qmkusbp->config->usbp) != USB_ACTIVE) || (qmkusbp->state != QMKUSB_READY)) { -        return; +    if (endpoint->report_storage != NULL) { +        endpoint->report_storage->reset_report(endpoint->report_storage->reports);      } +} -    /* If there is already a transaction ongoing then another one cannot be -       started.*/ -    if (usbGetTransmitStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_in)) { -        return; -    } +void usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint) { +    bqSuspendI(&endpoint->ibqueue); +    ibqResetI(&endpoint->ibqueue); +} -    /* Checking if there only a buffer partially filled, if so then it is -       enforced in the queue and transmitted.*/ -    if (obqTryFlushI(&qmkusbp->obqueue)) { -        size_t   n; -        uint8_t *buf = obqGetFullBufferI(&qmkusbp->obqueue, &n); +void usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint) { +    bqResumeX(&endpoint->obqueue); +} -        /* For fixed size drivers, fill the end with zeros */ -        if (qmkusbp->config->fixed_size) { -            memset(buf + n, 0, qmkusbp->config->in_size - n); -            n = qmkusbp->config->in_size; -        } +void usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint) { +    bqResumeX(&endpoint->ibqueue); +} -        osalDbgAssert(buf != NULL, "queue is empty"); +void usb_endpoint_in_configure_cb(usb_endpoint_in_t *endpoint) { +    usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config); +    obqResetI(&endpoint->obqueue); +    bqResumeX(&endpoint->obqueue); +} -        usbStartTransmitI(qmkusbp->config->usbp, qmkusbp->config->bulk_in, buf, n); -    } +void usb_endpoint_out_configure_cb(usb_endpoint_out_t *endpoint) { +    /* The current assumption is that there are no standalone OUT endpoints, +     * therefore if we share an endpoint with an IN endpoint, it is already +     * initialized. */ +#if !defined(USB_ENDPOINTS_ARE_REORDERABLE) +    usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config); +#endif +    ibqResetI(&endpoint->ibqueue); +    bqResumeX(&endpoint->ibqueue); +    (void)usb_start_receive(endpoint);  } -/** - * @brief   Default data transmitted callback. - * @details The application must use this function as callback for the IN - *          data endpoint. - * - * @param[in] usbp      pointer to the @p USBDriver object - * @param[in] ep        IN endpoint number - */ -void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep) { -    uint8_t *     buf; -    size_t        n; -    QMKUSBDriver *qmkusbp = usbp->in_params[ep - 1U]; +void usb_endpoint_in_tx_complete_cb(USBDriver *usbp, usbep_t ep) { +    usb_endpoint_in_t *endpoint = usbp->in_params[ep - 1U]; +    size_t             n; +    uint8_t *          buffer; -    if (qmkusbp == NULL) { +    if (endpoint == NULL) {          return;      }      osalSysLockFromISR(); -    /* Signaling that space is available in the output queue.*/ -    chnAddFlagsI(qmkusbp, CHN_OUTPUT_EMPTY); +    /* Sending succeded, so we can reset the timed out state. */ +    endpoint->timed_out = false;      /* Freeing the buffer just transmitted, if it was not a zero size packet.*/ -    if (usbp->epc[ep]->in_state->txsize > 0U) { -        obqReleaseEmptyBufferI(&qmkusbp->obqueue); +    if (!obqIsEmptyI(&endpoint->obqueue) && usbp->epc[ep]->in_state->txsize > 0U) { +        /* Store the last send report in the endpoint to be retrieved by a +         * GET_REPORT request or IDLE report handling. */ +        if (endpoint->report_storage != NULL) { +            buffer = obqGetFullBufferI(&endpoint->obqueue, &n); +            endpoint->report_storage->set_report(endpoint->report_storage->reports, buffer, n); +        } +        obqReleaseEmptyBufferI(&endpoint->obqueue);      }      /* Checking if there is a buffer ready for transmission.*/ -    buf = obqGetFullBufferI(&qmkusbp->obqueue, &n); +    buffer = obqGetFullBufferI(&endpoint->obqueue, &n); -    if (buf != NULL) { +    if (buffer != NULL) {          /* The endpoint cannot be busy, we are in the context of the callback,             so it is safe to transmit without a check.*/ -        usbStartTransmitI(usbp, ep, buf, n); -    } else if ((usbp->epc[ep]->in_state->txsize > 0U) && ((usbp->epc[ep]->in_state->txsize & ((size_t)usbp->epc[ep]->in_maxsize - 1U)) == 0U)) { +        usbStartTransmitI(usbp, ep, buffer, n); +    } else if ((usbp->epc[ep]->ep_mode == USB_EP_MODE_TYPE_BULK) && (usbp->epc[ep]->in_state->txsize > 0U) && ((usbp->epc[ep]->in_state->txsize & ((size_t)usbp->epc[ep]->in_maxsize - 1U)) == 0U)) {          /* Transmit zero sized packet in case the last one has maximum allowed -           size. Otherwise the recipient may expect more data coming soon and -           not return buffered data to app. See section 5.8.3 Bulk Transfer -           Packet Size Constraints of the USB Specification document.*/ -        if (!qmkusbp->config->fixed_size) { -            usbStartTransmitI(usbp, ep, usbp->setup, 0); -        } - +         * size. Otherwise the recipient may expect more data coming soon and +         * not return buffered data to app. See section 5.8.3 Bulk Transfer +         * Packet Size Constraints of the USB Specification document. */ +        usbStartTransmitI(usbp, ep, usbp->setup, 0);      } else {          /* Nothing to transmit.*/      } @@ -419,47 +228,114 @@ void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep) {      osalSysUnlockFromISR();  } -/** - * @brief   Default data received callback. - * @details The application must use this function as callback for the OUT - *          data endpoint. - * - * @param[in] usbp      pointer to the @p USBDriver object - * @param[in] ep        OUT endpoint number - */ -void qmkusbDataReceived(USBDriver *usbp, usbep_t ep) { -    QMKUSBDriver *qmkusbp = usbp->out_params[ep - 1U]; -    if (qmkusbp == NULL) { +void usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep) { +    usb_endpoint_out_t *endpoint = usbp->out_params[ep - 1U]; +    if (endpoint == NULL) {          return;      }      osalSysLockFromISR(); -    /* Signaling that data is available in the input queue.*/ -    chnAddFlagsI(qmkusbp, CHN_INPUT_AVAILABLE); - -    /* Posting the filled buffer in the queue.*/ -    ibqPostFullBufferI(&qmkusbp->ibqueue, usbGetReceiveTransactionSizeX(qmkusbp->config->usbp, qmkusbp->config->bulk_out)); +    size_t size = usbGetReceiveTransactionSizeX(usbp, ep); +    if (size > 0) { +        /* Posting the filled buffer in the queue.*/ +        ibqPostFullBufferI(&endpoint->ibqueue, usbGetReceiveTransactionSizeX(endpoint->config.usbp, endpoint->config.ep)); +    } -    /* The endpoint cannot be busy, we are in the context of the callback, -       so a packet is in the buffer for sure. Trying to get a free buffer -       for the next transaction.*/ -    (void)qmkusb_start_receive(qmkusbp); +    /* The endpoint cannot be busy, we are in the context of the callback, so a +     * packet is in the buffer for sure. Trying to get a free buffer for the +     * next transaction.*/ +    usb_start_receive(endpoint);      osalSysUnlockFromISR();  } -/** - * @brief   Default data received callback. - * @details The application must use this function as callback for the IN - *          interrupt endpoint. - * - * @param[in] usbp      pointer to the @p USBDriver object - * @param[in] ep        endpoint number - */ -void qmkusbInterruptTransmitted(USBDriver *usbp, usbep_t ep) { -    (void)usbp; -    (void)ep; +bool usb_endpoint_in_send(usb_endpoint_in_t *endpoint, const uint8_t *data, size_t size, sysinterval_t timeout, bool buffered) { +    osalDbgCheck((endpoint != NULL) && (data != NULL) && (size > 0U) && (size <= endpoint->config.buffer_size)); + +    osalSysLock(); +    if (usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE) { +        osalSysUnlock(); +        return false; +    } + +    /* Short circuit the waiting if this endpoint timed out before, e.g. if +     * nobody is listening on this endpoint (is disconnected) such as +     * `hid_listen`/`qmk console` or we are in an environment with a very +     * restricted USB stack. The reason is to not introduce micro lock-ups if +     * the report is send periodically. */ +    if (endpoint->timed_out && timeout != TIME_INFINITE) { +        timeout = TIME_IMMEDIATE; +    } +    osalSysUnlock(); + +    while (true) { +        size_t sent = obqWriteTimeout(&endpoint->obqueue, data, size, timeout); + +        if (sent < size) { +            osalSysLock(); +            endpoint->timed_out |= sent == 0; +            bqSuspendI(&endpoint->obqueue); +            obqResetI(&endpoint->obqueue); +            bqResumeX(&endpoint->obqueue); +            osalOsRescheduleS(); +            osalSysUnlock(); +            continue; +        } + +        if (!buffered) { +            obqFlush(&endpoint->obqueue); +        } + +        return true; +    } +} + +void usb_endpoint_in_flush(usb_endpoint_in_t *endpoint, bool padded) { +    osalDbgCheck(endpoint != NULL); + +    output_buffers_queue_t *obqp = &endpoint->obqueue; + +    if (padded && obqp->ptr != NULL) { +        ptrdiff_t bytes_left = (size_t)obqp->top - (size_t)obqp->ptr; +        while (bytes_left > 0) { +            // Putting bytes into a buffer that has space left should never +            // fail and be instant, therefore we don't check the return value +            // for errors here. +            obqPutTimeout(obqp, 0, TIME_IMMEDIATE); +            bytes_left--; +        } +    } + +    obqFlush(obqp); +} + +bool usb_endpoint_in_is_inactive(usb_endpoint_in_t *endpoint) { +    osalDbgCheck(endpoint != NULL); + +    osalSysLock(); +    bool inactive = obqIsEmptyI(&endpoint->obqueue) && !usbGetTransmitStatusI(endpoint->config.usbp, endpoint->config.ep); +    osalSysUnlock(); + +    return inactive;  } -/** @} */ +bool usb_endpoint_out_receive(usb_endpoint_out_t *endpoint, uint8_t *data, size_t size, sysinterval_t timeout) { +    osalDbgCheck((endpoint != NULL) && (data != NULL) && (size > 0U)); + +    osalSysLock(); +    if (usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE) { +        osalSysUnlock(); +        return false; +    } + +    if (endpoint->timed_out && timeout != TIME_INFINITE) { +        timeout = TIME_IMMEDIATE; +    } +    osalSysUnlock(); + +    const size_t received = ibqReadTimeout(&endpoint->ibqueue, data, size, timeout); +    endpoint->timed_out   = received == 0; + +    return received == size; +} diff --git a/tmk_core/protocol/chibios/usb_driver.h b/tmk_core/protocol/chibios/usb_driver.h index f94387debd..26787e1eba 100644 --- a/tmk_core/protocol/chibios/usb_driver.h +++ b/tmk_core/protocol/chibios/usb_driver.h @@ -1,177 +1,209 @@ -/* -    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - -    Licensed under the Apache License, Version 2.0 (the "License"); -    you may not use this file except in compliance with the License. -    You may obtain a copy of the License at - -        http://www.apache.org/licenses/LICENSE-2.0 - -    Unless required by applicable law or agreed to in writing, software -    distributed under the License is distributed on an "AS IS" BASIS, -    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -    See the License for the specific language governing permissions and -    limitations under the License. -*/ - -/** - * @file    usb_driver.h - * @brief   Usb driver suitable for both packet and serial formats - * - * @addtogroup SERIAL_USB - * @{ - */ +// Copyright 2023 Stefan Kerkmann (@KarlK90) +// Copyright 2020 Ryan (@fauxpark) +// Copyright 2016 Fredizzimo +// Copyright 2016 Giovanni Di Sirio +// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0  #pragma once -#include <hal_usb_cdc.h> - -/*===========================================================================*/ -/* Driver constants.                                                         */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Derived constants and error checks.                                       */ -/*===========================================================================*/ +#include <hal_buffers.h> +#include "usb_descriptor.h" +#include "chibios_config.h" +#include "usb_report_handling.h" +#include "string.h" +#include "timer.h"  #if HAL_USE_USB == FALSE  #    error "The USB Driver requires HAL_USE_USB"  #endif -/*===========================================================================*/ -/* Driver data structures and types.                                         */ -/*===========================================================================*/ +/* USB Low Level driver specific endpoint fields */ +#if !defined(usb_lld_endpoint_fields) +#    define usb_lld_endpoint_fields   \ +        2,        /* IN multiplier */ \ +            NULL, /* SETUP buffer (not a SETUP endpoint) */ +#endif -/** - * @brief Driver state machine possible states. - */ -typedef enum { -    QMKUSB_UNINIT = 0, /**< Not initialized.                   */ -    QMKUSB_STOP   = 1, /**< Stopped.                           */ -    QMKUSB_READY  = 2  /**< Ready.                             */ -} qmkusbstate_t; - -/** - * @brief   Structure representing a serial over USB driver. +/* + * 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.   */ -typedef struct QMKUSBDriver QMKUSBDriver; +#define QMK_USB_ENDPOINT_IN(mode, ep_size, ep_num, _buffer_capacity, _usb_requests_cb, _report_storage) \ +    {                                                                                                   \ +        .usb_requests_cb = _usb_requests_cb, .report_storage = _report_storage,                         \ +        .ep_config =                                                                                    \ +            {                                                                                           \ +                mode,                           /* EP Mode */                                           \ +                NULL,                           /* SETUP packet notification callback */                \ +                usb_endpoint_in_tx_complete_cb, /* IN notification callback */                          \ +                NULL,                           /* OUT notification callback */                         \ +                ep_size,                        /* IN maximum packet size */                            \ +                0,                              /* OUT maximum packet size */                           \ +                NULL,                           /* IN Endpoint state */                                 \ +                NULL,                           /* OUT endpoint state */                                \ +                usb_lld_endpoint_fields         /* USB driver specific endpoint fields */               \ +            },                                                                                          \ +        .config = {                                                                                     \ +            .usbp            = &USB_DRIVER,                                                             \ +            .ep              = ep_num,                                                                  \ +            .buffer_capacity = _buffer_capacity,                                                        \ +            .buffer_size     = ep_size,                                                                 \ +            .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0},     \ +        }                                                                                               \ +    } + +#if !defined(USB_ENDPOINTS_ARE_REORDERABLE) + +#    define QMK_USB_ENDPOINT_OUT(mode, ep_size, ep_num, _buffer_capacity)                              \ +        {                                                                                              \ +            .ep_config =                                                                               \ +                {                                                                                      \ +                    mode,                            /* EP Mode */                                     \ +                    NULL,                            /* SETUP packet notification callback */          \ +                    NULL,                            /* IN notification callback */                    \ +                    usb_endpoint_out_rx_complete_cb, /* OUT notification callback */                   \ +                    0,                               /* IN maximum packet size */                      \ +                    ep_size,                         /* OUT maximum packet size */                     \ +                    NULL,                            /* IN Endpoint state */                           \ +                    NULL,                            /* OUT endpoint state */                          \ +                    usb_lld_endpoint_fields          /* USB driver specific endpoint fields */         \ +                },                                                                                     \ +            .config = {                                                                                \ +                .usbp            = &USB_DRIVER,                                                        \ +                .ep              = ep_num,                                                             \ +                .buffer_capacity = _buffer_capacity,                                                   \ +                .buffer_size     = ep_size,                                                            \ +                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0} \ +            }                                                                                          \ +        } + +#else + +#    define QMK_USB_ENDPOINT_IN_SHARED(mode, ep_size, ep_num, _buffer_capacity, _usb_requests_cb, _report_storage) \ +        {                                                                                                          \ +            .usb_requests_cb = _usb_requests_cb, .is_shared = true, .report_storage = _report_storage,             \ +            .ep_config =                                                                                           \ +                {                                                                                                  \ +                    mode,                            /* EP Mode */                                                 \ +                    NULL,                            /* SETUP packet notification callback */                      \ +                    usb_endpoint_in_tx_complete_cb,  /* IN notification callback */                                \ +                    usb_endpoint_out_rx_complete_cb, /* OUT notification callback */                               \ +                    ep_size,                         /* IN maximum packet size */                                  \ +                    ep_size,                         /* OUT maximum packet size */                                 \ +                    NULL,                            /* IN Endpoint state */                                       \ +                    NULL,                            /* OUT endpoint state */                                      \ +                    usb_lld_endpoint_fields          /* USB driver specific endpoint fields */                     \ +                },                                                                                                 \ +            .config = {                                                                                            \ +                .usbp            = &USB_DRIVER,                                                                    \ +                .ep              = ep_num,                                                                         \ +                .buffer_capacity = _buffer_capacity,                                                               \ +                .buffer_size     = ep_size,                                                                        \ +                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0},            \ +            }                                                                                                      \ +        } + +/* The current assumption is that there are no standalone OUT endpoints, so the + * OUT endpoint is always initialized by the IN endpoint. */ +#    define QMK_USB_ENDPOINT_OUT(mode, ep_size, ep_num, _buffer_capacity)                              \ +        {                                                                                              \ +            .ep_config =                                                                               \ +                {                                                                                      \ +                    0 /* Already defined in the IN endpoint */                                         \ +                },                                                                                     \ +            .config = {                                                                                \ +                .usbp            = &USB_DRIVER,                                                        \ +                .ep              = ep_num,                                                             \ +                .buffer_capacity = _buffer_capacity,                                                   \ +                .buffer_size     = ep_size,                                                            \ +                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0} \ +            }                                                                                          \ +        } + +#endif -/** - * @brief   Serial over USB Driver configuration structure. - * @details An instance of this structure must be passed to @p sduStart() - *          in order to configure and start the driver operations. - */  typedef struct {      /**       * @brief   USB driver to use.       */      USBDriver *usbp; +      /** -     * @brief   Bulk IN endpoint used for outgoing data transfer. -     */ -    usbep_t bulk_in; -    /** -     * @brief   Bulk OUT endpoint used for incoming data transfer. -     */ -    usbep_t bulk_out; -    /** -     * @brief   Interrupt IN endpoint used for notifications. -     * @note    If set to zero then the INT endpoint is assumed to be not -     *          present, USB descriptors must be changed accordingly. +     * @brief   Endpoint used for data transfer       */ -    usbep_t int_in; +    usbep_t ep;      /** -     * @brief The number of buffers in the queues +     * @brief The number of buffers in the queue       */ -    size_t in_buffers; -    size_t out_buffers; +    size_t buffer_capacity;      /** -     * @brief The size of each buffer in the queue, typically the same as the endpoint size +     * @brief The size of each buffer in the queue, same as the endpoint size       */ -    size_t in_size; -    size_t out_size; +    size_t buffer_size;      /** -     * @brief Always send full buffers in_size (the rest is filled with zeroes) +     * @brief Buffer backing storage       */ -    bool fixed_size; +    uint8_t *buffer; +} usb_endpoint_config_t; -    /* Input buffer -     * @note needs to be initialized with a memory buffer of the right size -     */ -    uint8_t *ib; -    /* Output buffer -     * @note needs to be initialized with a memory buffer of the right size -     */ -    uint8_t *ob; -} QMKUSBConfig; +typedef struct { +    output_buffers_queue_t obqueue; +    USBEndpointConfig      ep_config; +    USBInEndpointState     ep_in_state; +#if defined(USB_ENDPOINTS_ARE_REORDERABLE) +    USBOutEndpointState ep_out_state; +    bool                is_shared; +#endif +    usb_endpoint_config_t config; +    usbreqhandler_t       usb_requests_cb; +    bool                  timed_out; +    usb_report_storage_t *report_storage; +} usb_endpoint_in_t; -/** - * @brief   @p SerialDriver specific data. - */ -#define _qmk_usb_driver_data                           \ -    _base_asynchronous_channel_data /* Driver state.*/ \ -        qmkusbstate_t state;                           \ -    /* Input buffers queue.*/                          \ -    input_buffers_queue_t ibqueue;                     \ -    /* Output queue.*/                                 \ -    output_buffers_queue_t obqueue;                    \ -    /* End of the mandatory fields.*/                  \ -    /* Current configuration data.*/                   \ -    const QMKUSBConfig *config; - -/** - * @brief   @p SerialUSBDriver specific methods. - */ -#define _qmk_usb_driver_methods _base_asynchronous_channel_methods +typedef struct { +    input_buffers_queue_t ibqueue; +    USBEndpointConfig     ep_config; +    USBOutEndpointState   ep_out_state; +    usb_endpoint_config_t config; +    bool                  timed_out; +} usb_endpoint_out_t; -/** - * @extends BaseAsynchronousChannelVMT - * - * @brief   @p SerialDriver virtual methods table. - */ -struct QMKUSBDriverVMT { -    _qmk_usb_driver_methods -}; +#ifdef __cplusplus +extern "C" { +#endif -/** - * @extends BaseAsynchronousChannel - * - * @brief   Full duplex serial driver class. - * @details This class extends @p BaseAsynchronousChannel by adding physical - *          I/O queues. - */ -struct QMKUSBDriver { -    /** @brief Virtual Methods Table.*/ -    const struct QMKUSBDriverVMT *vmt; -    _qmk_usb_driver_data -}; +void usb_endpoint_in_init(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_start(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_stop(usb_endpoint_in_t *endpoint); -/*===========================================================================*/ -/* Driver macros.                                                            */ -/*===========================================================================*/ +bool usb_endpoint_in_send(usb_endpoint_in_t *endpoint, const uint8_t *data, size_t size, sysinterval_t timeout, bool buffered); +void usb_endpoint_in_flush(usb_endpoint_in_t *endpoint, bool padded); +bool usb_endpoint_in_is_inactive(usb_endpoint_in_t *endpoint); -/*===========================================================================*/ -/* External declarations.                                                    */ -/*===========================================================================*/ +void usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_configure_cb(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_tx_complete_cb(USBDriver *usbp, usbep_t ep); + +void usb_endpoint_out_init(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_start(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_stop(usb_endpoint_out_t *endpoint); + +bool usb_endpoint_out_receive(usb_endpoint_out_t *endpoint, uint8_t *data, size_t size, sysinterval_t timeout); + +void usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_configure_cb(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep); -#ifdef __cplusplus -extern "C" { -#endif -void qmkusbInit(void); -void qmkusbObjectInit(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config); -void qmkusbStart(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config); -void qmkusbStop(QMKUSBDriver *qmkusbp); -void qmkusbSuspendHookI(QMKUSBDriver *qmkusbp); -void qmkusbWakeupHookI(QMKUSBDriver *qmkusbp); -void qmkusbConfigureHookI(QMKUSBDriver *qmkusbp); -bool qmkusbRequestsHook(USBDriver *usbp); -void qmkusbSOFHookI(QMKUSBDriver *qmkusbp); -void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep); -void qmkusbDataReceived(USBDriver *usbp, usbep_t ep); -void qmkusbInterruptTransmitted(USBDriver *usbp, usbep_t ep);  #ifdef __cplusplus  }  #endif diff --git a/tmk_core/protocol/chibios/usb_endpoints.c b/tmk_core/protocol/chibios/usb_endpoints.c new file mode 100644 index 0000000000..37ec3c722f --- /dev/null +++ b/tmk_core/protocol/chibios/usb_endpoints.c @@ -0,0 +1,152 @@ +// Copyright 2023 Stefan Kerkmann (@KarlK90) +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <ch.h> +#include <hal.h> + +#include "usb_main.h" +#include "usb_driver.h" +#include "usb_endpoints.h" +#include "report.h" + +usb_endpoint_in_t usb_endpoints_in[USB_ENDPOINT_IN_COUNT] = { +// clang-format off +#if defined(SHARED_EP_ENABLE) +    [USB_ENDPOINT_IN_SHARED] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, SHARED_EPSIZE, SHARED_IN_EPNUM, SHARED_IN_CAPACITY, NULL, +    QMK_USB_REPORT_STORAGE( +        &usb_shared_get_report, +        &usb_shared_set_report, +        &usb_shared_reset_report, +        &usb_shared_get_idle_rate, +        &usb_shared_set_idle_rate, +        &usb_shared_idle_timer_elapsed, +        (REPORT_ID_COUNT + 1), +#if defined(KEYBOARD_SHARED_EP) +        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_KEYBOARD, sizeof(report_keyboard_t)), +#endif +#if defined(MOUSE_SHARED_EP) +        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_MOUSE, sizeof(report_mouse_t)), +#endif +#if defined(EXTRAKEY_ENABLE) +        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_SYSTEM, sizeof(report_extra_t)), +        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_CONSUMER, sizeof(report_extra_t)), +#endif +#if defined(PROGRAMMABLE_BUTTON_ENABLE) +        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_PROGRAMMABLE_BUTTON, sizeof(report_programmable_button_t)), +#endif +#if defined(NKRO_ENABLE) +        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_NKRO, sizeof(report_nkro_t)), +#endif +#if defined(JOYSTICK_SHARED_EP) +        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_JOYSTICK, sizeof(report_joystick_t)), +#endif +#if defined(DIGITIZER_SHARED_EP) +        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_DIGITIZER, sizeof(report_digitizer_t)), +#endif +        ) +    ), +#endif +// clang-format on + +#if !defined(KEYBOARD_SHARED_EP) +    [USB_ENDPOINT_IN_KEYBOARD] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, KEYBOARD_EPSIZE, KEYBOARD_IN_EPNUM, KEYBOARD_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_keyboard_t))), +#endif + +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) +    [USB_ENDPOINT_IN_MOUSE] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, MOUSE_EPSIZE, MOUSE_IN_EPNUM, MOUSE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_mouse_t))), +#endif + +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) +    [USB_ENDPOINT_IN_JOYSTICK] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, JOYSTICK_EPSIZE, JOYSTICK_IN_EPNUM, JOYSTICK_IN_CAPACITY, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_joystick_t))), +#endif + +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) +    [USB_ENDPOINT_IN_JOYSTICK] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, DIGITIZER_EPSIZE, DIGITIZER_IN_EPNUM, DIGITIZER_IN_CAPACITY, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_digitizer_t))), +#endif + +#if defined(CONSOLE_ENABLE) +#    if defined(USB_ENDPOINTS_ARE_REORDERABLE) +    [USB_ENDPOINT_IN_CONSOLE] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, CONSOLE_EPSIZE, CONSOLE_IN_EPNUM, CONSOLE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(CONSOLE_EPSIZE)), +#    else +    [USB_ENDPOINT_IN_CONSOLE]  = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, CONSOLE_EPSIZE, CONSOLE_IN_EPNUM, CONSOLE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(CONSOLE_EPSIZE)), +#    endif +#endif + +#if defined(RAW_ENABLE) +#    if defined(USB_ENDPOINTS_ARE_REORDERABLE) +    [USB_ENDPOINT_IN_RAW] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_IN_EPNUM, RAW_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(RAW_EPSIZE)), +#    else +    [USB_ENDPOINT_IN_RAW]      = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_IN_EPNUM, RAW_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(RAW_EPSIZE)), +#    endif +#endif + +#if defined(MIDI_ENABLE) +#    if defined(USB_ENDPOINTS_ARE_REORDERABLE) +    [USB_ENDPOINT_IN_MIDI] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL), +#    else +    [USB_ENDPOINT_IN_MIDI]     = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL), +#    endif +#endif + +#if defined(VIRTSER_ENABLE) +#    if defined(USB_ENDPOINTS_ARE_REORDERABLE) +    [USB_ENDPOINT_IN_CDC_DATA] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_IN_EPNUM, CDC_IN_CAPACITY, virtser_usb_request_cb, NULL), +#    else +    [USB_ENDPOINT_IN_CDC_DATA] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_IN_EPNUM, CDC_IN_CAPACITY, virtser_usb_request_cb, NULL), +#    endif +    [USB_ENDPOINT_IN_CDC_SIGNALING] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, CDC_NOTIFICATION_EPSIZE, CDC_NOTIFICATION_EPNUM, CDC_SIGNALING_DUMMY_CAPACITY, NULL, NULL), +#endif +}; + +usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES] = { +#if !defined(KEYBOARD_SHARED_EP) +    [KEYBOARD_INTERFACE] = USB_ENDPOINT_IN_KEYBOARD, +#endif + +#if defined(RAW_ENABLE) +    [RAW_INTERFACE] = USB_ENDPOINT_IN_RAW, +#endif + +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) +    [MOUSE_INTERFACE] = USB_ENDPOINT_IN_MOUSE, +#endif + +#if defined(SHARED_EP_ENABLE) +    [SHARED_INTERFACE] = USB_ENDPOINT_IN_SHARED, +#endif + +#if defined(CONSOLE_ENABLE) +    [CONSOLE_INTERFACE] = USB_ENDPOINT_IN_CONSOLE, +#endif + +#if defined(MIDI_ENABLE) +    [AS_INTERFACE] = USB_ENDPOINT_IN_MIDI, +#endif + +#if defined(VIRTSER_ENABLE) +    [CCI_INTERFACE] = USB_ENDPOINT_IN_CDC_SIGNALING, +    [CDI_INTERFACE] = USB_ENDPOINT_IN_CDC_DATA, +#endif + +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) +    [JOYSTICK_INTERFACE] = USB_ENDPOINT_IN_JOYSTICK, +#endif + +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) +    [DIGITIZER_INTERFACE] = USB_ENDPOINT_IN_DIGITIZER, +#endif +}; + +usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT] = { +#if defined(RAW_ENABLE) +    [USB_ENDPOINT_OUT_RAW] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_OUT_EPNUM, RAW_OUT_CAPACITY), +#endif + +#if defined(MIDI_ENABLE) +    [USB_ENDPOINT_OUT_MIDI] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_OUT_EPNUM, MIDI_STREAM_OUT_CAPACITY), +#endif + +#if defined(VIRTSER_ENABLE) +    [USB_ENDPOINT_OUT_CDC_DATA] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_OUT_EPNUM, CDC_OUT_CAPACITY), +#endif +}; diff --git a/tmk_core/protocol/chibios/usb_endpoints.h b/tmk_core/protocol/chibios/usb_endpoints.h new file mode 100644 index 0000000000..a4e5ed88fc --- /dev/null +++ b/tmk_core/protocol/chibios/usb_endpoints.h @@ -0,0 +1,137 @@ +// Copyright 2023 Stefan Kerkmann (@KarlK90) +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "usb_descriptor.h" + +#if !defined(USB_DEFAULT_BUFFER_CAPACITY) +#    define USB_DEFAULT_BUFFER_CAPACITY 4 +#endif + +#if !defined(KEYBOARD_IN_CAPACITY) +#    define KEYBOARD_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif +#if !defined(SHARED_IN_CAPACITY) +#    define SHARED_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif +#if !defined(MOUSE_IN_CAPACITY) +#    define MOUSE_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(JOYSTICK_IN_CAPACITY) +#    define JOYSTICK_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(DIGITIZER_IN_CAPACITY) +#    define DIGITIZER_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(CONSOLE_IN_CAPACITY) +#    define CONSOLE_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(CONSOLE_OUT_CAPACITY) +#    define CONSOLE_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(RAW_IN_CAPACITY) +#    define RAW_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(RAW_OUT_CAPACITY) +#    define RAW_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(MIDI_STREAM_IN_CAPACITY) +#    define MIDI_STREAM_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(MIDI_STREAM_OUT_CAPACITY) +#    define MIDI_STREAM_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(CDC_IN_CAPACITY) +#    define CDC_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#if !defined(CDC_OUT_CAPACITY) +#    define CDC_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY +#endif + +#define CDC_SIGNALING_DUMMY_CAPACITY 1 + +typedef enum { +#if defined(SHARED_EP_ENABLE) +    USB_ENDPOINT_IN_SHARED, +#endif + +#if !defined(KEYBOARD_SHARED_EP) +    USB_ENDPOINT_IN_KEYBOARD, +#endif + +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) +    USB_ENDPOINT_IN_MOUSE, +#endif + +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) +    USB_ENDPOINT_IN_JOYSTICK, +#endif + +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) +    USB_ENDPOINT_IN_DIGITIZER, +#endif + +#if defined(CONSOLE_ENABLE) +    USB_ENDPOINT_IN_CONSOLE, +#endif + +#if defined(RAW_ENABLE) +    USB_ENDPOINT_IN_RAW, +#endif + +#if defined(MIDI_ENABLE) +    USB_ENDPOINT_IN_MIDI, +#endif + +#if defined(VIRTSER_ENABLE) +    USB_ENDPOINT_IN_CDC_DATA, +    USB_ENDPOINT_IN_CDC_SIGNALING, +#endif +    USB_ENDPOINT_IN_COUNT, +/* All non shared endpoints have to be consequtive numbers starting from 0, so + * that they can be used as array indices. The shared endpoints all point to + * the same endpoint so they have to be defined last to not reset the enum + * counter. */ +#if defined(SHARED_EP_ENABLE) +#    if defined(KEYBOARD_SHARED_EP) +    USB_ENDPOINT_IN_KEYBOARD = USB_ENDPOINT_IN_SHARED, +#    endif +#    if defined(MOUSE_SHARED_EP) +    USB_ENDPOINT_IN_MOUSE = USB_ENDPOINT_IN_SHARED, +#    endif +#    if defined(JOYSTICK_SHARED_EP) +    USB_ENDPOINT_IN_JOYSTICK = USB_ENDPOINT_IN_SHARED, +#    endif +#    if defined(DIGITIZER_SHARED_EP) +    USB_ENDPOINT_IN_DIGITIZER = USB_ENDPOINT_IN_SHARED, +#    endif +#endif +} usb_endpoint_in_lut_t; + +#define IS_VALID_USB_ENDPOINT_IN_LUT(i) ((i) >= 0 && (i) < USB_ENDPOINT_IN_COUNT) + +usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES]; + +typedef enum { +#if defined(RAW_ENABLE) +    USB_ENDPOINT_OUT_RAW, +#endif +#if defined(MIDI_ENABLE) +    USB_ENDPOINT_OUT_MIDI, +#endif +#if defined(VIRTSER_ENABLE) +    USB_ENDPOINT_OUT_CDC_DATA, +#endif +    USB_ENDPOINT_OUT_COUNT, +} usb_endpoint_out_lut_t; diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 7b1e641213..2024a3bc7f 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1,45 +1,32 @@ -/* - * (c) 2015 flabberast <[email protected]> - * - * 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. - */ +// Copyright 2023 Stefan Kerkmann +// Copyright 2020-2021 Ryan (@fauxpark) +// Copyright 2020 Nick Brassel (@tzarc) +// Copyright 2020 a-chol +// Copyright 2020 xyzz +// Copyright 2020 Joel Challis (@zvecr) +// Copyright 2020 George (@goshdarnharris) +// Copyright 2018 James Laird-Wah +// Copyright 2018 Drashna Jaelre (@drashna) +// Copyright 2016 Fredizzimo +// Copyright 2016 Giovanni Di Sirio +// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0  #include <ch.h>  #include <hal.h>  #include <string.h>  #include "usb_main.h" +#include "usb_report_handling.h"  #include "host.h" -#include "chibios_config.h" -#include "debug.h"  #include "suspend.h" +#include "timer.h"  #ifdef SLEEP_LED_ENABLE  #    include "sleep_led.h"  #    include "led.h"  #endif  #include "wait.h" +#include "usb_endpoints.h"  #include "usb_device_state.h"  #include "usb_descriptor.h"  #include "usb_driver.h" @@ -51,11 +38,6 @@  extern keymap_config_t keymap_config;  #endif -#if defined(CONSOLE_ENABLE) -#    define RBUF_SIZE 256 -#    include "ring_buffer.h" -#endif -  /* ---------------------------------------------------------   *       Global interface variables and declarations   * --------------------------------------------------------- @@ -69,33 +51,16 @@ extern keymap_config_t keymap_config;  #    define usb_lld_disconnect_bus(usbp)  #endif -uint8_t                keyboard_idle __attribute__((aligned(2)))     = 0; -uint8_t                keyboard_protocol __attribute__((aligned(2))) = 1; -uint8_t                keyboard_led_state                            = 0; -volatile uint16_t      keyboard_idle_count                           = 0; -static virtual_timer_t keyboard_idle_timer; - -static void keyboard_idle_timer_cb(struct ch_virtual_timer *, void *arg); +extern usb_endpoint_in_t  usb_endpoints_in[USB_ENDPOINT_IN_COUNT]; +extern usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT]; -report_keyboard_t keyboard_report_sent = {0}; -report_mouse_t    mouse_report_sent    = {0}; +uint8_t _Alignas(2) keyboard_idle     = 0; +uint8_t _Alignas(2) keyboard_protocol = 1; +uint8_t keyboard_led_state            = 0; -union { -    uint8_t           report_id; -    report_keyboard_t keyboard; -#ifdef EXTRAKEY_ENABLE -    report_extra_t extra; -#endif -#ifdef MOUSE_ENABLE -    report_mouse_t mouse; -#endif -#ifdef DIGITIZER_ENABLE -    report_digitizer_t digitizer; -#endif -#ifdef JOYSTICK_ENABLE -    report_joystick_t joystick; -#endif -} universal_report_blank = {0}; +static bool __attribute__((__unused__)) send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size); +static void __attribute__((__unused__)) flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded); +static bool __attribute__((__unused__)) receive_report(usb_endpoint_out_lut_t endpoint, void *report, size_t size);  /* ---------------------------------------------------------   *            Descriptors and USB driver objects @@ -109,6 +74,11 @@ union {              NULL, /* SETUP buffer (not a SETUP endpoint) */  #endif +/* + * 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) {      usb_control_request_t *setup = (usb_control_request_t *)usbp->setup; @@ -123,299 +93,6 @@ static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype      return &descriptor;  } -/* - * USB notification callback that does nothing.  Needed to work around bugs in - * some USB LLDs that fail to resume the waiting thread when the notification - * callback pointer is NULL. - */ -static void dummy_usb_cb(USBDriver *usbp, usbep_t ep) { -    (void)usbp; -    (void)ep; -} - -#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 */ -    dummy_usb_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 */ -    usb_lld_endpoint_fields /* USB driver specific endpoint fields */ -}; -#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 */ -    dummy_usb_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 */ -    usb_lld_endpoint_fields /* USB driver specific endpoint fields */ -}; -#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 */ -    dummy_usb_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 */ -    usb_lld_endpoint_fields /* USB driver specific endpoint fields */ -}; -#endif - -#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) -/* joystick endpoint state structure */ -static USBInEndpointState joystick_ep_state; - -/* joystick endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig joystick_ep_config = { -    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */ -    NULL,                   /* SETUP packet notification callback */ -    dummy_usb_cb,           /* IN notification callback */ -    NULL,                   /* OUT notification callback */ -    JOYSTICK_EPSIZE,        /* IN maximum packet size */ -    0,                      /* OUT maximum packet size */ -    &joystick_ep_state,     /* IN Endpoint state */ -    NULL,                   /* OUT endpoint state */ -    usb_lld_endpoint_fields /* USB driver specific endpoint fields */ -}; -#endif - -#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) -/* digitizer endpoint state structure */ -static USBInEndpointState digitizer_ep_state; - -/* digitizer endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig digitizer_ep_config = { -    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */ -    NULL,                   /* SETUP packet notification callback */ -    dummy_usb_cb,           /* IN notification callback */ -    NULL,                   /* OUT notification callback */ -    DIGITIZER_EPSIZE,       /* IN maximum packet size */ -    0,                      /* OUT maximum packet size */ -    &digitizer_ep_state,    /* IN Endpoint state */ -    NULL,                   /* OUT endpoint state */ -    usb_lld_endpoint_fields /* USB driver specific endpoint fields */ -}; -#endif - -#ifdef CONSOLE_ENABLE -/* Console endpoint state structure */ -static USBInEndpointState console_ep_state; - -/* Console endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ -static const USBEndpointConfig console_ep_config = { -    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */ -    NULL,                   /* SETUP packet notification callback */ -    dummy_usb_cb,           /* IN notification callback */ -    NULL,                   /* OUT notification callback */ -    CONSOLE_EPSIZE,         /* IN maximum packet size */ -    0,                      /* OUT maximum packet size */ -    &console_ep_state,      /* IN Endpoint state */ -    NULL,                   /* OUT endpoint state */ -    usb_lld_endpoint_fields /* USB driver specific endpoint fields */ -}; -#endif - -#ifdef USB_ENDPOINTS_ARE_REORDERABLE -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 - -#ifdef USB_ENDPOINTS_ARE_REORDERABLE -/* 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 */                                                            \ -                    usb_lld_endpoint_fields /* USB driver specific endpoint fields */                                           \ -                },                                                                                                              \ -            .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 */                                                        \ -                    usb_lld_endpoint_fields     /* USB driver specific endpoint fields */                                       \ -                },                                                                                                              \ -            .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          = (__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 -/* 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 */                                                            \ -                    usb_lld_endpoint_fields /* USB driver specific endpoint fields */                                           \ -                },                                                                                                              \ -            .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 */                                                            \ -                    usb_lld_endpoint_fields /* USB driver specific endpoint fields */                                           \ -                },                                                                                                              \ -            .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 */                                                        \ -                    usb_lld_endpoint_fields     /* USB driver specific endpoint fields */                                       \ -                },                                                                                                              \ -            .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          = (__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)]){}, \ -            }                                                                                                                   \ -        } -#endif - -typedef struct { -    union { -        struct { -#ifdef 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 RAW_ENABLE -#    ifndef RAW_IN_CAPACITY -#        define RAW_IN_CAPACITY 4 -#    endif -#    ifndef RAW_OUT_CAPACITY -#        define RAW_OUT_CAPACITY 4 -#    endif -#    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   * --------------------------------------------------------- @@ -507,36 +184,11 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {          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 -#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) -            usbInitEndpointI(usbp, JOYSTICK_IN_EPNUM, &joystick_ep_config); -#endif -#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) -            usbInitEndpointI(usbp, DIGITIZER_IN_EPNUM, &digitizer_ep_config); -#endif -#ifdef CONSOLE_ENABLE -            usbInitEndpointI(usbp, CONSOLE_IN_EPNUM, &console_ep_config); -#endif -            for (int i = 0; i < NUM_USB_DRIVERS; i++) { -#ifdef USB_ENDPOINTS_ARE_REORDERABLE -                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); +            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) { +                usb_endpoint_in_configure_cb(&usb_endpoints_in[i]); +            } +            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) { +                usb_endpoint_out_configure_cb(&usb_endpoints_out[i]);              }              osalSysUnlockFromISR();              if (last_suspend_state) { @@ -550,22 +202,25 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {              /* Falls into.*/          case USB_EVENT_RESET:              usb_event_queue_enqueue(event); -            for (int i = 0; i < NUM_USB_DRIVERS; i++) { -                chSysLockFromISR(); -                /* Disconnection event on suspend.*/ -                qmkusbSuspendHookI(&drivers.array[i].driver); -                chSysUnlockFromISR(); +            chSysLockFromISR(); +            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) { +                usb_endpoint_in_suspend_cb(&usb_endpoints_in[i]);              } +            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) { +                usb_endpoint_out_suspend_cb(&usb_endpoints_out[i]); +            } +            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(); +            chSysLockFromISR(); +            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) { +                usb_endpoint_in_wakeup_cb(&usb_endpoints_in[i]); +            } +            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) { +                usb_endpoint_out_wakeup_cb(&usb_endpoints_out[i]);              } +            chSysUnlockFromISR();              usb_event_queue_enqueue(USB_EVENT_WAKEUP);              return; @@ -587,7 +242,7 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {   * Other Device    Required    Optional    Optional    Optional    Optional    Optional   */ -static uint8_t set_report_buf[2] __attribute__((aligned(4))); +static uint8_t _Alignas(4) set_report_buf[2];  static void set_led_transfer_cb(USBDriver *usbp) {      usb_control_request_t *setup = (usb_control_request_t *)usbp->setup; @@ -611,41 +266,7 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {              case USB_RTYPE_DIR_DEV2HOST:                  switch (setup->bRequest) {                      case HID_REQ_GetReport: -                        switch (setup->wIndex) { -#ifndef KEYBOARD_SHARED_EP -                            case KEYBOARD_INTERFACE: -                                usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, KEYBOARD_REPORT_SIZE, NULL); -                                return TRUE; -                                break; -#endif -#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) -                            case MOUSE_INTERFACE: -                                usbSetupTransfer(usbp, (uint8_t *)&mouse_report_sent, sizeof(mouse_report_sent), NULL); -                                return TRUE; -                                break; -#endif -#ifdef SHARED_EP_ENABLE -                            case SHARED_INTERFACE: -#    ifdef KEYBOARD_SHARED_EP -                                if (setup->wValue.lbyte == REPORT_ID_KEYBOARD) { -                                    usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, KEYBOARD_REPORT_SIZE, NULL); -                                    return true; -                                } -#    endif -#    ifdef MOUSE_SHARED_EP -                                if (setup->wValue.lbyte == REPORT_ID_MOUSE) { -                                    usbSetupTransfer(usbp, (uint8_t *)&mouse_report_sent, sizeof(mouse_report_sent), NULL); -                                    return true; -                                } -#    endif -#endif /* SHARED_EP_ENABLE */ -                            default: -                                universal_report_blank.report_id = setup->wValue.lbyte; -                                usbSetupTransfer(usbp, (uint8_t *)&universal_report_blank, setup->wLength, NULL); -                                return true; -                        } -                        break; - +                        return usb_get_report_cb(usbp);                      case HID_REQ_GetProtocol:                          if (setup->wIndex == KEYBOARD_INTERFACE) {                              usbSetupTransfer(usbp, &keyboard_protocol, sizeof(uint8_t), NULL); @@ -654,10 +275,8 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {                          break;                      case HID_REQ_GetIdle: -                        usbSetupTransfer(usbp, &keyboard_idle, sizeof(uint8_t), NULL); -                        return true; +                        return usb_get_idle_cb(usbp);                  } -                break;              case USB_RTYPE_DIR_HOST2DEV:                  switch (setup->bRequest) { @@ -671,38 +290,15 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {                                  return true;                          }                          break; -                      case HID_REQ_SetProtocol:                          if (setup->wIndex == KEYBOARD_INTERFACE) {                              keyboard_protocol = setup->wValue.word; -#ifdef NKRO_ENABLE -                            if (!keyboard_protocol && 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 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); -                                osalSysUnlockFromISR(); -                            }                          }                          usbSetupTransfer(usbp, NULL, 0, NULL);                          return true; -                      case HID_REQ_SetIdle:                          keyboard_idle = setup->wValue.hbyte; -                        /* 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 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); -                            osalSysUnlockFromISR(); -                        } -                        usbSetupTransfer(usbp, NULL, 0, NULL); -                        return true; +                        return usb_set_idle_cb(usbp);                  }                  break;          } @@ -719,52 +315,40 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {          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); +    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) { +        if (usb_endpoints_in[i].usb_requests_cb != NULL) { +            if (usb_endpoints_in[i].usb_requests_cb(usbp)) { +                return true; +            }          }      }      return false;  } -static void usb_sof_cb(USBDriver *usbp) { -    osalSysLockFromISR(); -    for (int i = 0; i < NUM_USB_DRIVERS; i++) { -        qmkusbSOFHookI(&drivers.array[i].driver); -    } -    osalSysUnlockFromISR(); +static __attribute__((unused)) void dummy_cb(USBDriver *usbp) { +    (void)usbp;  } -/* USB driver configuration */  static const USBConfig usbcfg = {      usb_event_cb,          /* USB events callback */      usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */      usb_requests_hook_cb,  /* Requests hook callback */ -    usb_sof_cb             /* Start Of Frame callback */ +#if STM32_USB_USE_OTG1 == TRUE || STM32_USB_USE_OTG2 == TRUE +    dummy_cb, /* Workaround for OTG Peripherals not servicing new interrupts +    after resuming from suspend. */ +#endif  }; -/* - * Initialize the USB driver - */  void init_usb_driver(USBDriver *usbp) { -    for (int i = 0; i < NUM_USB_DRIVERS; i++) { -#ifdef USB_ENDPOINTS_ARE_REORDERABLE -        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 +    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) { +        usb_endpoint_in_init(&usb_endpoints_in[i]); +        usb_endpoint_in_start(&usb_endpoints_in[i]); +    } + +    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) { +        usb_endpoint_out_init(&usb_endpoints_out[i]); +        usb_endpoint_out_start(&usb_endpoints_out[i]);      }      /* @@ -777,23 +361,31 @@ void init_usb_driver(USBDriver *usbp) {      wait_ms(50);      usbStart(usbp, &usbcfg);      usbConnectBus(usbp); - -    chVTObjectInit(&keyboard_idle_timer);  }  __attribute__((weak)) void restart_usb_driver(USBDriver *usbp) {      usbDisconnectBus(usbp);      usbStop(usbp); -#if USB_SUSPEND_WAKEUP_DELAY > 0 -    // Some hubs, kvm switches, and monitors do -    // weird things, with USB device state bouncing -    // around wildly on wakeup, yielding race -    // conditions that can corrupt the keyboard state. -    // -    // Pause for a while to let things settle... -    wait_ms(USB_SUSPEND_WAKEUP_DELAY); -#endif +    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) { +        usb_endpoint_in_stop(&usb_endpoints_in[i]); +    } + +    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) { +        usb_endpoint_out_stop(&usb_endpoints_out[i]); +    } + +    wait_ms(50); + +    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) { +        usb_endpoint_in_init(&usb_endpoints_in[i]); +        usb_endpoint_in_start(&usb_endpoints_in[i]); +    } + +    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) { +        usb_endpoint_out_init(&usb_endpoints_out[i]); +        usb_endpoint_out_start(&usb_endpoints_out[i]); +    }      usbStart(usbp, &usbcfg);      usbConnectBus(usbp); @@ -804,81 +396,78 @@ __attribute__((weak)) void restart_usb_driver(USBDriver *usbp) {   * ---------------------------------------------------------   */ -/* Idle requests timer code - * callback (called from ISR, unlocked state) */ -static void keyboard_idle_timer_cb(struct ch_virtual_timer *timer, void *arg) { -    (void)timer; -    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 * TIME_MS2I(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 keyboard_led_state;  } -void send_report(uint8_t endpoint, void *report, size_t size) { -    osalSysLock(); -    if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { -        osalSysUnlock(); -        return; -    } +/** + * @brief Send a report to the host, the report is enqueued into an output + * queue and send once the USB endpoint becomes empty. + * + * @param endpoint USB IN endpoint to send the report from + * @param report pointer to the report + * @param size size of the report + * @return true Success + * @return false Failure + */ +bool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size) { +    return usb_endpoint_in_send(&usb_endpoints_in[endpoint], (uint8_t *)report, size, TIME_MS2I(100), false); +} -    if (usbGetTransmitStatusI(&USB_DRIVER, endpoint)) { -        /* 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[endpoint]->in_state->thread, TIME_MS2I(10)) == MSG_TIMEOUT) { -            osalSysUnlock(); -            return; -        } -    } -    usbStartTransmitI(&USB_DRIVER, endpoint, report, size); -    osalSysUnlock(); +/** + * @brief Send a report to the host, but delay the sending until the size of + * endpoint report is reached or the incompletely filled buffer is flushed with + * a call to `flush_report_buffered`. This is useful if the report is being + * updated frequently. The complete report is then enqueued into an output + * queue and send once the USB endpoint becomes empty. + * + * @param endpoint USB IN endpoint to send the report from + * @param report pointer to the report + * @param size size of the report + * @return true Success + * @return false Failure + */ +static bool send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size) { +    return usb_endpoint_in_send(&usb_endpoints_in[endpoint], (uint8_t *)report, size, TIME_MS2I(100), true); +} + +/** @brief Flush all buffered reports which were enqueued with a call to + * `send_report_buffered` that haven't been send. If necessary the buffered + * report can be padded with zeros up to the endpoints maximum size. + * + * @param endpoint USB IN endpoint to flush the reports from + * @param padded Pad the buffered report with zeros up to the endpoints maximum size + */ +static void flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded) { +    usb_endpoint_in_flush(&usb_endpoints_in[endpoint], padded); +} + +/** + * @brief Receive a report from the host. + * + * @param endpoint USB OUT endpoint to receive the report from + * @param report pointer to the report + * @param size size of the report + * @return true Success + * @return false Failure + */ +static bool receive_report(usb_endpoint_out_lut_t endpoint, void *report, size_t size) { +    return usb_endpoint_out_receive(&usb_endpoints_out[endpoint], (uint8_t *)report, size, TIME_IMMEDIATE);  } -/* prepare and start sending a report IN - * not callable from ISR or locked state */  void send_keyboard(report_keyboard_t *report) {      /* If we're in Boot Protocol, don't send any report ID or other funky fields */      if (!keyboard_protocol) { -        send_report(KEYBOARD_IN_EPNUM, &report->mods, 8); +        send_report(USB_ENDPOINT_IN_KEYBOARD, &report->mods, 8);      } else { -        send_report(KEYBOARD_IN_EPNUM, report, KEYBOARD_REPORT_SIZE); +        send_report(USB_ENDPOINT_IN_KEYBOARD, report, KEYBOARD_REPORT_SIZE);      } - -    keyboard_report_sent = *report;  }  void send_nkro(report_nkro_t *report) {  #ifdef NKRO_ENABLE -    send_report(SHARED_IN_EPNUM, report, sizeof(report_nkro_t)); +    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_nkro_t));  #endif  } @@ -889,8 +478,7 @@ void send_nkro(report_nkro_t *report) {  void send_mouse(report_mouse_t *report) {  #ifdef MOUSE_ENABLE -    send_report(MOUSE_IN_EPNUM, report, sizeof(report_mouse_t)); -    mouse_report_sent = *report; +    send_report(USB_ENDPOINT_IN_MOUSE, report, sizeof(report_mouse_t));  #endif  } @@ -901,25 +489,25 @@ void send_mouse(report_mouse_t *report) {  void send_extra(report_extra_t *report) {  #ifdef EXTRAKEY_ENABLE -    send_report(SHARED_IN_EPNUM, report, sizeof(report_extra_t)); +    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_extra_t));  #endif  }  void send_programmable_button(report_programmable_button_t *report) {  #ifdef PROGRAMMABLE_BUTTON_ENABLE -    send_report(SHARED_IN_EPNUM, report, sizeof(report_programmable_button_t)); +    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_programmable_button_t));  #endif  }  void send_joystick(report_joystick_t *report) {  #ifdef JOYSTICK_ENABLE -    send_report(JOYSTICK_IN_EPNUM, report, sizeof(report_joystick_t)); +    send_report(USB_ENDPOINT_IN_JOYSTICK, report, sizeof(report_joystick_t));  #endif  }  void send_digitizer(report_digitizer_t *report) {  #ifdef DIGITIZER_ENABLE -    send_report(DIGITIZER_IN_EPNUM, report, sizeof(report_digitizer_t)); +    send_report(USB_ENDPOINT_IN_DIGITIZER, report, sizeof(report_digitizer_t));  #endif  } @@ -931,46 +519,21 @@ void send_digitizer(report_digitizer_t *report) {  #ifdef CONSOLE_ENABLE  int8_t sendchar(uint8_t c) { -    rbuf_enqueue(c); -    return 0; +    return (int8_t)send_report_buffered(USB_ENDPOINT_IN_CONSOLE, &c, sizeof(uint8_t));  }  void console_task(void) { -    if (!rbuf_has_data()) { -        return; -    } - -    osalSysLock(); -    if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { -        osalSysUnlock(); -        return; -    } - -    if (usbGetTransmitStatusI(&USB_DRIVER, CONSOLE_IN_EPNUM)) { -        osalSysUnlock(); -        return; -    } - -    // Send in chunks - padded with zeros to 32 -    char    send_buf[CONSOLE_EPSIZE] = {0}; -    uint8_t send_buf_count           = 0; -    while (rbuf_has_data() && send_buf_count < CONSOLE_EPSIZE) { -        send_buf[send_buf_count++] = rbuf_dequeue(); -    } - -    usbStartTransmitI(&USB_DRIVER, CONSOLE_IN_EPNUM, (const uint8_t *)send_buf, CONSOLE_EPSIZE); -    osalSysUnlock(); +    flush_report_buffered(USB_ENDPOINT_IN_CONSOLE, true);  }  #endif /* CONSOLE_ENABLE */  #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); +    send_report(USB_ENDPOINT_IN_RAW, data, length);  }  __attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) { @@ -981,13 +544,9 @@ __attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) {  void raw_hid_task(void) {      uint8_t buffer[RAW_EPSIZE]; -    size_t  size = 0; -    do { -        size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); -        if (size > 0) { -            raw_hid_receive(buffer, size); -        } -    } while (size > 0); +    while (receive_report(USB_ENDPOINT_OUT_RAW, buffer, sizeof(buffer))) { +        raw_hid_receive(buffer, sizeof(buffer)); +    }  }  #endif @@ -995,32 +554,50 @@ void raw_hid_task(void) {  #ifdef MIDI_ENABLE  void send_midi_packet(MIDI_EventPacket_t *event) { -    chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); +    send_report(USB_ENDPOINT_IN_MIDI, (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); -} -void midi_ep_task(void) { -    uint8_t buffer[MIDI_STREAM_EPSIZE]; -    size_t  size = 0; -    do { -        size = chnReadTimeout(&drivers.midi_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); -        if (size > 0) { -            MIDI_EventPacket_t event; -            recv_midi_packet(&event); -        } -    } while (size > 0); +    return receive_report(USB_ENDPOINT_OUT_MIDI, (uint8_t *)event, sizeof(MIDI_EventPacket_t));  } +  #endif  #ifdef VIRTSER_ENABLE +#    include "hal_usb_cdc.h" +/** + * @brief CDC serial driver configuration structure. Set to 9600 baud, 1 stop bit, no parity, 8 data bits. + */ +static cdc_linecoding_t linecoding = {{0x00, 0x96, 0x00, 0x00}, LC_STOP_1, LC_PARITY_NONE, 8}; + +bool virtser_usb_request_cb(USBDriver *usbp) { +    if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) { /* bmRequestType */ +        if (usbp->setup[4] == CCI_INTERFACE) {                            /* wIndex (LSB) */ +            switch (usbp->setup[1]) {                                     /* bRequest */ +                case CDC_GET_LINE_CODING: +                    usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL); +                    return true; +                case CDC_SET_LINE_CODING: +                    usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL); +                    return true; +                case CDC_SET_CONTROL_LINE_STATE: +                    /* Nothing to do, there are no control lines.*/ +                    usbSetupTransfer(usbp, NULL, 0, NULL); +                    return true; +                default: +                    return false; +            } +        } +    } + +    return false; +} +  void virtser_init(void) {}  void virtser_send(const uint8_t byte) { -    chnWrite(&drivers.serial_driver.driver, &byte, 1); +    send_report_buffered(USB_ENDPOINT_IN_CDC_DATA, (void *)&byte, sizeof(byte));  }  __attribute__((weak)) void virtser_recv(uint8_t c) { @@ -1028,14 +605,14 @@ __attribute__((weak)) void virtser_recv(uint8_t c) {  }  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++) { +    uint8_t buffer[CDC_EPSIZE]; +    while (receive_report(USB_ENDPOINT_OUT_CDC_DATA, buffer, sizeof(buffer))) { +        for (int i = 0; i < sizeof(buffer); i++) {              virtser_recv(buffer[i]);          } -    } while (numBytesReceived > 0); +    } + +    flush_report_buffered(USB_ENDPOINT_IN_CDC_DATA, false);  }  #endif diff --git a/tmk_core/protocol/chibios/usb_main.h b/tmk_core/protocol/chibios/usb_main.h index 3fd1e84fe8..5ba6fb1961 100644 --- a/tmk_core/protocol/chibios/usb_main.h +++ b/tmk_core/protocol/chibios/usb_main.h @@ -1,25 +1,21 @@ -/* - * (c) 2015 flabberast <[email protected]> - * - * 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. - */ +// Copyright 2023 Stefan Kerkmann (@KarlK90) +// Copyright 2020 Ryan (@fauxpark) +// Copyright 2020 Joel Challis (@zvecr) +// Copyright 2018 James Laird-Wah +// Copyright 2016 Fredizzimo +// Copyright 2016 Giovanni Di Sirio +// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0  #pragma once  #include <ch.h>  #include <hal.h> +#include "usb_device_state.h" +#include "usb_descriptor.h" +#include "usb_driver.h" +#include "usb_endpoints.h" +  /* -------------------------   * General USB driver header   * ------------------------- @@ -36,6 +32,8 @@ void init_usb_driver(USBDriver *usbp);  /* Restart the USB driver and bus */  void restart_usb_driver(USBDriver *usbp); +bool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size); +  /* ---------------   * USB Event queue   * --------------- @@ -58,3 +56,14 @@ void usb_event_queue_task(void);  int8_t sendchar(uint8_t c);  #endif /* CONSOLE_ENABLE */ + +/* -------------- + * Virtser header + * -------------- + */ + +#if defined(VIRTSER_ENABLE) + +bool virtser_usb_request_cb(USBDriver *usbp); + +#endif diff --git a/tmk_core/protocol/chibios/usb_report_handling.c b/tmk_core/protocol/chibios/usb_report_handling.c new file mode 100644 index 0000000000..64074b2164 --- /dev/null +++ b/tmk_core/protocol/chibios/usb_report_handling.c @@ -0,0 +1,296 @@ +// Copyright 2023 Stefan Kerkmann (@KarlK90) +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <string.h> +#include <stdint.h> +#include <stdbool.h> + +#include "usb_report_handling.h" +#include "usb_endpoints.h" +#include "usb_main.h" +#include "usb_types.h" +#include "usb_driver.h" +#include "report.h" + +extern usb_endpoint_in_t     usb_endpoints_in[USB_ENDPOINT_IN_COUNT]; +extern usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES]; + +void usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) { +    if (*reports == NULL) { +        return; +    } + +    (*reports)->last_report = chVTGetSystemTimeX(); +    (*reports)->length      = length; +    memcpy(&(*reports)->data, data, length); +} + +void usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) { +    (void)report_id; +    if (*reports == NULL) { +        return; +    } + +    report->length = (*reports)->length; +    memcpy(&report->data, &(*reports)->data, report->length); +} + +void usb_reset_report(usb_fs_report_t **reports) { +    if (*reports == NULL) { +        return; +    } + +    memset(&(*reports)->data, 0, (*reports)->length); +    (*reports)->idle_rate   = 0; +    (*reports)->last_report = 0; +} + +void usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) { +    uint8_t report_id = data[0]; + +    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) { +        return; +    } + +    reports[report_id]->last_report = chVTGetSystemTimeX(); +    reports[report_id]->length      = length; +    memcpy(&reports[report_id]->data, data, length); +} + +void usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) { +    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) { +        return; +    } + +    report->length = reports[report_id]->length; +    memcpy(&report->data, &reports[report_id]->data, report->length); +} + +void usb_shared_reset_report(usb_fs_report_t **reports) { +    for (int i = 0; i <= REPORT_ID_COUNT; i++) { +        if (reports[i] == NULL) { +            continue; +        } +        memset(&reports[i]->data, 0, reports[i]->length); +        reports[i]->idle_rate   = 0; +        reports[i]->last_report = 0; +    } +} + +bool usb_get_report_cb(USBDriver *driver) { +    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup; +    uint8_t                interface = setup->wIndex; +    uint8_t                report_id = setup->wValue.lbyte; + +    static usb_fs_report_t report; + +    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) { +        return false; +    } + +    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface]; + +    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) { +        return false; +    } + +    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage; + +    if (report_storage == NULL) { +        return false; +    } + +    report_storage->get_report(report_storage->reports, report_id, &report); + +    usbSetupTransfer(driver, (uint8_t *)report.data, report.length, NULL); + +    return true; +} + +static bool run_idle_task = false; + +void usb_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) { +    (void)report_id; + +    if (*reports == NULL) { +        return; +    } + +    (*reports)->idle_rate = idle_rate * 4; + +    run_idle_task |= idle_rate != 0; +} + +uint8_t usb_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) { +    (void)report_id; + +    if (*reports == NULL) { +        return 0; +    } + +    return (*reports)->idle_rate / 4; +} + +bool usb_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) { +    (void)report_id; + +    if (*reports == NULL) { +        return false; +    } + +    osalSysLock(); +    time_msecs_t idle_rate   = (*reports)->idle_rate; +    systime_t    last_report = (*reports)->last_report; +    osalSysUnlock(); + +    if (idle_rate == 0) { +        return false; +    } + +    return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate; +} + +void usb_shared_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) { +    // USB spec demands that a report_id of 0 would set the idle rate for all +    // reports of that endpoint, but this can easily lead to resource +    // exhaustion, therefore we deliberalty break the spec at this point. +    if (report_id == 0 || report_id > REPORT_ID_COUNT || reports[report_id] == NULL) { +        return; +    } + +    reports[report_id]->idle_rate = idle_rate * 4; + +    run_idle_task |= idle_rate != 0; +} + +uint8_t usb_shared_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) { +    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) { +        return 0; +    } + +    return reports[report_id]->idle_rate / 4; +} + +bool usb_shared_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) { +    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) { +        return false; +    } + +    osalSysLock(); +    time_msecs_t idle_rate   = reports[report_id]->idle_rate; +    systime_t    last_report = reports[report_id]->last_report; +    osalSysUnlock(); + +    if (idle_rate == 0) { +        return false; +    } + +    return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate; +} + +void usb_idle_task(void) { +    if (!run_idle_task) { +        return; +    } + +    static usb_fs_report_t report; +    bool                   non_zero_idle_rate_found = false; + +    for (int ep = 0; ep < USB_ENDPOINT_IN_COUNT; ep++) { +        usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage; + +        if (report_storage == NULL) { +            continue; +        } + +#if defined(SHARED_EP_ENABLE) +        if (ep == USB_ENDPOINT_IN_SHARED) { +            for (int report_id = 1; report_id <= REPORT_ID_COUNT; report_id++) { +                osalSysLock(); +                non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, report_id) != 0; +                osalSysUnlock(); + +                if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, report_id)) { +                    osalSysLock(); +                    report_storage->get_report(report_storage->reports, report_id, &report); +                    osalSysUnlock(); +                    send_report(ep, &report.data, report.length); +                } +            } +            continue; +        } +#endif + +        osalSysLock(); +        non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, 0) != 0; +        osalSysUnlock(); + +        if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, 0)) { +            osalSysLock(); +            report_storage->get_report(report_storage->reports, 0, &report); +            osalSysUnlock(); +            send_report(ep, &report.data, report.length); +        } +    } + +    run_idle_task = non_zero_idle_rate_found; +} + +bool usb_get_idle_cb(USBDriver *driver) { +    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup; +    uint8_t                interface = setup->wIndex; +    uint8_t                report_id = setup->wValue.lbyte; + +    static uint8_t _Alignas(4) idle_rate; + +    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) { +        return false; +    } + +    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface]; + +    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) { +        return false; +    } + +    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage; + +    if (report_storage == NULL) { +        return false; +    } + +    idle_rate = report_storage->get_idle(report_storage->reports, report_id); + +    usbSetupTransfer(driver, &idle_rate, 1, NULL); + +    return true; +} + +bool usb_set_idle_cb(USBDriver *driver) { +    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup; +    uint8_t                interface = setup->wIndex; +    uint8_t                report_id = setup->wValue.lbyte; +    uint8_t                idle_rate = setup->wValue.hbyte; + +    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) { +        return false; +    } + +    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface]; + +    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) { +        return false; +    } + +    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage; + +    if (report_storage == NULL) { +        return false; +    } + +    report_storage->set_idle(report_storage->reports, report_id, idle_rate); + +    usbSetupTransfer(driver, NULL, 0, NULL); + +    return true; +} diff --git a/tmk_core/protocol/chibios/usb_report_handling.h b/tmk_core/protocol/chibios/usb_report_handling.h new file mode 100644 index 0000000000..18b4ce5e26 --- /dev/null +++ b/tmk_core/protocol/chibios/usb_report_handling.h @@ -0,0 +1,77 @@ +// Copyright 2023 Stefan Kerkmann (@KarlK90) +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <ch.h> +#include <hal.h> +#include <stdint.h> + +#include "timer.h" + +typedef struct { +    time_msecs_t idle_rate; +    systime_t    last_report; +    uint8_t      data[64]; +    size_t       length; +} usb_fs_report_t; + +typedef struct { +    usb_fs_report_t **reports; +    const void (*get_report)(usb_fs_report_t **, uint8_t, usb_fs_report_t *); +    const void (*set_report)(usb_fs_report_t **, const uint8_t *, size_t); +    const void (*reset_report)(usb_fs_report_t **); +    const void (*set_idle)(usb_fs_report_t **, uint8_t, uint8_t); +    const uint8_t (*get_idle)(usb_fs_report_t **, uint8_t); +    const bool (*idle_timer_elasped)(usb_fs_report_t **, uint8_t); +} usb_report_storage_t; + +#define QMK_USB_REPORT_STROAGE_ENTRY(_report_id, _report_size) [_report_id] = &((usb_fs_report_t){.data = {[0] = _report_id}, .length = _report_size}) + +#define QMK_USB_REPORT_STORAGE(_get_report, _set_report, _reset_report, _get_idle, _set_idle, _idle_timer_elasped, _report_count, _reports...) \ +    &((usb_report_storage_t){                                                                                                                  \ +        .reports            = (_Alignas(4) usb_fs_report_t *[_report_count]){_reports},                                                        \ +        .get_report         = _get_report,                                                                                                     \ +        .set_report         = _set_report,                                                                                                     \ +        .reset_report       = _reset_report,                                                                                                   \ +        .get_idle           = _get_idle,                                                                                                       \ +        .set_idle           = _set_idle,                                                                                                       \ +        .idle_timer_elasped = _idle_timer_elasped,                                                                                             \ +    }) + +#define QMK_USB_REPORT_STORAGE_DEFAULT(_report_length)                        \ +    QMK_USB_REPORT_STORAGE(&usb_get_report,         /* _get_report */         \ +                           &usb_set_report,         /* _set_report */         \ +                           &usb_reset_report,       /* _reset_report */       \ +                           &usb_get_idle_rate,      /* _get_idle */           \ +                           &usb_set_idle_rate,      /* _set_idle */           \ +                           &usb_idle_timer_elapsed, /* _idle_timer_elasped */ \ +                           1,                       /* _report_count */       \ +                           QMK_USB_REPORT_STROAGE_ENTRY(0, _report_length)) + +// USB HID SET_REPORT and GET_REPORT  handling functions +void usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length); +void usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length); + +void usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report); +void usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report); + +void usb_reset_report(usb_fs_report_t **reports); +void usb_shared_reset_report(usb_fs_report_t **reports); + +bool usb_get_report_cb(USBDriver *driver); + +// USB HID SET_IDLE and GET_IDLE handling functions +void usb_idle_task(void); + +void usb_set_idle_rate(usb_fs_report_t **timers, uint8_t report_id, uint8_t idle_rate); +void usb_shared_set_idle_rate(usb_fs_report_t **timers, uint8_t report_id, uint8_t idle_rate); + +uint8_t usb_get_idle_rate(usb_fs_report_t **timers, uint8_t report_id); +uint8_t usb_shared_get_idle_rate(usb_fs_report_t **timers, uint8_t report_id); + +bool usb_idle_timer_elapsed(usb_fs_report_t **timers, uint8_t report_id); +bool usb_shared_idle_timer_elapsed(usb_fs_report_t **timers, uint8_t report_id); + +bool usb_get_idle_cb(USBDriver *driver); +bool usb_set_idle_cb(USBDriver *driver); diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 22cc0db8ce..2142b04460 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -67,6 +67,11 @@  #    include "raw_hid.h"  #endif +#ifdef WAIT_FOR_USB +// TODO: Remove backwards compatibility with old define +#    define USB_WAIT_FOR_ENUMERATION +#endif +  uint8_t keyboard_idle = 0;  /* 0: Boot Protocol, 1: Report Protocol(default) */  uint8_t        keyboard_protocol  = 1; @@ -151,7 +156,7 @@ __attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) {   *   * FIXME: Needs doc   */ -static void raw_hid_task(void) { +void raw_hid_task(void) {      // Create a temporary buffer to hold the read in data from the host      uint8_t data[RAW_EPSIZE];      bool    data_read = false; @@ -807,7 +812,7 @@ void protocol_pre_init(void) {      /* wait for USB startup & debug output */ -#ifdef WAIT_FOR_USB +#ifdef USB_WAIT_FOR_ENUMERATION      while (USB_DeviceState != DEVICE_STATE_Configured) {  #    if defined(INTERRUPT_CONTROL_ENDPOINT)          ; @@ -865,10 +870,6 @@ void protocol_post_task(void) {      CDC_Device_USBTask(&cdc_device);  #endif -#ifdef RAW_ENABLE -    raw_hid_task(); -#endif -  #if !defined(INTERRUPT_CONTROL_ENDPOINT)      USB_USBTask();  #endif diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h index 47bc4f2f2b..0e4f6e9def 100644 --- a/tmk_core/protocol/report.h +++ b/tmk_core/protocol/report.h @@ -29,7 +29,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  // clang-format off  /* HID report IDs */ -enum hid_report_ids { +enum hid_report_ids {  +    REPORT_ID_ALL = 0,      REPORT_ID_KEYBOARD = 1,      REPORT_ID_MOUSE,      REPORT_ID_SYSTEM, @@ -37,9 +38,12 @@ enum hid_report_ids {      REPORT_ID_PROGRAMMABLE_BUTTON,      REPORT_ID_NKRO,      REPORT_ID_JOYSTICK, -    REPORT_ID_DIGITIZER +    REPORT_ID_DIGITIZER, +    REPORT_ID_COUNT = REPORT_ID_DIGITIZER  }; +#define IS_VALID_REPORT_ID(id) ((id) >= REPORT_ID_ALL && (id) <= REPORT_ID_COUNT) +  /* Mouse buttons */  #define MOUSE_BTN_MASK(n) (1 << (n))  enum mouse_buttons { diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 2469990f4d..ecfb022702 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -196,6 +196,8 @@ enum usb_interfaces {      TOTAL_INTERFACES  }; +#define IS_VALID_INTERFACE(i) ((i) >= 0 && (i) < TOTAL_INTERFACES) +  #define NEXT_EPNUM __COUNTER__  /* diff --git a/tmk_core/protocol/vusb/protocol.c b/tmk_core/protocol/vusb/protocol.c index 6178d48ef2..41ccf451fd 100644 --- a/tmk_core/protocol/vusb/protocol.c +++ b/tmk_core/protocol/vusb/protocol.c @@ -31,14 +31,6 @@  #    include "sleep_led.h"  #endif -#ifdef CONSOLE_ENABLE -void console_task(void); -#endif - -#ifdef RAW_ENABLE -void raw_hid_task(void); -#endif -  /* This is from main.c of USBaspLoader */  static void initForUsbConnectivity(void) {      uint8_t i = 0; @@ -136,7 +128,7 @@ static inline bool should_do_suspend(void) {      return vusb_suspended;  } -void protocol_task(void) { +void protocol_pre_task(void) {  #if !defined(NO_USB_STARTUP_CHECK)      if (should_do_suspend()) {          dprintln("suspending keyboard"); @@ -159,7 +151,9 @@ void protocol_task(void) {          vusb_wakeup();      }  #endif +} +void protocol_keyboard_task(void) {      usbPoll();      // TODO: configuration process is inconsistent. it sometime fails. @@ -167,20 +161,8 @@ void protocol_task(void) {      if (usbConfiguration && usbInterruptIsReady()) {          keyboard_task();      } +} -#ifdef RAW_ENABLE -    usbPoll(); - -    if (usbConfiguration && usbInterruptIsReady4()) { -        raw_hid_task(); -    } -#endif - -#ifdef CONSOLE_ENABLE -    usbPoll(); - -    if (usbConfiguration && usbInterruptIsReady3()) { -        console_task(); -    } -#endif +void protocol_post_task(void) { +    // do nothing  } diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index cfeeed3712..c8ab494253 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -162,6 +162,12 @@ __attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) {  }  void raw_hid_task(void) { +    usbPoll(); + +    if (!usbConfiguration || !usbInterruptIsReady4()) { +        return; +    } +      if (raw_output_received_bytes == RAW_BUFFER_SIZE) {          raw_hid_receive(raw_output_buffer, RAW_BUFFER_SIZE);          raw_output_received_bytes = 0; @@ -182,7 +188,9 @@ int8_t sendchar(uint8_t c) {  }  void console_task(void) { -    if (!usbConfiguration) { +    usbPoll(); + +    if (!usbConfiguration || !usbInterruptIsReady3()) {          return;      } | 
