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((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state");
 | 
			
		||||
    endpoint->config.usbp->out_params[endpoint->config.ep - 1U] = endpoint;
 | 
			
		||||
    endpoint->timed_out                                         = false;
 | 
			
		||||
    osalSysUnlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    osalDbgAssert((qmkusbp->state == QMKUSB_STOP) || (qmkusbp->state == QMKUSB_READY), "invalid state");
 | 
			
		||||
void usb_endpoint_in_stop(usb_endpoint_in_t *endpoint) {
 | 
			
		||||
    osalDbgCheck(endpoint != NULL);
 | 
			
		||||
 | 
			
		||||
    /* Driver in stopped state.*/
 | 
			
		||||
    usbp->in_params[qmkusbp->config->bulk_in - 1U]   = NULL;
 | 
			
		||||
    usbp->out_params[qmkusbp->config->bulk_out - 1U] = NULL;
 | 
			
		||||
    if (qmkusbp->config->int_in > 0U) {
 | 
			
		||||
        usbp->in_params[qmkusbp->config->int_in - 1U] = NULL;
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
    qmkusbp->config = NULL;
 | 
			
		||||
    qmkusbp->state  = QMKUSB_STOP;
 | 
			
		||||
 | 
			
		||||
    /* Enforces a disconnection.*/
 | 
			
		||||
    chnAddFlagsI(qmkusbp, CHN_DISCONNECTED);
 | 
			
		||||
    ibqResetI(&qmkusbp->ibqueue);
 | 
			
		||||
    obqResetI(&qmkusbp->obqueue);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   SOF handler.
 | 
			
		||||
 * @details The SOF interrupt is used for automatic flushing of incomplete
 | 
			
		||||
 *          buffers pending in the output queue.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] qmkusbp      pointer to a @p QMKUSBDriver object
 | 
			
		||||
 *
 | 
			
		||||
 * @iclass
 | 
			
		||||
 */
 | 
			
		||||
void qmkusbSOFHookI(QMKUSBDriver *qmkusbp) {
 | 
			
		||||
    /* If the USB driver is not in the appropriate state then transactions
 | 
			
		||||
       must not be started.*/
 | 
			
		||||
    if ((usbGetDriverStateI(qmkusbp->config->usbp) != USB_ACTIVE) || (qmkusbp->state != QMKUSB_READY)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If there is already a transaction ongoing then another one cannot be
 | 
			
		||||
       started.*/
 | 
			
		||||
    if (usbGetTransmitStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_in)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Checking if there only a buffer partially filled, if so then it is
 | 
			
		||||
       enforced in the queue and transmitted.*/
 | 
			
		||||
    if (obqTryFlushI(&qmkusbp->obqueue)) {
 | 
			
		||||
        size_t   n;
 | 
			
		||||
        uint8_t *buf = obqGetFullBufferI(&qmkusbp->obqueue, &n);
 | 
			
		||||
 | 
			
		||||
        /* 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);
 | 
			
		||||
    if (endpoint->report_storage != NULL) {
 | 
			
		||||
        endpoint->report_storage->reset_report(endpoint->report_storage->reports);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Default data transmitted callback.
 | 
			
		||||
 * @details The application must use this function as callback for the IN
 | 
			
		||||
 *          data endpoint.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] usbp      pointer to the @p USBDriver object
 | 
			
		||||
 * @param[in] ep        IN endpoint number
 | 
			
		||||
 */
 | 
			
		||||
void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep) {
 | 
			
		||||
    uint8_t *     buf;
 | 
			
		||||
    size_t        n;
 | 
			
		||||
    QMKUSBDriver *qmkusbp = usbp->in_params[ep - 1U];
 | 
			
		||||
void usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint) {
 | 
			
		||||
    bqSuspendI(&endpoint->ibqueue);
 | 
			
		||||
    ibqResetI(&endpoint->ibqueue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    if (qmkusbp == NULL) {
 | 
			
		||||
void usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint) {
 | 
			
		||||
    bqResumeX(&endpoint->obqueue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint) {
 | 
			
		||||
    bqResumeX(&endpoint->ibqueue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 *          buffer;
 | 
			
		||||
 | 
			
		||||
    if (endpoint == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    osalSysLockFromISR();
 | 
			
		||||
 | 
			
		||||
    /* Signaling that space is available in the output queue.*/
 | 
			
		||||
    chnAddFlagsI(qmkusbp, CHN_OUTPUT_EMPTY);
 | 
			
		||||
    /* Sending succeded, so we can reset the timed out state. */
 | 
			
		||||
    endpoint->timed_out = false;
 | 
			
		||||
 | 
			
		||||
    /* Freeing the buffer just transmitted, if it was not a zero size packet.*/
 | 
			
		||||
    if (usbp->epc[ep]->in_state->txsize > 0U) {
 | 
			
		||||
        obqReleaseEmptyBufferI(&qmkusbp->obqueue);
 | 
			
		||||
    if (!obqIsEmptyI(&endpoint->obqueue) && usbp->epc[ep]->in_state->txsize > 0U) {
 | 
			
		||||
        /* Store the last send report in the endpoint to be retrieved by a
 | 
			
		||||
         * GET_REPORT request or IDLE report handling. */
 | 
			
		||||
        if (endpoint->report_storage != NULL) {
 | 
			
		||||
            buffer = obqGetFullBufferI(&endpoint->obqueue, &n);
 | 
			
		||||
            endpoint->report_storage->set_report(endpoint->report_storage->reports, buffer, n);
 | 
			
		||||
        }
 | 
			
		||||
        obqReleaseEmptyBufferI(&endpoint->obqueue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Checking if there is a buffer ready for transmission.*/
 | 
			
		||||
    buf = obqGetFullBufferI(&qmkusbp->obqueue, &n);
 | 
			
		||||
    buffer = obqGetFullBufferI(&endpoint->obqueue, &n);
 | 
			
		||||
 | 
			
		||||
    if (buf != NULL) {
 | 
			
		||||
    if (buffer != NULL) {
 | 
			
		||||
        /* The endpoint cannot be busy, we are in the context of the callback,
 | 
			
		||||
           so it is safe to transmit without a check.*/
 | 
			
		||||
        usbStartTransmitI(usbp, ep, buf, n);
 | 
			
		||||
    } else if ((usbp->epc[ep]->in_state->txsize > 0U) && ((usbp->epc[ep]->in_state->txsize & ((size_t)usbp->epc[ep]->in_maxsize - 1U)) == 0U)) {
 | 
			
		||||
        usbStartTransmitI(usbp, ep, buffer, n);
 | 
			
		||||
    } else if ((usbp->epc[ep]->ep_mode == USB_EP_MODE_TYPE_BULK) && (usbp->epc[ep]->in_state->txsize > 0U) && ((usbp->epc[ep]->in_state->txsize & ((size_t)usbp->epc[ep]->in_maxsize - 1U)) == 0U)) {
 | 
			
		||||
        /* Transmit zero sized packet in case the last one has maximum allowed
 | 
			
		||||
           size. Otherwise the recipient may expect more data coming soon and
 | 
			
		||||
           not return buffered data to app. See section 5.8.3 Bulk Transfer
 | 
			
		||||
           Packet Size Constraints of the USB Specification document.*/
 | 
			
		||||
        if (!qmkusbp->config->fixed_size) {
 | 
			
		||||
            usbStartTransmitI(usbp, ep, usbp->setup, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
         * size. Otherwise the recipient may expect more data coming soon and
 | 
			
		||||
         * not return buffered data to app. See section 5.8.3 Bulk Transfer
 | 
			
		||||
         * Packet Size Constraints of the USB Specification document. */
 | 
			
		||||
        usbStartTransmitI(usbp, ep, usbp->setup, 0);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Nothing to transmit.*/
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -419,47 +228,114 @@ void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep) {
 | 
			
		|||
    osalSysUnlockFromISR();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Default data received callback.
 | 
			
		||||
 * @details The application must use this function as callback for the OUT
 | 
			
		||||
 *          data endpoint.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] usbp      pointer to the @p USBDriver object
 | 
			
		||||
 * @param[in] ep        OUT endpoint number
 | 
			
		||||
 */
 | 
			
		||||
void qmkusbDataReceived(USBDriver *usbp, usbep_t ep) {
 | 
			
		||||
    QMKUSBDriver *qmkusbp = usbp->out_params[ep - 1U];
 | 
			
		||||
    if (qmkusbp == NULL) {
 | 
			
		||||
void usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep) {
 | 
			
		||||
    usb_endpoint_out_t *endpoint = usbp->out_params[ep - 1U];
 | 
			
		||||
    if (endpoint == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    osalSysLockFromISR();
 | 
			
		||||
 | 
			
		||||
    /* Signaling that data is available in the input queue.*/
 | 
			
		||||
    chnAddFlagsI(qmkusbp, CHN_INPUT_AVAILABLE);
 | 
			
		||||
    size_t size = usbGetReceiveTransactionSizeX(usbp, ep);
 | 
			
		||||
    if (size > 0) {
 | 
			
		||||
        /* Posting the filled buffer in the queue.*/
 | 
			
		||||
        ibqPostFullBufferI(&endpoint->ibqueue, usbGetReceiveTransactionSizeX(endpoint->config.usbp, endpoint->config.ep));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Posting the filled buffer in the queue.*/
 | 
			
		||||
    ibqPostFullBufferI(&qmkusbp->ibqueue, usbGetReceiveTransactionSizeX(qmkusbp->config->usbp, qmkusbp->config->bulk_out));
 | 
			
		||||
 | 
			
		||||
    /* 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;
 | 
			
		||||
    size_t buffer_size;
 | 
			
		||||
 | 
			
		||||
    /* Input buffer
 | 
			
		||||
     * @note needs to be initialized with a memory buffer of the right size
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Buffer backing storage
 | 
			
		||||
     */
 | 
			
		||||
    uint8_t *ib;
 | 
			
		||||
    /* Output buffer
 | 
			
		||||
     * @note needs to be initialized with a memory buffer of the right size
 | 
			
		||||
     */
 | 
			
		||||
    uint8_t *ob;
 | 
			
		||||
} QMKUSBConfig;
 | 
			
		||||
    uint8_t *buffer;
 | 
			
		||||
} usb_endpoint_config_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   @p SerialDriver specific data.
 | 
			
		||||
 */
 | 
			
		||||
#define _qmk_usb_driver_data                           \
 | 
			
		||||
    _base_asynchronous_channel_data /* Driver state.*/ \
 | 
			
		||||
        qmkusbstate_t state;                           \
 | 
			
		||||
    /* Input buffers queue.*/                          \
 | 
			
		||||
    input_buffers_queue_t ibqueue;                     \
 | 
			
		||||
    /* Output queue.*/                                 \
 | 
			
		||||
    output_buffers_queue_t obqueue;                    \
 | 
			
		||||
    /* End of the mandatory fields.*/                  \
 | 
			
		||||
    /* Current configuration data.*/                   \
 | 
			
		||||
    const QMKUSBConfig *config;
 | 
			
		||||
typedef struct {
 | 
			
		||||
    output_buffers_queue_t obqueue;
 | 
			
		||||
    USBEndpointConfig      ep_config;
 | 
			
		||||
    USBInEndpointState     ep_in_state;
 | 
			
		||||
#if defined(USB_ENDPOINTS_ARE_REORDERABLE)
 | 
			
		||||
    USBOutEndpointState ep_out_state;
 | 
			
		||||
    bool                is_shared;
 | 
			
		||||
#endif
 | 
			
		||||
    usb_endpoint_config_t config;
 | 
			
		||||
    usbreqhandler_t       usb_requests_cb;
 | 
			
		||||
    bool                  timed_out;
 | 
			
		||||
    usb_report_storage_t *report_storage;
 | 
			
		||||
} usb_endpoint_in_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   @p SerialUSBDriver specific methods.
 | 
			
		||||
 */
 | 
			
		||||
#define _qmk_usb_driver_methods _base_asynchronous_channel_methods
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @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;
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -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