diff options
Diffstat (limited to 'tmk_core/protocol/chibios/usb_driver.h')
-rw-r--r-- | tmk_core/protocol/chibios/usb_driver.h | 312 |
1 files changed, 172 insertions, 140 deletions
diff --git a/tmk_core/protocol/chibios/usb_driver.h b/tmk_core/protocol/chibios/usb_driver.h index f94387debd..26787e1eba 100644 --- a/tmk_core/protocol/chibios/usb_driver.h +++ b/tmk_core/protocol/chibios/usb_driver.h @@ -1,177 +1,209 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file usb_driver.h - * @brief Usb driver suitable for both packet and serial formats - * - * @addtogroup SERIAL_USB - * @{ - */ +// Copyright 2023 Stefan Kerkmann (@KarlK90) +// Copyright 2020 Ryan (@fauxpark) +// Copyright 2016 Fredizzimo +// Copyright 2016 Giovanni Di Sirio +// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0 #pragma once -#include <hal_usb_cdc.h> - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ +#include <hal_buffers.h> +#include "usb_descriptor.h" +#include "chibios_config.h" +#include "usb_report_handling.h" +#include "string.h" +#include "timer.h" #if HAL_USE_USB == FALSE # error "The USB Driver requires HAL_USE_USB" #endif -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ +/* USB Low Level driver specific endpoint fields */ +#if !defined(usb_lld_endpoint_fields) +# define usb_lld_endpoint_fields \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ +#endif -/** - * @brief Driver state machine possible states. - */ -typedef enum { - QMKUSB_UNINIT = 0, /**< Not initialized. */ - QMKUSB_STOP = 1, /**< Stopped. */ - QMKUSB_READY = 2 /**< Ready. */ -} qmkusbstate_t; - -/** - * @brief Structure representing a serial over USB driver. +/* + * Implementation notes: + * + * USBEndpointConfig - Configured using explicit order instead of struct member name. + * This is due to ChibiOS hal LLD differences, which is dependent on hardware, + * "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`. + * Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file + * makes the assumption this is safe to avoid littering with preprocessor directives. */ -typedef struct QMKUSBDriver QMKUSBDriver; +#define QMK_USB_ENDPOINT_IN(mode, ep_size, ep_num, _buffer_capacity, _usb_requests_cb, _report_storage) \ + { \ + .usb_requests_cb = _usb_requests_cb, .report_storage = _report_storage, \ + .ep_config = \ + { \ + mode, /* EP Mode */ \ + NULL, /* SETUP packet notification callback */ \ + usb_endpoint_in_tx_complete_cb, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + ep_size, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + usb_lld_endpoint_fields /* USB driver specific endpoint fields */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .ep = ep_num, \ + .buffer_capacity = _buffer_capacity, \ + .buffer_size = ep_size, \ + .buffer = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0}, \ + } \ + } + +#if !defined(USB_ENDPOINTS_ARE_REORDERABLE) + +# define QMK_USB_ENDPOINT_OUT(mode, ep_size, ep_num, _buffer_capacity) \ + { \ + .ep_config = \ + { \ + mode, /* EP Mode */ \ + NULL, /* SETUP packet notification callback */ \ + NULL, /* IN notification callback */ \ + usb_endpoint_out_rx_complete_cb, /* OUT notification callback */ \ + 0, /* IN maximum packet size */ \ + ep_size, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + usb_lld_endpoint_fields /* USB driver specific endpoint fields */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .ep = ep_num, \ + .buffer_capacity = _buffer_capacity, \ + .buffer_size = ep_size, \ + .buffer = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0} \ + } \ + } + +#else + +# define QMK_USB_ENDPOINT_IN_SHARED(mode, ep_size, ep_num, _buffer_capacity, _usb_requests_cb, _report_storage) \ + { \ + .usb_requests_cb = _usb_requests_cb, .is_shared = true, .report_storage = _report_storage, \ + .ep_config = \ + { \ + mode, /* EP Mode */ \ + NULL, /* SETUP packet notification callback */ \ + usb_endpoint_in_tx_complete_cb, /* IN notification callback */ \ + usb_endpoint_out_rx_complete_cb, /* OUT notification callback */ \ + ep_size, /* IN maximum packet size */ \ + ep_size, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + usb_lld_endpoint_fields /* USB driver specific endpoint fields */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .ep = ep_num, \ + .buffer_capacity = _buffer_capacity, \ + .buffer_size = ep_size, \ + .buffer = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0}, \ + } \ + } + +/* The current assumption is that there are no standalone OUT endpoints, so the + * OUT endpoint is always initialized by the IN endpoint. */ +# define QMK_USB_ENDPOINT_OUT(mode, ep_size, ep_num, _buffer_capacity) \ + { \ + .ep_config = \ + { \ + 0 /* Already defined in the IN endpoint */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .ep = ep_num, \ + .buffer_capacity = _buffer_capacity, \ + .buffer_size = ep_size, \ + .buffer = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0} \ + } \ + } + +#endif -/** - * @brief Serial over USB Driver configuration structure. - * @details An instance of this structure must be passed to @p sduStart() - * in order to configure and start the driver operations. - */ typedef struct { /** * @brief USB driver to use. */ USBDriver *usbp; + /** - * @brief Bulk IN endpoint used for outgoing data transfer. - */ - usbep_t bulk_in; - /** - * @brief Bulk OUT endpoint used for incoming data transfer. - */ - usbep_t bulk_out; - /** - * @brief Interrupt IN endpoint used for notifications. - * @note If set to zero then the INT endpoint is assumed to be not - * present, USB descriptors must be changed accordingly. + * @brief Endpoint used for data transfer */ - usbep_t int_in; + usbep_t ep; /** - * @brief The number of buffers in the queues + * @brief The number of buffers in the queue */ - size_t in_buffers; - size_t out_buffers; + size_t buffer_capacity; /** - * @brief The size of each buffer in the queue, typically the same as the endpoint size + * @brief The size of each buffer in the queue, same as the endpoint size */ - size_t in_size; - size_t out_size; + size_t buffer_size; /** - * @brief Always send full buffers in_size (the rest is filled with zeroes) + * @brief Buffer backing storage */ - bool fixed_size; + uint8_t *buffer; +} usb_endpoint_config_t; - /* Input buffer - * @note needs to be initialized with a memory buffer of the right size - */ - uint8_t *ib; - /* Output buffer - * @note needs to be initialized with a memory buffer of the right size - */ - uint8_t *ob; -} QMKUSBConfig; +typedef struct { + output_buffers_queue_t obqueue; + USBEndpointConfig ep_config; + USBInEndpointState ep_in_state; +#if defined(USB_ENDPOINTS_ARE_REORDERABLE) + USBOutEndpointState ep_out_state; + bool is_shared; +#endif + usb_endpoint_config_t config; + usbreqhandler_t usb_requests_cb; + bool timed_out; + usb_report_storage_t *report_storage; +} usb_endpoint_in_t; -/** - * @brief @p SerialDriver specific data. - */ -#define _qmk_usb_driver_data \ - _base_asynchronous_channel_data /* Driver state.*/ \ - qmkusbstate_t state; \ - /* Input buffers queue.*/ \ - input_buffers_queue_t ibqueue; \ - /* Output queue.*/ \ - output_buffers_queue_t obqueue; \ - /* End of the mandatory fields.*/ \ - /* Current configuration data.*/ \ - const QMKUSBConfig *config; - -/** - * @brief @p SerialUSBDriver specific methods. - */ -#define _qmk_usb_driver_methods _base_asynchronous_channel_methods +typedef struct { + input_buffers_queue_t ibqueue; + USBEndpointConfig ep_config; + USBOutEndpointState ep_out_state; + usb_endpoint_config_t config; + bool timed_out; +} usb_endpoint_out_t; -/** - * @extends BaseAsynchronousChannelVMT - * - * @brief @p SerialDriver virtual methods table. - */ -struct QMKUSBDriverVMT { - _qmk_usb_driver_methods -}; +#ifdef __cplusplus +extern "C" { +#endif -/** - * @extends BaseAsynchronousChannel - * - * @brief Full duplex serial driver class. - * @details This class extends @p BaseAsynchronousChannel by adding physical - * I/O queues. - */ -struct QMKUSBDriver { - /** @brief Virtual Methods Table.*/ - const struct QMKUSBDriverVMT *vmt; - _qmk_usb_driver_data -}; +void usb_endpoint_in_init(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_start(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_stop(usb_endpoint_in_t *endpoint); -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ +bool usb_endpoint_in_send(usb_endpoint_in_t *endpoint, const uint8_t *data, size_t size, sysinterval_t timeout, bool buffered); +void usb_endpoint_in_flush(usb_endpoint_in_t *endpoint, bool padded); +bool usb_endpoint_in_is_inactive(usb_endpoint_in_t *endpoint); -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ +void usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_configure_cb(usb_endpoint_in_t *endpoint); +void usb_endpoint_in_tx_complete_cb(USBDriver *usbp, usbep_t ep); + +void usb_endpoint_out_init(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_start(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_stop(usb_endpoint_out_t *endpoint); + +bool usb_endpoint_out_receive(usb_endpoint_out_t *endpoint, uint8_t *data, size_t size, sysinterval_t timeout); + +void usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_configure_cb(usb_endpoint_out_t *endpoint); +void usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep); -#ifdef __cplusplus -extern "C" { -#endif -void qmkusbInit(void); -void qmkusbObjectInit(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config); -void qmkusbStart(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config); -void qmkusbStop(QMKUSBDriver *qmkusbp); -void qmkusbSuspendHookI(QMKUSBDriver *qmkusbp); -void qmkusbWakeupHookI(QMKUSBDriver *qmkusbp); -void qmkusbConfigureHookI(QMKUSBDriver *qmkusbp); -bool qmkusbRequestsHook(USBDriver *usbp); -void qmkusbSOFHookI(QMKUSBDriver *qmkusbp); -void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep); -void qmkusbDataReceived(USBDriver *usbp, usbep_t ep); -void qmkusbInterruptTransmitted(USBDriver *usbp, usbep_t ep); #ifdef __cplusplus } #endif |