aboutsummaryrefslogtreecommitdiff
path: root/tmk_core/protocol/chibios/usb_driver.h
diff options
context:
space:
mode:
Diffstat (limited to 'tmk_core/protocol/chibios/usb_driver.h')
-rw-r--r--tmk_core/protocol/chibios/usb_driver.h312
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