forked from mirrors/qmk_userspace
Pick upstream PR #21656
This commit is contained in:
parent
dda14805a1
commit
b6d42d84d2
13 changed files with 1292 additions and 1158 deletions
|
@ -192,14 +192,18 @@ void protocol_pre_task(void) {
|
|||
/* Remote wakeup */
|
||||
if ((USB_DRIVER.status & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED) && suspend_wakeup_condition()) {
|
||||
usbWakeupHost(&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
|
||||
}
|
||||
|
@ -217,4 +221,5 @@ void protocol_post_task(void) {
|
|||
#ifdef RAW_ENABLE
|
||||
raw_hid_task();
|
||||
#endif
|
||||
usb_idle_task();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
usbStartReceiveI(endpoint->config.usbp, endpoint->config.ep, buffer, endpoint->ibqueue.bsize - sizeof(size_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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((qmkusbp->state == QMKUSB_STOP) || (qmkusbp->state == QMKUSB_READY), "invalid state");
|
||||
|
||||
/* 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;
|
||||
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();
|
||||
}
|
||||
qmkusbp->config = NULL;
|
||||
qmkusbp->state = QMKUSB_STOP;
|
||||
|
||||
/* Enforces a disconnection.*/
|
||||
chnAddFlagsI(qmkusbp, CHN_DISCONNECTED);
|
||||
ibqResetI(&qmkusbp->ibqueue);
|
||||
obqResetI(&qmkusbp->obqueue);
|
||||
void usb_endpoint_in_stop(usb_endpoint_in_t *endpoint) {
|
||||
osalDbgCheck(endpoint != NULL);
|
||||
|
||||
osalSysLock();
|
||||
endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = NULL;
|
||||
|
||||
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);
|
||||
|
||||
osalSysLock();
|
||||
osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state");
|
||||
|
||||
bqSuspendI(&endpoint->ibqueue);
|
||||
ibqResetI(&endpoint->ibqueue);
|
||||
osalOsRescheduleS();
|
||||
osalSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
void usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint) {
|
||||
bqSuspendI(&endpoint->obqueue);
|
||||
obqResetI(&endpoint->obqueue);
|
||||
|
||||
if (endpoint->report_storage != NULL) {
|
||||
endpoint->report_storage->reset_report(endpoint->report_storage->reports);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
void usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint) {
|
||||
bqSuspendI(&endpoint->ibqueue);
|
||||
ibqResetI(&endpoint->ibqueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_wakeup_cb(usb_endpoint_in_t *endpoint) {
|
||||
bqResumeX(&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;
|
||||
void usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint) {
|
||||
bqResumeX(&endpoint->ibqueue);
|
||||
}
|
||||
|
||||
/* 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_in_configure_cb(usb_endpoint_in_t *endpoint) {
|
||||
usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config);
|
||||
obqResetI(&endpoint->obqueue);
|
||||
bqResumeX(&endpoint->obqueue);
|
||||
}
|
||||
|
||||
/* Checking if there only a buffer partially filled, if so then it is
|
||||
enforced in the queue and transmitted.*/
|
||||
if (obqTryFlushI(&qmkusbp->obqueue)) {
|
||||
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);
|
||||
}
|
||||
|
||||
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 *buf = obqGetFullBufferI(&qmkusbp->obqueue, &n);
|
||||
uint8_t * buffer;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
osalDbgAssert(buf != NULL, "queue is empty");
|
||||
|
||||
usbStartTransmitI(qmkusbp->config->usbp, qmkusbp->config->bulk_in, buf, n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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];
|
||||
|
||||
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) {
|
||||
* 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);
|
||||
|
||||
size_t size = usbGetReceiveTransactionSizeX(usbp, ep);
|
||||
if (size > 0) {
|
||||
/* Posting the filled buffer in the queue.*/
|
||||
ibqPostFullBufferI(&qmkusbp->ibqueue, usbGetReceiveTransactionSizeX(qmkusbp->config->usbp, qmkusbp->config->bulk_out));
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
/*
|
||||
* 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 enum {
|
||||
QMKUSB_UNINIT = 0, /**< Not initialized. */
|
||||
QMKUSB_STOP = 1, /**< Stopped. */
|
||||
QMKUSB_READY = 2 /**< Ready. */
|
||||
} qmkusbstate_t;
|
||||
#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}, \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Structure representing a serial over USB driver.
|
||||
*/
|
||||
typedef struct QMKUSBDriver QMKUSBDriver;
|
||||
#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.
|
||||
*/
|
||||
usbep_t int_in;
|
||||
|
||||
/**
|
||||
* @brief The number of buffers in the queues
|
||||
* @brief Endpoint used for data transfer
|
||||
*/
|
||||
size_t in_buffers;
|
||||
size_t out_buffers;
|
||||
usbep_t ep;
|
||||
|
||||
/**
|
||||
* @brief The size of each buffer in the queue, typically the same as the endpoint size
|
||||
* @brief The number of buffers in the queue
|
||||
*/
|
||||
size_t in_size;
|
||||
size_t out_size;
|
||||
size_t buffer_capacity;
|
||||
|
||||
/**
|
||||
* @brief Always send full buffers in_size (the rest is filled with zeroes)
|
||||
* @brief The size of each buffer in the queue, same as the endpoint size
|
||||
*/
|
||||
bool fixed_size;
|
||||
|
||||
/* 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;
|
||||
size_t buffer_size;
|
||||
|
||||
/**
|
||||
* @brief @p SerialDriver specific data.
|
||||
* @brief Buffer backing storage
|
||||
*/
|
||||
#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;
|
||||
uint8_t *buffer;
|
||||
} usb_endpoint_config_t;
|
||||
|
||||
/**
|
||||
* @brief @p SerialUSBDriver specific methods.
|
||||
*/
|
||||
#define _qmk_usb_driver_methods _base_asynchronous_channel_methods
|
||||
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;
|
||||
|
||||
/**
|
||||
* @extends BaseAsynchronousChannelVMT
|
||||
*
|
||||
* @brief @p SerialDriver virtual methods table.
|
||||
*/
|
||||
struct QMKUSBDriverVMT {
|
||||
_qmk_usb_driver_methods
|
||||
};
|
||||
|
||||
/**
|
||||
* @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
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
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;
|
||||
|
||||
#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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
|
152
tmk_core/protocol/chibios/usb_endpoints.c
Normal file
152
tmk_core/protocol/chibios/usb_endpoints.c
Normal file
|
@ -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
|
||||
};
|
137
tmk_core/protocol/chibios/usb_endpoints.h
Normal file
137
tmk_core/protocol/chibios/usb_endpoints.h
Normal file
|
@ -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;
|
|
@ -1,45 +1,32 @@
|
|||
/*
|
||||
* (c) 2015 flabberast <s3+flabbergast@sdfeu.org>
|
||||
*
|
||||
* 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"
|
||||
|
@ -64,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;
|
||||
extern usb_endpoint_in_t usb_endpoints_in[USB_ENDPOINT_IN_COUNT];
|
||||
extern usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT];
|
||||
|
||||
uint8_t _Alignas(2) keyboard_idle = 0;
|
||||
uint8_t _Alignas(2) keyboard_protocol = 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);
|
||||
|
||||
report_keyboard_t keyboard_report_sent = {0};
|
||||
report_mouse_t mouse_report_sent = {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
|
||||
|
@ -104,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;
|
||||
|
||||
|
@ -118,291 +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 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 CONSOLE_ENABLE
|
||||
usb_driver_config_t console_driver;
|
||||
#endif
|
||||
#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 CONSOLE_ENABLE
|
||||
# define CONSOLE_IN_CAPACITY 4
|
||||
# define CONSOLE_OUT_CAPACITY 4
|
||||
# define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR
|
||||
# define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR
|
||||
.console_driver = QMK_USB_DRIVER_CONFIG(CONSOLE, 0, true),
|
||||
#endif
|
||||
#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
|
||||
* ---------------------------------------------------------
|
||||
|
@ -494,33 +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
|
||||
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);
|
||||
for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
|
||||
usb_endpoint_in_configure_cb(&usb_endpoints_in[i]);
|
||||
}
|
||||
qmkusbConfigureHookI(&drivers.array[i].driver);
|
||||
for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
|
||||
usb_endpoint_out_configure_cb(&usb_endpoints_out[i]);
|
||||
}
|
||||
osalSysUnlockFromISR();
|
||||
if (last_suspend_state) {
|
||||
|
@ -534,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();
|
||||
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();
|
||||
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;
|
||||
|
||||
|
@ -571,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;
|
||||
|
@ -595,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);
|
||||
|
@ -638,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) {
|
||||
|
@ -655,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;
|
||||
}
|
||||
|
@ -703,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]);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -761,8 +361,6 @@ void init_usb_driver(USBDriver *usbp) {
|
|||
wait_ms(50);
|
||||
usbStart(usbp, &usbcfg);
|
||||
usbConnectBus(usbp);
|
||||
|
||||
chVTObjectInit(&keyboard_idle_timer);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void usb_start(USBDriver *usbp) {
|
||||
|
@ -775,81 +373,78 @@ __attribute__((weak)) void usb_start(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
|
||||
}
|
||||
|
||||
|
@ -860,8 +455,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
|
||||
}
|
||||
|
||||
|
@ -872,25 +466,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
|
||||
}
|
||||
|
||||
|
@ -902,61 +496,21 @@ void send_digitizer(report_digitizer_t *report) {
|
|||
#ifdef CONSOLE_ENABLE
|
||||
|
||||
int8_t sendchar(uint8_t c) {
|
||||
static bool timed_out = false;
|
||||
/* The `timed_out` state is an approximation of the ideal `is_listener_disconnected?` state.
|
||||
*
|
||||
* When a 5ms timeout write has timed out, hid_listen is most likely not running, or not
|
||||
* listening to this keyboard, so we go into the timed_out state. In this state we assume
|
||||
* that hid_listen is most likely not gonna be connected to us any time soon, so it would
|
||||
* be wasteful to write follow-up characters with a 5ms timeout, it would all add up and
|
||||
* unncecessarily slow down the firmware. However instead of just dropping the characters,
|
||||
* we write them with a TIME_IMMEDIATE timeout, which is a zero timeout,
|
||||
* and this will succeed only if hid_listen gets connected again. When a write with
|
||||
* TIME_IMMEDIATE timeout succeeds, we know that hid_listen is listening to us again, and
|
||||
* we can go back to the timed_out = false state, and following writes will be executed
|
||||
* with a 5ms timeout. The reason we don't just send all characters with the TIME_IMMEDIATE
|
||||
* timeout is that this could cause bytes to be lost even if hid_listen is running, if there
|
||||
* is a lot of data being sent over the console.
|
||||
*
|
||||
* This logic will work correctly as long as hid_listen is able to receive at least 200
|
||||
* bytes per second. On a heavily overloaded machine that's so overloaded that it's
|
||||
* unusable, and constantly swapping, hid_listen might have trouble receiving 200 bytes per
|
||||
* second, so some bytes might be lost on the console.
|
||||
*/
|
||||
|
||||
const sysinterval_t timeout = timed_out ? TIME_IMMEDIATE : TIME_MS2I(5);
|
||||
const size_t result = chnWriteTimeout(&drivers.console_driver.driver, &c, 1, timeout);
|
||||
timed_out = (result == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Just a dummy function for now, this could be exposed as a weak function
|
||||
// Or connected to the actual QMK console
|
||||
static void console_receive(uint8_t *data, uint8_t length) {
|
||||
(void)data;
|
||||
(void)length;
|
||||
return (int8_t)send_report_buffered(USB_ENDPOINT_IN_CONSOLE, &c, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
void console_task(void) {
|
||||
uint8_t buffer[CONSOLE_EPSIZE];
|
||||
size_t size = 0;
|
||||
do {
|
||||
size = chnReadTimeout(&drivers.console_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
|
||||
if (size > 0) {
|
||||
console_receive(buffer, size);
|
||||
}
|
||||
} while (size > 0);
|
||||
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) {
|
||||
|
@ -967,13 +521,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 (receive_report(USB_ENDPOINT_OUT_RAW, buffer, sizeof(buffer))) {
|
||||
raw_hid_receive(buffer, sizeof(buffer));
|
||||
}
|
||||
} while (size > 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -981,32 +531,59 @@ 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);
|
||||
return receive_report(USB_ENDPOINT_OUT_MIDI, (uint8_t *)event, 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) {
|
||||
while (receive_report(USB_ENDPOINT_OUT_MIDI, buffer, sizeof(buffer))) {
|
||||
MIDI_EventPacket_t event;
|
||||
// TODO: this seems totally wrong? The midi task will never see any
|
||||
// packets if we consume them here
|
||||
recv_midi_packet(&event);
|
||||
}
|
||||
} while (size > 0);
|
||||
}
|
||||
#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) {
|
||||
|
@ -1014,14 +591,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
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
/*
|
||||
* (c) 2015 flabberast <s3+flabbergast@sdfeu.org>
|
||||
*
|
||||
* 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);
|
|||
/* Start the USB driver */
|
||||
void usb_start(USBDriver *usbp);
|
||||
|
||||
bool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size);
|
||||
|
||||
/* ---------------
|
||||
* USB Event queue
|
||||
* ---------------
|
||||
|
@ -57,7 +55,15 @@ void usb_event_queue_task(void);
|
|||
/* Putchar over the USB console */
|
||||
int8_t sendchar(uint8_t c);
|
||||
|
||||
/* Flush output (send everything immediately) */
|
||||
void console_flush_output(void);
|
||||
|
||||
#endif /* CONSOLE_ENABLE */
|
||||
|
||||
/* --------------
|
||||
* Virtser header
|
||||
* --------------
|
||||
*/
|
||||
|
||||
#if defined(VIRTSER_ENABLE)
|
||||
|
||||
bool virtser_usb_request_cb(USBDriver *usbp);
|
||||
|
||||
#endif
|
||||
|
|
296
tmk_core/protocol/chibios/usb_report_handling.c
Normal file
296
tmk_core/protocol/chibios/usb_report_handling.c
Normal file
|
@ -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;
|
||||
}
|
77
tmk_core/protocol/chibios/usb_report_handling.h
Normal file
77
tmk_core/protocol/chibios/usb_report_handling.h
Normal file
|
@ -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);
|
|
@ -26,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
/* HID report IDs */
|
||||
enum hid_report_ids {
|
||||
REPORT_ID_ALL = 0,
|
||||
REPORT_ID_KEYBOARD = 1,
|
||||
REPORT_ID_MOUSE,
|
||||
REPORT_ID_SYSTEM,
|
||||
|
@ -33,9 +34,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)
|
||||
|
||||
#ifdef APDAPTIVE_NKRO_ENABLE
|
||||
/* Keyboard report type */
|
||||
#define KB_RPT_MASK(n) (1 << (n))
|
||||
|
|
|
@ -420,14 +420,6 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {
|
|||
HID_RI_REPORT_COUNT(8, CONSOLE_EPSIZE),
|
||||
HID_RI_REPORT_SIZE(8, 0x08),
|
||||
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||
|
||||
// Data from host
|
||||
HID_RI_USAGE(8, 0x76), // Vendor Defined
|
||||
HID_RI_LOGICAL_MINIMUM(8, 0x00),
|
||||
HID_RI_LOGICAL_MAXIMUM(16, 0x00FF),
|
||||
HID_RI_REPORT_COUNT(8, CONSOLE_EPSIZE),
|
||||
HID_RI_REPORT_SIZE(8, 0x08),
|
||||
HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE),
|
||||
HID_RI_END_COLLECTION(0),
|
||||
};
|
||||
#endif
|
||||
|
@ -677,7 +669,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
|
|||
},
|
||||
.InterfaceNumber = CONSOLE_INTERFACE,
|
||||
.AlternateSetting = 0x00,
|
||||
.TotalEndpoints = 2,
|
||||
.TotalEndpoints = 1,
|
||||
.Class = HID_CSCP_HIDClass,
|
||||
.SubClass = HID_CSCP_NonBootSubclass,
|
||||
.Protocol = HID_CSCP_NonBootProtocol,
|
||||
|
@ -704,16 +696,6 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
|
|||
.EndpointSize = CONSOLE_EPSIZE,
|
||||
.PollingIntervalMS = 0x01
|
||||
},
|
||||
.Console_OUTEndpoint = {
|
||||
.Header = {
|
||||
.Size = sizeof(USB_Descriptor_Endpoint_t),
|
||||
.Type = DTYPE_Endpoint
|
||||
},
|
||||
.EndpointAddress = (ENDPOINT_DIR_OUT | CONSOLE_OUT_EPNUM),
|
||||
.Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
|
||||
.EndpointSize = CONSOLE_EPSIZE,
|
||||
.PollingIntervalMS = 0x01
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef MIDI_ENABLE
|
||||
|
|
|
@ -97,7 +97,6 @@ typedef struct {
|
|||
USB_Descriptor_Interface_t Console_Interface;
|
||||
USB_HID_Descriptor_HID_t Console_HID;
|
||||
USB_Descriptor_Endpoint_t Console_INEndpoint;
|
||||
USB_Descriptor_Endpoint_t Console_OUTEndpoint;
|
||||
#endif
|
||||
|
||||
#ifdef MIDI_ENABLE
|
||||
|
@ -197,6 +196,8 @@ enum usb_interfaces {
|
|||
TOTAL_INTERFACES
|
||||
};
|
||||
|
||||
#define IS_VALID_INTERFACE(i) ((i) >= 0 && (i) < TOTAL_INTERFACES)
|
||||
|
||||
#define NEXT_EPNUM __COUNTER__
|
||||
|
||||
/*
|
||||
|
@ -232,19 +233,6 @@ enum usb_endpoints {
|
|||
|
||||
#ifdef CONSOLE_ENABLE
|
||||
CONSOLE_IN_EPNUM = NEXT_EPNUM,
|
||||
|
||||
# ifdef PROTOCOL_CHIBIOS
|
||||
// ChibiOS has enough memory and descriptor to actually enable the endpoint
|
||||
// It could use the same endpoint numbers, as that's supported by ChibiOS
|
||||
// But the QMK code currently assumes that the endpoint numbers are different
|
||||
# ifdef USB_ENDPOINTS_ARE_REORDERABLE
|
||||
# define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM
|
||||
# else
|
||||
CONSOLE_OUT_EPNUM = NEXT_EPNUM,
|
||||
# endif
|
||||
# else
|
||||
# define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef MIDI_ENABLE
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue