// 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 #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 /* 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 /* * 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. */ #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 typedef struct { /** * @brief USB driver to use. */ USBDriver *usbp; /** * @brief Endpoint used for data transfer */ usbep_t ep; /** * @brief The number of buffers in the queue */ size_t buffer_capacity; /** * @brief The size of each buffer in the queue, same as the endpoint size */ size_t buffer_size; /** * @brief Buffer backing storage */ uint8_t *buffer; } usb_endpoint_config_t; 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; 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 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 /** @} */