From 19537f4c37250d9ed8fda1cfcb72601eaea8cfcc Mon Sep 17 00:00:00 2001 From: Ren Chen Date: Fri, 24 Nov 2023 14:41:57 -0600 Subject: [PATCH 01/39] [nrf fromtree] drivers: udc: add IT82xx2 USB device controller driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add UDC driver for IT82xx2 SoC. This commit passes tests with 1. samples/subsys/usb/cdc_acm/ 2. samples/subsys/usb/console/ 3. The extend endpoint test with CDC ACM tool 4. USB suspend/resume detection Signed-off-by: Ren Chen (cherry picked from commit 766a5ea5742d6645163a225ed8d96151119832dc) Signed-off-by: Tomasz Moń --- drivers/usb/udc/CMakeLists.txt | 1 + drivers/usb/udc/Kconfig | 1 + drivers/usb/udc/Kconfig.it82xx2 | 27 + drivers/usb/udc/udc_it82xx2.c | 1477 +++++++++++++++++++++++++++++++ 4 files changed, 1506 insertions(+) create mode 100644 drivers/usb/udc/Kconfig.it82xx2 create mode 100644 drivers/usb/udc/udc_it82xx2.c diff --git a/drivers/usb/udc/CMakeLists.txt b/drivers/usb/udc/CMakeLists.txt index ebca5f64277..2dd77536b26 100644 --- a/drivers/usb/udc/CMakeLists.txt +++ b/drivers/usb/udc/CMakeLists.txt @@ -12,3 +12,4 @@ zephyr_library_sources_ifdef(CONFIG_UDC_KINETIS udc_kinetis.c) zephyr_library_sources_ifdef(CONFIG_UDC_SKELETON udc_skeleton.c) zephyr_library_sources_ifdef(CONFIG_UDC_VIRTUAL udc_virtual.c) zephyr_library_sources_ifdef(CONFIG_UDC_STM32 udc_stm32.c) +zephyr_library_sources_ifdef(CONFIG_UDC_IT82XX2 udc_it82xx2.c) diff --git a/drivers/usb/udc/Kconfig b/drivers/usb/udc/Kconfig index 5b19a3385ad..b407e1212b5 100644 --- a/drivers/usb/udc/Kconfig +++ b/drivers/usb/udc/Kconfig @@ -53,5 +53,6 @@ source "drivers/usb/udc/Kconfig.kinetis" source "drivers/usb/udc/Kconfig.skeleton" source "drivers/usb/udc/Kconfig.virtual" source "drivers/usb/udc/Kconfig.stm32" +source "drivers/usb/udc/Kconfig.it82xx2" endif # UDC_DRIVER diff --git a/drivers/usb/udc/Kconfig.it82xx2 b/drivers/usb/udc/Kconfig.it82xx2 new file mode 100644 index 00000000000..649b2531d10 --- /dev/null +++ b/drivers/usb/udc/Kconfig.it82xx2 @@ -0,0 +1,27 @@ +# Copyright (c) 2023 ITE Corporation. +# SPDX-License-Identifier: Apache-2.0 + +config UDC_IT82XX2 + bool "IT82XX2 USB device controller driver" + default y + depends on DT_HAS_ITE_IT82XX2_USB_ENABLED + help + IT82xx2 USB device controller driver. + +if UDC_IT82XX2 + +config UDC_IT82xx2_EVENT_COUNT + int "UDC IT82xx2 event count" + range 4 64 + default 8 + help + IT82xx2 event count. + +config UDC_IT82xx2_STACK_SIZE + int "IT82xx2 UDC driver internal thread stack size" + default 1024 + help + Size of the stack used in the driver for IT82xx2 USBD ISR event + handling. + +endif # UDC_IT82XX2 diff --git a/drivers/usb/udc/udc_it82xx2.c b/drivers/usb/udc/udc_it82xx2.c new file mode 100644 index 00000000000..70209f11e17 --- /dev/null +++ b/drivers/usb/udc/udc_it82xx2.c @@ -0,0 +1,1477 @@ +/* + * Copyright (c) 2023 ITE Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "udc_common.h" + +#include +#include +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(udc_it82xx2, CONFIG_UDC_DRIVER_LOG_LEVEL); + +#define DT_DRV_COMPAT ite_it82xx2_usb + +#define IT8XXX2_IS_EXTEND_ENDPOINT(n) (USB_EP_GET_IDX(n) >= 4) + +/* Shared FIFO number including FIFO_1/2/3 */ +#define SHARED_FIFO_NUM 3 + +/* The related definitions of the register dc_line_status: 0x51 */ +#define RX_LINE_STATE_MASK (RX_LINE_FULL_SPD | RX_LINE_LOW_SPD) +#define RX_LINE_LOW_SPD 0x02 +#define RX_LINE_FULL_SPD 0x01 +#define RX_LINE_RESET 0x00 + +#define DC_ADDR_NULL 0x00 +#define DC_ADDR_MASK 0x7F + +/* EPN Extend Control 2 Register Mask Definition */ +#define COMPLETED_TRANS 0xF0 + +/* The related definitions of the register EP STATUS: + * 0x41/0x45/0x49/0x4D + */ +#define EP_STATUS_ERROR 0x0F + +/* ENDPOINT[3..0]_CONTROL_REG */ +#define ENDPOINT_EN BIT(0) +#define ENDPOINT_RDY BIT(1) + +/* The bit definitions of the register EP RX/TX FIFO Control: + * EP_RX_FIFO_CONTROL: 0X64/0x84/0xA4/0xC4 + * EP_TX_FIFO_CONTROL: 0X74/0x94/0xB4/0xD4 + */ +#define FIFO_FORCE_EMPTY BIT(0) + +/* The bit definitions of the register Host/Device Control: 0XE0 */ +#define RESET_CORE BIT(1) + +/* ENDPOINT[3..0]_STATUS_REG */ +#define DC_STALL_SENT BIT(5) + +/* DC_INTERRUPT_STATUS_REG */ +#define DC_TRANS_DONE BIT(0) +#define DC_RESET_EVENT BIT(2) +#define DC_SOF_RECEIVED BIT(3) +#define DC_NAK_SENT_INT BIT(4) + +/* DC_CONTROL_REG */ +#define DC_GLOBAL_ENABLE BIT(0) +#define DC_TX_LINE_STATE_DM BIT(1) +#define DC_DIRECT_CONTROL BIT(3) +#define DC_FULL_SPEED_LINE_POLARITY BIT(4) +#define DC_FULL_SPEED_LINE_RATE BIT(5) +#define DC_CONNECT_TO_HOST BIT(6) /* internal pull-up */ + +/* Bit [1:0] represents the TRANSACTION_TYPE as follows: */ +enum it82xx2_transaction_types { + DC_SETUP_TRANS = 0, + DC_IN_TRANS, + DC_OUTDATA_TRANS, + DC_ALL_TRANS +}; + +enum it82xx2_event_type { + IT82xx2_EVT_XFER, + IT82xx2_EVT_SETUP_TOKEN, + IT82xx2_EVT_OUT_TOKEN, + IT82xx2_EVT_IN_TOKEN, +}; + +struct it82xx2_ep_event { + sys_snode_t node; + const struct device *dev; + uint8_t ep; + enum it82xx2_event_type event; +}; + +K_MSGQ_DEFINE(evt_msgq, sizeof(struct it82xx2_ep_event), + CONFIG_UDC_IT82xx2_EVENT_COUNT, sizeof(uint32_t)); + +struct usb_it8xxx2_wuc { + /* WUC control device structure */ + const struct device *dev; + /* WUC pin mask */ + uint8_t mask; +}; + +struct it82xx2_data { + const struct device *dev; + + struct k_fifo fifo; + struct k_work_delayable suspended_work; + + struct k_thread thread_data; + struct k_sem suspended_sem; + + /* FIFO_1/2/3 ready status */ + bool fifo_ready[SHARED_FIFO_NUM]; + + /* FIFO_1/2/3 semaphore */ + struct k_sem fifo_sem[SHARED_FIFO_NUM]; + + /* Record if the previous transaction of endpoint0 is STALL */ + bool stall_is_sent; +}; + +struct usb_it82xx2_config { + struct usb_it82xx2_regs *const base; + const struct pinctrl_dev_config *pcfg; + const struct usb_it8xxx2_wuc wuc; + uint8_t usb_irq; + uint8_t wu_irq; + struct udc_ep_config *ep_cfg_in; + struct udc_ep_config *ep_cfg_out; + void (*make_thread)(const struct device *dev); +}; + +enum it82xx2_ep_ctrl { + EP_IN_DIRECTION_SET, + EP_IOS_ENABLE, + EP_ENABLE, + EP_DATA_SEQ_1, + EP_DATA_SEQ_TOGGLE, + EP_READY_ENABLE, +}; + +static inline void ep_set_halt(const struct device *dev, const uint8_t ep_idx, const bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + union epn0n1_extend_ctrl_reg *ext_ctrl; + uint8_t idx = (ep_idx - 4) / 2; + + ext_ctrl = usb_regs->fifo_regs[EP_EXT_REGS_9X].ext_4_15.epn0n1_ext_ctrl; + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + if (ep_idx % 2) { + ext_ctrl[idx].fields.epn1_send_stall_bit = enable; + } else { + ext_ctrl[idx].fields.epn0_send_stall_bit = enable; + } + } else { + ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit = enable; + } +} + +static volatile void *it82xx2_get_ext_ctrl(const struct device *dev, const uint8_t ep_idx, + const enum it82xx2_ep_ctrl ctrl) +{ + uint8_t idx; + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + union epn0n1_extend_ctrl_reg *epn0n1_ext_ctrl = + usb_regs->fifo_regs[EP_EXT_REGS_9X].ext_4_15.epn0n1_ext_ctrl; + struct epn_ext_ctrl_regs *ext_ctrl = + usb_regs->fifo_regs[EP_EXT_REGS_DX].ext_0_3.epn_ext_ctrl; + + if (ctrl == EP_IN_DIRECTION_SET || ctrl == EP_ENABLE) { + idx = ((ep_idx - 4) % 3) + 1; + return &ext_ctrl[idx].epn_ext_ctrl1; + } + + idx = (ep_idx - 4) / 2; + return &epn0n1_ext_ctrl[idx]; +} + +static int it82xx2_usb_extend_ep_ctrl(const struct device *dev, const uint8_t ep, + const enum it82xx2_ep_ctrl ctrl, const bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct epn_ext_ctrl_regs *ext_ctrl = + usb_regs->fifo_regs[EP_EXT_REGS_DX].ext_0_3.epn_ext_ctrl; + volatile union epn_extend_ctrl1_reg *epn_ext_ctrl1 = NULL; + volatile union epn0n1_extend_ctrl_reg *epn0n1_ext_ctrl = NULL; + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (ctrl == EP_IN_DIRECTION_SET || ctrl == EP_ENABLE) { + epn_ext_ctrl1 = it82xx2_get_ext_ctrl(dev, ep_idx, ctrl); + } else { + epn0n1_ext_ctrl = it82xx2_get_ext_ctrl(dev, ep_idx, ctrl); + } + + switch (ctrl) { + case EP_IOS_ENABLE: + if (ep_idx % 2) { + epn0n1_ext_ctrl->fields.epn1_iso_enable_bit = enable; + } else { + epn0n1_ext_ctrl->fields.epn0_iso_enable_bit = enable; + } + break; + case EP_DATA_SEQ_1: + if (ep_idx % 2) { + epn0n1_ext_ctrl->fields.epn1_outdata_sequence_bit = enable; + } else { + epn0n1_ext_ctrl->fields.epn0_outdata_sequence_bit = enable; + } + break; + case EP_DATA_SEQ_TOGGLE: + if (!enable) { + break; + } + if (ep_idx % 2) { + if (epn0n1_ext_ctrl->fields.epn1_outdata_sequence_bit) { + epn0n1_ext_ctrl->fields.epn1_outdata_sequence_bit = 0; + } else { + epn0n1_ext_ctrl->fields.epn1_outdata_sequence_bit = 1; + } + } else { + if (epn0n1_ext_ctrl->fields.epn0_outdata_sequence_bit) { + epn0n1_ext_ctrl->fields.epn0_outdata_sequence_bit = 0; + } else { + epn0n1_ext_ctrl->fields.epn0_outdata_sequence_bit = 1; + } + } + break; + case EP_IN_DIRECTION_SET: + if (((ep_idx - 4) / 3 == 0)) { + epn_ext_ctrl1->fields.epn0_direction_bit = enable; + } else if (((ep_idx - 4) / 3 == 1)) { + epn_ext_ctrl1->fields.epn3_direction_bit = enable; + } else if (((ep_idx - 4) / 3 == 2)) { + epn_ext_ctrl1->fields.epn6_direction_bit = enable; + } else if (((ep_idx - 4) / 3 == 3)) { + epn_ext_ctrl1->fields.epn9_direction_bit = enable; + } else { + LOG_ERR("Invalid endpoint 0x%x for control type 0x%x", ep, ctrl); + return -EINVAL; + } + break; + case EP_ENABLE: + if (((ep_idx - 4) / 3 == 0)) { + epn_ext_ctrl1->fields.epn0_enable_bit = enable; + } else if (((ep_idx - 4) / 3 == 1)) { + epn_ext_ctrl1->fields.epn3_enable_bit = enable; + } else if (((ep_idx - 4) / 3 == 2)) { + epn_ext_ctrl1->fields.epn6_enable_bit = enable; + } else if (((ep_idx - 4) / 3 == 3)) { + epn_ext_ctrl1->fields.epn9_enable_bit = enable; + } else { + LOG_ERR("Invalid endpoint 0x%x for control type 0x%x", ep, ctrl); + return -EINVAL; + } + break; + case EP_READY_ENABLE: + int idx = ((ep_idx - 4) % 3) + 1; + + (enable) ? (ext_ctrl[idx].epn_ext_ctrl2 |= BIT((ep_idx - 4) / 3)) + : (ext_ctrl[idx].epn_ext_ctrl2 &= ~BIT((ep_idx - 4) / 3)); + break; + default: + LOG_ERR("Unknown control type 0x%x", ctrl); + return -EINVAL; + } + + return 0; +} + +static int it82xx2_usb_ep_ctrl(const struct device *dev, uint8_t ep, enum it82xx2_ep_ctrl ctrl, + bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + return -EINVAL; + } + + switch (ctrl) { + case EP_IN_DIRECTION_SET: + ep_regs[ep_idx].ep_ctrl.fields.direction_bit = enable; + break; + case EP_IOS_ENABLE: + ep_regs[ep_idx].ep_ctrl.fields.iso_enable_bit = enable; + break; + case EP_ENABLE: + ep_regs[ep_idx].ep_ctrl.fields.enable_bit = enable; + break; + case EP_READY_ENABLE: + ep_regs[ep_idx].ep_ctrl.fields.ready_bit = enable; + break; + case EP_DATA_SEQ_1: + ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = enable; + break; + case EP_DATA_SEQ_TOGGLE: + if (!enable) { + break; + } + if (ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit) { + ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = 0; + } else { + ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = 1; + } + break; + default: + LOG_ERR("Unknown control type 0x%x", ctrl); + return -EINVAL; + } + return 0; +} + +static int it82xx2_usb_set_ep_ctrl(const struct device *dev, uint8_t ep, enum it82xx2_ep_ctrl ctrl, + bool enable) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + int ret = 0; + + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + ret = it82xx2_usb_extend_ep_ctrl(dev, ep, ctrl, enable); + } else { + ret = it82xx2_usb_ep_ctrl(dev, ep, ctrl, enable); + } + return ret; +} + +/* Standby(deep doze) mode enable/disable */ +static void it82xx2_enable_standby_state(bool enable) +{ + if (enable) { + pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); + } else { + pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); + } +} + +/* Wake-up interrupt (USB D+) Enable/Disable */ +static void it82xx2_enable_wu_irq(const struct device *dev, bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + + /* Clear pending interrupt */ + it8xxx2_wuc_clear_status(config->wuc.dev, config->wuc.mask); + + if (enable) { + irq_enable(config->wu_irq); + } else { + irq_disable(config->wu_irq); + } +} + +static void it82xx2_wu_isr(const void *arg) +{ + const struct device *dev = arg; + + it82xx2_enable_wu_irq(dev, false); + it82xx2_enable_standby_state(false); + LOG_DBG("USB D+ (WU) Triggered"); +} + +static void it8xxx2_usb_dc_wuc_init(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + + /* Initializing the WUI */ + it8xxx2_wuc_set_polarity(config->wuc.dev, config->wuc.mask, WUC_TYPE_EDGE_FALLING); + it8xxx2_wuc_clear_status(config->wuc.dev, config->wuc.mask); + + /* Enabling the WUI */ + it8xxx2_wuc_enable(config->wuc.dev, config->wuc.mask); + + /* Connect WU (USB D+) interrupt but make it disabled initially */ + irq_connect_dynamic(config->wu_irq, 0, it82xx2_wu_isr, dev, 0); +} + +/* The ep_fifo_res[ep_idx % SHARED_FIFO_NUM] where the SHARED_FIFO_NUM is 3 represents the + * EP mapping because when (ep_idx % SHARED_FIFO_NUM) is 3, it actually means the EP0. + */ +static const uint8_t ep_fifo_res[SHARED_FIFO_NUM] = {3, 1, 2}; + +static int it82xx2_usb_fifo_ctrl(const struct device *dev, uint8_t ep) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + volatile uint8_t *ep_fifo_ctrl = usb_regs->fifo_regs[EP_EXT_REGS_BX].fifo_ctrl.ep_fifo_ctrl; + uint8_t fifon_ctrl = (ep_fifo_res[ep_idx % SHARED_FIFO_NUM] - 1) * 2; + int ret = 0; + + if (ep_idx == 0) { + LOG_ERR("Invalid endpoint 0x%x", ep); + return -EINVAL; + } + + if (USB_EP_DIR_IS_IN(ep)) { + if (ep_idx < 8) { + ep_fifo_ctrl[fifon_ctrl] = BIT(ep_idx); + ep_fifo_ctrl[fifon_ctrl + 1] = 0x0; + } else { + ep_fifo_ctrl[fifon_ctrl] = 0x0; + ep_fifo_ctrl[fifon_ctrl + 1] = BIT(ep_idx - 8); + } + } else if (USB_EP_DIR_IS_OUT(ep)) { + if (ep_idx < 8) { + ep_fifo_ctrl[fifon_ctrl] |= BIT(ep_idx); + } else { + ep_fifo_ctrl[fifon_ctrl + 1] |= BIT(ep_idx - 8); + } + } else { + LOG_ERR("Failed to set fifo control register for ep 0x%x", ep); + ret = -EINVAL; + } + + return ret; +} + +static void it82xx2_event_submit(const struct device *dev, const uint8_t ep, + const enum it82xx2_event_type event) +{ + struct it82xx2_ep_event evt; + + evt.dev = dev; + evt.ep = ep; + evt.event = event; + k_msgq_put(&evt_msgq, &evt, K_NO_WAIT); +} + +static int it82xx2_ep_enqueue(const struct device *dev, struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + udc_buf_put(cfg, buf); + + it82xx2_event_submit(dev, cfg->addr, IT82xx2_EVT_XFER); + return 0; +} + +static int it82xx2_ep_dequeue(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + struct net_buf *buf; + unsigned int lock_key; + uint8_t fifo_idx; + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + lock_key = irq_lock(); + if (USB_EP_DIR_IS_IN(cfg->addr)) { + ff_regs[fifo_idx].ep_tx_fifo_ctrl = FIFO_FORCE_EMPTY; + } else { + ff_regs[fifo_idx].ep_rx_fifo_ctrl = FIFO_FORCE_EMPTY; + } + irq_unlock(lock_key); + + buf = udc_buf_get_all(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, -ECONNABORTED); + } + + udc_ep_set_busy(dev, cfg->addr, false); + + return 0; +} + +static inline void ctrl_ep_stall_workaround(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct gctrl_it8xxx2_regs *const gctrl_regs = GCTRL_IT8XXX2_REGS_BASE; + struct it82xx2_data *priv = udc_get_private(dev); + unsigned int lock_key; + uint32_t idx = 0; + + priv->stall_is_sent = true; + lock_key = irq_lock(); + ep_set_halt(dev, 0, true); + it82xx2_usb_set_ep_ctrl(dev, 0, EP_READY_ENABLE, true); + + /* It82xx2 does not support clearing the STALL bit by hardware; instead, the STALL bit need + * to be cleared by firmware. The SETUP token will be STALLed, which isn't compliant to + * USB specification, if firmware clears the STALL bit too late. Due to this hardware + * limitations, device controller polls to check if the stall bit has been transmitted for + * 3ms and then disables it after responsing STALLed. + */ + while (idx < 198 && !(ep_regs[0].ep_status & DC_STALL_SENT)) { + /* wait 15.15us */ + gctrl_regs->GCTRL_WNCKR = 0; + idx++; + } + + if (idx < 198) { + ep_set_halt(dev, 0, false); + } + irq_unlock(lock_key); +} + +static int it82xx2_ep_set_halt(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + + if (ep_idx == 0) { + ctrl_ep_stall_workaround(dev); + } else { + ep_set_halt(dev, ep_idx, true); + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_READY_ENABLE, true); + } + + LOG_DBG("Endpoint 0x%x is halted", cfg->addr); + + return 0; +} + +static int it82xx2_ep_clear_halt(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + + ep_set_halt(dev, ep_idx, false); + + LOG_DBG("Endpoint 0x%x clear halted", cfg->addr); + + return 0; +} + +static int it82xx2_ep_enable(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + + /* Configure endpoint */ + if (ep_idx != 0) { + if (USB_EP_DIR_IS_IN(cfg->addr)) { + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_IN_DIRECTION_SET, true); + } else { + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_IN_DIRECTION_SET, false); + it82xx2_usb_fifo_ctrl(dev, cfg->addr); + } + + switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_BULK: + __fallthrough; + case USB_EP_TYPE_INTERRUPT: + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_IOS_ENABLE, false); + break; + case USB_EP_TYPE_ISO: + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_IOS_ENABLE, true); + break; + case USB_EP_TYPE_CONTROL: + __fallthrough; + default: + return -ENOTSUP; + } + } + + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + uint8_t fifo_idx; + + fifo_idx = ep_fifo_res[ep_idx % SHARED_FIFO_NUM]; + it82xx2_usb_set_ep_ctrl(dev, fifo_idx, EP_ENABLE, true); + } + + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_ENABLE, true); + + LOG_DBG("Endpoint 0x%02x is enabled", cfg->addr); + + return 0; +} + +static int it82xx2_ep_disable(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_ENABLE, false); + + LOG_DBG("Endpoint 0x%02x is disabled", cfg->addr); + + return 0; +} + +static int it82xx2_host_wakeup(const struct device *dev) +{ + struct it82xx2_data *priv = udc_get_private(dev); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + int ret; + + if (udc_is_suspended(dev)) { + usb_regs->dc_control = DC_GLOBAL_ENABLE | DC_FULL_SPEED_LINE_POLARITY | + DC_FULL_SPEED_LINE_RATE | DC_DIRECT_CONTROL | + DC_TX_LINE_STATE_DM | DC_CONNECT_TO_HOST; + + /* The remote wakeup device must hold the resume signal for */ + /* at least 1 ms but for no more than 15 ms */ + k_msleep(2); + + usb_regs->dc_control = DC_GLOBAL_ENABLE | DC_FULL_SPEED_LINE_POLARITY | + DC_FULL_SPEED_LINE_RATE | DC_CONNECT_TO_HOST; + + ret = k_sem_take(&priv->suspended_sem, K_MSEC(500)); + if (ret < 0) { + LOG_ERR("Failed to wake up host"); + } + } + + return 0; +} + +static int it82xx2_set_address(const struct device *dev, const uint8_t addr) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + usb_regs->dc_address = addr & DC_ADDR_MASK; + + LOG_DBG("Set usb address 0x%02x", addr); + + return 0; +} + +static int it82xx2_usb_dc_ip_init(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + /* reset usb controller */ + usb_regs->host_device_control = RESET_CORE; + k_msleep(1); + usb_regs->port0_misc_control &= ~(PULL_DOWN_EN); + usb_regs->port1_misc_control &= ~(PULL_DOWN_EN); + + /* clear reset bit */ + usb_regs->host_device_control = 0; + + usb_regs->dc_interrupt_status = DC_TRANS_DONE | DC_RESET_EVENT | DC_SOF_RECEIVED; + + usb_regs->dc_interrupt_mask = 0x00; + usb_regs->dc_interrupt_mask = DC_TRANS_DONE | DC_RESET_EVENT | DC_SOF_RECEIVED; + + usb_regs->dc_address = DC_ADDR_NULL; + + return 0; +} + +static void it82xx2_enable_sof_int(const struct device *dev, bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + usb_regs->dc_interrupt_status = DC_SOF_RECEIVED; + if (enable) { + usb_regs->dc_interrupt_mask |= DC_SOF_RECEIVED; + } else { + usb_regs->dc_interrupt_mask &= ~DC_SOF_RECEIVED; + } +} + +void it82xx2_dc_reset(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + struct it82xx2_data *priv = udc_get_private(dev); + + for (uint8_t ep_idx = 0; ep_idx < 4; ep_idx++) { + ff_regs[ep_idx].ep_rx_fifo_ctrl = FIFO_FORCE_EMPTY; + ff_regs[ep_idx].ep_tx_fifo_ctrl = FIFO_FORCE_EMPTY; + } + + ep_regs[0].ep_ctrl.value = ENDPOINT_EN; + usb_regs->dc_address = DC_ADDR_NULL; + usb_regs->dc_interrupt_status = DC_NAK_SENT_INT | DC_SOF_RECEIVED; + + priv->fifo_ready[0] = false; + priv->fifo_ready[1] = false; + priv->fifo_ready[2] = false; + k_sem_give(&priv->fifo_sem[0]); + k_sem_give(&priv->fifo_sem[1]); + k_sem_give(&priv->fifo_sem[2]); +} + +static int it82xx2_xfer_in_data(const struct device *dev, uint8_t ep, struct net_buf *buf) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + struct it82xx2_data *priv = udc_get_private(dev); + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep); + uint8_t fifo_idx; + size_t len; + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + if (ep_idx == 0) { + ff_regs[ep_idx].ep_tx_fifo_ctrl = FIFO_FORCE_EMPTY; + } else { + k_sem_take(&priv->fifo_sem[fifo_idx - 1], K_FOREVER); + it82xx2_usb_fifo_ctrl(dev, ep); + } + + len = MIN(buf->len, ep_cfg->mps); + + for (size_t i = 0; i < len; i++) { + ff_regs[fifo_idx].ep_tx_fifo_data = buf->data[i]; + } + + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + it82xx2_usb_extend_ep_ctrl(dev, ep_idx, EP_READY_ENABLE, true); + } + it82xx2_usb_set_ep_ctrl(dev, fifo_idx, EP_READY_ENABLE, true); + + if (ep_idx != 0) { + priv->fifo_ready[fifo_idx - 1] = true; + } + + LOG_DBG("Writed %d packets to endpoint%d tx fifo", buf->len, ep_idx); + + return 0; +} + +static int it82xx2_xfer_out_data(const struct device *dev, uint8_t ep, struct net_buf *buf) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + uint8_t fifo_idx; + size_t len; + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + if (ep_regs[fifo_idx].ep_status & EP_STATUS_ERROR) { + LOG_WRN("endpoint%d error status 0x%02x", ep_idx, ep_regs[fifo_idx].ep_status); + return -EINVAL; + } + + len = (uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_lsb + + (((uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_msb) << 8); + + len = MIN(net_buf_tailroom(buf), len); + uint8_t *data_ptr = net_buf_tail(buf); + + for (size_t idx = 0; idx < len; idx++) { + data_ptr[idx] = ff_regs[fifo_idx].ep_rx_fifo_data; + } + + net_buf_add(buf, len); + + return 0; +} + +static int work_handler_xfer_continue(const struct device *dev, uint8_t ep, struct net_buf *buf) +{ + int ret = 0; + + if (USB_EP_DIR_IS_OUT(ep)) { + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + struct it82xx2_data *priv = udc_get_private(dev); + uint8_t fifo_idx; + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_READY_ENABLE, true); + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + it82xx2_usb_set_ep_ctrl(dev, fifo_idx, EP_READY_ENABLE, true); + } + if (ep_idx != 0) { + priv->fifo_ready[fifo_idx - 1] = true; + } + } else { + ret = it82xx2_xfer_in_data(dev, ep, buf); + } + + return ret; +} + +static int work_handler_xfer_next(const struct device *dev, uint8_t ep) +{ + struct net_buf *buf; + + buf = udc_buf_peek(dev, ep); + if (buf == NULL) { + return -ENODATA; + } + + return work_handler_xfer_continue(dev, ep, buf); +} + +/* + * Allocate buffer and initiate a new control OUT transfer, + * use successive buffer descriptor when next is true. + */ +static int it82xx2_ctrl_feed_dout(const struct device *dev, const size_t length) +{ + struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + struct net_buf *buf; + + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); + if (buf == NULL) { + return -ENOMEM; + } + udc_buf_put(cfg, buf); + + it82xx2_usb_set_ep_ctrl(dev, 0, EP_READY_ENABLE, true); + + return 0; +} + +static bool it82xx2_fake_token(const struct device *dev, uint8_t ep, uint8_t token_type) +{ + struct it82xx2_data *priv = udc_get_private(dev); + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + uint8_t fifo_idx; + bool is_fake = true; + + if (ep_idx == 0) { + switch (token_type) { + case DC_IN_TRANS: + if (priv->stall_is_sent) { + return true; + } + is_fake = !udc_ctrl_stage_is_data_in(dev) && + !udc_ctrl_stage_is_status_in(dev) && + !udc_ctrl_stage_is_no_data(dev); + break; + case DC_OUTDATA_TRANS: + is_fake = !udc_ctrl_stage_is_data_out(dev) && + !udc_ctrl_stage_is_status_out(dev); + break; + default: + LOG_ERR("Invalid token type"); + break; + } + } else { + fifo_idx = ep_fifo_res[ep_idx % SHARED_FIFO_NUM]; + + if (!priv->fifo_ready[fifo_idx - 1]) { + is_fake = true; + } else { + priv->fifo_ready[fifo_idx - 1] = false; + is_fake = false; + } + } + + return is_fake; +} + +static inline int work_handler_in(const struct device *dev, uint8_t ep) +{ + struct it82xx2_data *priv = udc_get_private(dev); + struct udc_ep_config *ep_cfg; + struct net_buf *buf; + uint8_t fifo_idx; + int err = 0; + + if (it82xx2_fake_token(dev, ep, DC_IN_TRANS)) { + return 0; + } + + if (ep != USB_CONTROL_EP_IN) { + fifo_idx = ep_fifo_res[USB_EP_GET_IDX(ep) % SHARED_FIFO_NUM]; + k_sem_give(&priv->fifo_sem[fifo_idx - 1]); + } + + buf = udc_buf_peek(dev, ep); + if (buf == NULL) { + return -ENODATA; + } + ep_cfg = udc_get_ep_cfg(dev, ep); + + net_buf_pull(buf, MIN(buf->len, ep_cfg->mps)); + + it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_TOGGLE, true); + + if (buf->len) { + work_handler_xfer_continue(dev, ep, buf); + return 0; + } + + if (udc_ep_buf_has_zlp(buf)) { + work_handler_xfer_continue(dev, ep, buf); + udc_ep_buf_clear_zlp(buf); + return 0; + } + + buf = udc_buf_get(dev, ep); + if (buf == NULL) { + return -ENODATA; + } + + udc_ep_set_busy(dev, ep, false); + + if (ep == USB_CONTROL_EP_IN) { + if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { + /* Status stage finished, notify upper layer */ + udc_ctrl_submit_status(dev, buf); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (udc_ctrl_stage_is_status_out(dev)) { + /* + * IN transfer finished, release buffer, + * Feed control OUT buffer for status stage. + */ + net_buf_unref(buf); + return it82xx2_ctrl_feed_dout(dev, 0U); + } + return err; + } + + return udc_submit_ep_event(dev, buf, 0); +} + +static inline int work_handler_setup(const struct device *dev, uint8_t ep) +{ + struct it82xx2_data *priv = udc_get_private(dev); + struct net_buf *buf; + int err = 0; + + if (udc_ctrl_stage_is_status_out(dev)) { + /* out -> setup */ + buf = udc_buf_get(dev, USB_CONTROL_EP_OUT); + if (buf) { + udc_ep_set_busy(dev, USB_CONTROL_EP_OUT, false); + net_buf_unref(buf); + } + } + + if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { + /* in -> setup */ + work_handler_in(dev, USB_CONTROL_EP_IN); + } + + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet)); + if (buf == NULL) { + LOG_ERR("Failed to allocate buffer"); + return -ENOMEM; + } + + udc_ep_buf_set_setup(buf); + it82xx2_xfer_out_data(dev, ep, buf); + if (buf->len != sizeof(struct usb_setup_packet)) { + LOG_DBG("buffer length %d read from chip", buf->len); + net_buf_unref(buf); + return 0; + } + + priv->stall_is_sent = false; + LOG_HEXDUMP_DBG(buf->data, buf->len, "setup:"); + + udc_ctrl_update_stage(dev, buf); + + it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_1, true); + + if (udc_ctrl_stage_is_data_out(dev)) { + /* Allocate and feed buffer for data OUT stage */ + LOG_DBG("s:%p|feed for -out-", buf); + err = it82xx2_ctrl_feed_dout(dev, udc_data_stage_length(buf)); + if (err == -ENOMEM) { + err = udc_submit_ep_event(dev, buf, err); + } + } else if (udc_ctrl_stage_is_data_in(dev)) { + udc_ctrl_submit_s_in_status(dev); + } else { + udc_ctrl_submit_s_status(dev); + } + + return err; +} + +static inline int work_handler_out(const struct device *dev, uint8_t ep) +{ + struct net_buf *buf; + int err = 0; + + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + struct udc_ep_config *ep_cfg; + uint8_t fifo_idx; + size_t len; + + if (it82xx2_fake_token(dev, ep, DC_OUTDATA_TRANS)) { + return 0; + } + + buf = udc_buf_get(dev, ep); + if (buf == NULL) { + return -ENODATA; + } + + udc_ep_set_busy(dev, ep, false); + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + len = (uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_lsb + + (((uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_msb) << 8); + + if (ep == USB_CONTROL_EP_OUT) { + if (udc_ctrl_stage_is_status_out(dev) && len != 0) { + LOG_DBG("Handle early setup token"); + /* Notify upper layer */ + udc_ctrl_submit_status(dev, buf); + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + return 0; + } + } + + ep_cfg = udc_get_ep_cfg(dev, ep); + if (len > ep_cfg->mps) { + LOG_ERR("Failed to handle this packet due to the packet size"); + return -ENOBUFS; + } + + it82xx2_xfer_out_data(dev, ep, buf); + + if (ep == USB_CONTROL_EP_OUT) { + if (udc_ctrl_stage_is_status_out(dev)) { + /* Status stage finished, notify upper layer */ + udc_ctrl_submit_status(dev, buf); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (udc_ctrl_stage_is_status_in(dev)) { + it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_1, true); + err = udc_ctrl_submit_s_out_status(dev, buf); + } + } else { + err = udc_submit_ep_event(dev, buf, 0); + } + + return err; +} + +static void xfer_work_handler(const struct device *dev) +{ + while (true) { + struct it82xx2_ep_event evt; + int err = 0; + + k_msgq_get(&evt_msgq, &evt, K_FOREVER); + + switch (evt.event) { + case IT82xx2_EVT_SETUP_TOKEN: + err = work_handler_setup(evt.dev, evt.ep); + break; + case IT82xx2_EVT_IN_TOKEN: + err = work_handler_in(evt.dev, evt.ep); + break; + case IT82xx2_EVT_OUT_TOKEN: + err = work_handler_out(evt.dev, evt.ep); + break; + case IT82xx2_EVT_XFER: + break; + default: + LOG_ERR("Unknown event type 0x%x", evt.event); + err = -EINVAL; + break; + } + + if (err) { + udc_submit_event(evt.dev, UDC_EVT_ERROR, err); + } + + if (evt.ep != USB_CONTROL_EP_OUT && !udc_ep_is_busy(evt.dev, evt.ep)) { + if (work_handler_xfer_next(evt.dev, evt.ep) == 0) { + udc_ep_set_busy(evt.dev, evt.ep, true); + } + } + } +} + +static inline bool it82xx2_check_ep0_stall(const struct device *dev, const uint8_t ep_idx, + const uint8_t transtype) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + + if (ep_idx != 0) { + return false; + } + + /* Check if the stall bit is set */ + if (ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit) { + ep_set_halt(dev, ep_idx, false); + if (transtype == DC_SETUP_TRANS) { + ff_regs[ep_idx].ep_rx_fifo_ctrl = FIFO_FORCE_EMPTY; + } + LOG_ERR("Cleared stall bit"); + return true; + } + + /* Check if the IN transaction is STALL */ + if ((transtype == DC_IN_TRANS) && (ep_regs[ep_idx].ep_status & DC_STALL_SENT)) { + return true; + } + + return false; +} + +static void it82xx2_usb_xfer_done(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct epn_ext_ctrl_regs *epn_ext_ctrl = + usb_regs->fifo_regs[EP_EXT_REGS_DX].ext_0_3.epn_ext_ctrl; + + for (uint8_t fifo_idx = 0; fifo_idx < 4; fifo_idx++) { + uint8_t ep, ep_idx, ep_ctrl, transtype; + + ep_ctrl = ep_regs[fifo_idx].ep_ctrl.value; + transtype = ep_regs[fifo_idx].ep_transtype_sts & DC_ALL_TRANS; + + if (!(ep_ctrl & ENDPOINT_EN) || (ep_ctrl & ENDPOINT_RDY)) { + continue; + } + + if (fifo_idx == 0) { + ep_idx = 0; + if (it82xx2_check_ep0_stall(dev, ep_idx, transtype)) { + continue; + } + } else { + ep_idx = (epn_ext_ctrl[fifo_idx].epn_ext_ctrl2 & COMPLETED_TRANS) >> 4; + if (ep_idx == 0) { + continue; + } + } + + switch (transtype) { + case DC_SETUP_TRANS: + /* SETUP transaction done */ + if (ep_idx != 0) { + break; + } + it82xx2_event_submit(dev, ep_idx, IT82xx2_EVT_SETUP_TOKEN); + break; + case DC_IN_TRANS: + /* IN transaction done */ + ep = USB_EP_DIR_IN | ep_idx; + it82xx2_event_submit(dev, ep, IT82xx2_EVT_IN_TOKEN); + break; + case DC_OUTDATA_TRANS: + /* OUT transaction done */ + ep = USB_EP_DIR_OUT | ep_idx; + it82xx2_event_submit(dev, ep, IT82xx2_EVT_OUT_TOKEN); + break; + default: + LOG_ERR("Unknown transaction type"); + break; + } + } +} + +static void it82xx2_usb_dc_isr(const void *arg) +{ + const struct device *dev = arg; + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_data *priv = udc_get_private(dev); + + uint8_t status = usb_regs->dc_interrupt_status & + usb_regs->dc_interrupt_mask; /* mask non enable int */ + + /* reset event */ + if (status & DC_RESET_EVENT) { + if ((usb_regs->dc_line_status & RX_LINE_STATE_MASK) == RX_LINE_RESET) { + it82xx2_dc_reset(dev); + usb_regs->dc_interrupt_status = DC_RESET_EVENT; + + udc_submit_event(dev, UDC_EVT_RESET, 0); + return; + } + usb_regs->dc_interrupt_status = DC_RESET_EVENT; + } + + /* sof received */ + if (status & DC_SOF_RECEIVED) { + it82xx2_enable_sof_int(dev, false); + k_work_reschedule(&priv->suspended_work, K_MSEC(5)); + } + + /* transaction done */ + if (status & DC_TRANS_DONE) { + /* clear interrupt before new transaction */ + usb_regs->dc_interrupt_status = DC_TRANS_DONE; + if (udc_is_suspended(dev) && udc_is_enabled(dev)) { + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + k_sem_give(&priv->suspended_sem); + } + it82xx2_usb_xfer_done(dev); + return; + } +} + +static void suspended_handler(struct k_work *item) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(item); + struct it82xx2_data *priv = CONTAINER_OF(dwork, struct it82xx2_data, suspended_work); + const struct device *dev = priv->dev; + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + if (usb_regs->dc_interrupt_status & DC_SOF_RECEIVED) { + usb_regs->dc_interrupt_status = DC_SOF_RECEIVED; + if (udc_is_suspended(dev) && udc_is_enabled(dev)) { + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + k_sem_give(&priv->suspended_sem); + } + k_work_reschedule(&priv->suspended_work, K_MSEC(5)); + return; + } + + it82xx2_enable_sof_int(dev, true); + + if (!udc_is_suspended(dev) && udc_is_enabled(dev)) { + udc_set_suspended(dev, true); + udc_submit_event(dev, UDC_EVT_SUSPEND, 0); + it82xx2_enable_wu_irq(dev, true); + it82xx2_enable_standby_state(true); + + k_sem_reset(&priv->suspended_sem); + } +} + +static int it82xx2_enable(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_data *priv = udc_get_private(dev); + + k_sem_init(&priv->suspended_sem, 0, 1); + k_work_init_delayable(&priv->suspended_work, suspended_handler); + + /* Initialize FIFO ready status */ + priv->fifo_ready[0] = false; + priv->fifo_ready[1] = false; + priv->fifo_ready[2] = false; + + /* Initialize FIFO semaphore */ + k_sem_init(&priv->fifo_sem[0], 1, 1); + k_sem_init(&priv->fifo_sem[1], 1, 1); + k_sem_init(&priv->fifo_sem[2], 1, 1); + + usb_regs->dc_control = DC_GLOBAL_ENABLE | DC_FULL_SPEED_LINE_POLARITY | + DC_FULL_SPEED_LINE_RATE | DC_CONNECT_TO_HOST; + + /* Enable USB D+ and USB interrupts */ + it82xx2_enable_wu_irq(dev, true); + irq_enable(config->usb_irq); + + return 0; +} + +static int it82xx2_disable(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + irq_disable(config->usb_irq); + + /* stop pull-up D+ D-*/ + usb_regs->dc_control &= ~DC_CONNECT_TO_HOST; + + return 0; +} + +static int it82xx2_init(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct gctrl_it8xxx2_regs *const gctrl_regs = GCTRL_IT8XXX2_REGS_BASE; + int ret; + + /* + * Disable USB debug path , prevent CPU enter + * JTAG mode and then reset by USB command. + */ + gctrl_regs->GCTRL_MCCR &= ~(IT8XXX2_GCTRL_MCCR_USB_EN); + gctrl_regs->gctrl_pmer2 |= IT8XXX2_GCTRL_PMER2_USB_PAD_EN; + + it82xx2_usb_dc_ip_init(dev); + + ret = udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, USB_EP_TYPE_CONTROL, + config->ep_cfg_out[0].caps.mps, 0); + if (ret) { + LOG_ERR("Failed to enable ep 0x%02x", USB_CONTROL_EP_OUT); + return ret; + } + + ret = udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, USB_EP_TYPE_CONTROL, + config->ep_cfg_in[0].caps.mps, 0); + if (ret) { + LOG_ERR("Failed to enable ep 0x%02x", USB_CONTROL_EP_IN); + return ret; + } + return 0; +} + +static int it82xx2_shutdown(const struct device *dev) +{ + if (udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT)) { + LOG_ERR("Failed to disable control endpoint"); + return -EIO; + } + + if (udc_ep_disable_internal(dev, USB_CONTROL_EP_IN)) { + LOG_ERR("Failed to disable control endpoint"); + return -EIO; + } + + return 0; +} + +static int it82xx2_lock(const struct device *dev) +{ + return udc_lock_internal(dev, K_FOREVER); +} + +static int it82xx2_unlock(const struct device *dev) +{ + return udc_unlock_internal(dev); +} + +static const struct udc_api it82xx2_api = { + .ep_enqueue = it82xx2_ep_enqueue, + .ep_dequeue = it82xx2_ep_dequeue, + .ep_set_halt = it82xx2_ep_set_halt, + .ep_clear_halt = it82xx2_ep_clear_halt, + .ep_try_config = NULL, + .ep_enable = it82xx2_ep_enable, + .ep_disable = it82xx2_ep_disable, + .host_wakeup = it82xx2_host_wakeup, + .set_address = it82xx2_set_address, + .enable = it82xx2_enable, + .disable = it82xx2_disable, + .init = it82xx2_init, + .shutdown = it82xx2_shutdown, + .lock = it82xx2_lock, + .unlock = it82xx2_unlock, +}; + +static int it82xx2_usb_driver_preinit(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct udc_data *data = dev->data; + struct it82xx2_data *priv = udc_get_private(dev); + int err; + + k_mutex_init(&data->mutex); + k_fifo_init(&priv->fifo); + + err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (err < 0) { + LOG_ERR("Failed to configure usb pins"); + return err; + } + + for (int i = 0; i < MAX_NUM_ENDPOINTS; i++) { + config->ep_cfg_out[i].caps.out = 1; + if (i == 0) { + config->ep_cfg_out[i].caps.control = 1; + config->ep_cfg_out[i].caps.mps = USB_CONTROL_EP_MPS; + } else if ((i % 3) == 2) { + config->ep_cfg_out[i].caps.bulk = 1; + config->ep_cfg_out[i].caps.interrupt = 1; + config->ep_cfg_out[i].caps.iso = 1; + config->ep_cfg_out[i].caps.mps = 64; + } + + config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; + err = udc_register_ep(dev, &config->ep_cfg_out[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + for (int i = 0; i < MAX_NUM_ENDPOINTS; i++) { + config->ep_cfg_in[i].caps.in = 1; + if (i == 0) { + config->ep_cfg_in[i].caps.control = 1; + config->ep_cfg_in[i].caps.mps = USB_CONTROL_EP_MPS; + } else if ((i % 3) != 2) { + config->ep_cfg_in[i].caps.bulk = 1; + config->ep_cfg_in[i].caps.interrupt = 1; + config->ep_cfg_in[i].caps.iso = 1; + config->ep_cfg_in[i].caps.mps = 64; + } + + config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i; + err = udc_register_ep(dev, &config->ep_cfg_in[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + data->caps.rwup = true; + data->caps.mps0 = UDC_MPS0_64; + + priv->dev = dev; + + config->make_thread(dev); + + /* Initializing WU (USB D+) */ + it8xxx2_usb_dc_wuc_init(dev); + + /* Connect USB interrupt */ + irq_connect_dynamic(config->usb_irq, 0, it82xx2_usb_dc_isr, dev, 0); + + return 0; +} + +#define IT82xx2_USB_DEVICE_DEFINE(n) \ + K_KERNEL_STACK_DEFINE(udc_it82xx2_stack_##n, CONFIG_UDC_IT82xx2_STACK_SIZE); \ + \ + static void udc_it82xx2_thread_##n(void *dev, void *arg1, void *arg2) \ + { \ + ARG_UNUSED(arg1); \ + ARG_UNUSED(arg2); \ + xfer_work_handler(dev); \ + } \ + \ + static void udc_it82xx2_make_thread_##n(const struct device *dev) \ + { \ + struct it82xx2_data *priv = udc_get_private(dev); \ + \ + k_thread_create(&priv->thread_data, udc_it82xx2_stack_##n, \ + K_THREAD_STACK_SIZEOF(udc_it82xx2_stack_##n), \ + udc_it82xx2_thread_##n, (void *)dev, NULL, NULL, K_PRIO_COOP(8), \ + 0, K_NO_WAIT); \ + k_thread_name_set(&priv->thread_data, dev->name); \ + } \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct udc_ep_config ep_cfg_out[MAX_NUM_ENDPOINTS]; \ + static struct udc_ep_config ep_cfg_in[MAX_NUM_ENDPOINTS]; \ + \ + static struct usb_it82xx2_config udc_cfg_##n = { \ + .base = (struct usb_it82xx2_regs *)DT_INST_REG_ADDR(n), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .wuc = {.dev = IT8XXX2_DEV_WUC(0, n), .mask = IT8XXX2_DEV_WUC_MASK(0, n)}, \ + .usb_irq = DT_INST_IRQ_BY_IDX(n, 0, irq), \ + .wu_irq = DT_INST_IRQ_BY_IDX(n, 1, irq), \ + .ep_cfg_in = ep_cfg_out, \ + .ep_cfg_out = ep_cfg_in, \ + .make_thread = udc_it82xx2_make_thread_##n, \ + }; \ + \ + static struct it82xx2_data priv_data_##n = {}; \ + \ + static struct udc_data udc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, it82xx2_usb_driver_preinit, NULL, &udc_data_##n, &udc_cfg_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &it82xx2_api); + +DT_INST_FOREACH_STATUS_OKAY(IT82xx2_USB_DEVICE_DEFINE) From 9e466591ad496b7bb5420afb33f58f522b79f881 Mon Sep 17 00:00:00 2001 From: Mark Wang Date: Mon, 27 May 2024 15:39:48 +0800 Subject: [PATCH 02/39] [nrf fromtree] drivers: udc: implement udc_mcux_ehci and udc_mcux_ip3511 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit udc_mcux_ehci is based on the MCUX USB controller driver (usb_device_ehci.c); udc_mcux_ip3511 is based on the MCUX USB controller driver (usb_device_lpcip3511.c); add related Kconfig and CMake; include the usb_phy.h path in modules/hal_nxp/usb/CMakeLists.txt because udc_mcux.c use it; add related macros to usb_device_config.h; update CMakeLists for udc_mcux_ehci and udc_mcux_ip3511. Signed-off-by: Mark Wang (cherry picked from commit b6b43c3ed70a8a5eaf92c8cef9b1eb1416e22829) Signed-off-by: Tomasz Moń --- drivers/usb/udc/CMakeLists.txt | 2 + drivers/usb/udc/Kconfig | 1 + drivers/usb/udc/Kconfig.mcux | 17 + drivers/usb/udc/udc_mcux_ehci.c | 929 ++++++++++++++++++++++++ drivers/usb/udc/udc_mcux_ip3511.c | 890 +++++++++++++++++++++++ modules/hal_nxp/CMakeLists.txt | 3 + modules/hal_nxp/usb/CMakeLists.txt | 5 +- modules/hal_nxp/usb/usb_device_config.h | 53 ++ 8 files changed, 1899 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/udc/Kconfig.mcux create mode 100644 drivers/usb/udc/udc_mcux_ehci.c create mode 100644 drivers/usb/udc/udc_mcux_ip3511.c diff --git a/drivers/usb/udc/CMakeLists.txt b/drivers/usb/udc/CMakeLists.txt index 2dd77536b26..6004729d373 100644 --- a/drivers/usb/udc/CMakeLists.txt +++ b/drivers/usb/udc/CMakeLists.txt @@ -13,3 +13,5 @@ zephyr_library_sources_ifdef(CONFIG_UDC_SKELETON udc_skeleton.c) zephyr_library_sources_ifdef(CONFIG_UDC_VIRTUAL udc_virtual.c) zephyr_library_sources_ifdef(CONFIG_UDC_STM32 udc_stm32.c) zephyr_library_sources_ifdef(CONFIG_UDC_IT82XX2 udc_it82xx2.c) +zephyr_library_sources_ifdef(CONFIG_UDC_NXP_EHCI udc_mcux_ehci.c) +zephyr_library_sources_ifdef(CONFIG_UDC_NXP_IP3511 udc_mcux_ip3511.c) diff --git a/drivers/usb/udc/Kconfig b/drivers/usb/udc/Kconfig index b407e1212b5..b4d1df0114a 100644 --- a/drivers/usb/udc/Kconfig +++ b/drivers/usb/udc/Kconfig @@ -54,5 +54,6 @@ source "drivers/usb/udc/Kconfig.skeleton" source "drivers/usb/udc/Kconfig.virtual" source "drivers/usb/udc/Kconfig.stm32" source "drivers/usb/udc/Kconfig.it82xx2" +source "drivers/usb/udc/Kconfig.mcux" endif # UDC_DRIVER diff --git a/drivers/usb/udc/Kconfig.mcux b/drivers/usb/udc/Kconfig.mcux new file mode 100644 index 00000000000..f36eab115b0 --- /dev/null +++ b/drivers/usb/udc/Kconfig.mcux @@ -0,0 +1,17 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config UDC_NXP_EHCI + bool "NXP MCUX USB EHCI Device controller driver" + default y + depends on DT_HAS_NXP_EHCI_ENABLED + select NOCACHE_MEMORY if HAS_MCUX_CACHE && CPU_HAS_DCACHE + help + NXP MCUX USB Device Controller Driver for EHCI. + +config UDC_NXP_IP3511 + bool "NXP MCUX USB IP3511 Device controller driver" + default y + depends on DT_HAS_NXP_LPCIP3511_ENABLED + help + NXP MCUX USB Device Controller Driver for KHCI. diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c new file mode 100644 index 00000000000..e5e56adb469 --- /dev/null +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -0,0 +1,929 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define DT_DRV_COMPAT nxp_ehci + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "udc_common.h" +#include "usb.h" +#include "usb_device_config.h" +#include "usb_device_mcux_drv_port.h" +#include "usb_device_ehci.h" +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED +#include "usb_phy.h" +#endif + +#include +LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL); + +/* + * There is no real advantage to change control endpoint size + * but we can use it for testing UDC driver API and higher layers. + */ +#define USB_MCUX_MPS0 UDC_MPS0_64 +#define USB_MCUX_EP0_SIZE 64 + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct udc_mcux_data, mcux_device) + +struct udc_mcux_config { + const usb_device_controller_interface_struct_t *mcux_if; + void (*irq_enable_func)(const struct device *dev); + void (*irq_disable_func)(const struct device *dev); + size_t num_of_eps; + struct udc_ep_config *ep_cfg_in; + struct udc_ep_config *ep_cfg_out; + uintptr_t base; + const struct pinctrl_dev_config *pincfg; +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED + usb_phy_config_struct_t *phy_config; +#endif +}; + +struct udc_mcux_data { + const struct device *dev; + usb_device_struct_t mcux_device; + uint8_t controller_id; /* 0xFF is invalid value */ +}; + +/* TODO: implement the cache maintenance + * solution1: Use the non-cached buf to do memcpy before/after giving buffer to usb controller. + * solution2: Use cache API to flush/invalid cache. but it needs the given buffer is + * cache line size aligned and the buffer range cover multiple of cache line size block. + * Need to change the usb stack to implement it, will try to implement it later. + */ +#if defined(CONFIG_NOCACHE_MEMORY) +K_HEAP_DEFINE_NOCACHE(mcux_packet_alloc_pool, USB_DEVICE_CONFIG_ENDPOINTS * 2u * 1024u); + +/* allocate non-cached buffer for usb */ +static void *udc_mcux_nocache_alloc(uint32_t size) +{ + void *p = (void *)k_heap_alloc(&mcux_packet_alloc_pool, size, K_NO_WAIT); + + if (p != NULL) { + (void)memset(p, 0, size); + } + + return p; +} + +/* free the allocated non-cached buffer */ +static void udc_mcux_nocache_free(void *p) +{ + if (p == NULL) { + return; + } + k_heap_free(&mcux_packet_alloc_pool, p); +} +#endif + +static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command, + void *param) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + status = mcux_if->deviceControl(priv->mcux_device.controllerHandle, + command, param); + + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +/* If ep is busy, return busy. Otherwise feed the buf to controller */ +static int udc_mcux_ep_feed(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status = kStatus_USB_Success; + uint8_t *data; + uint32_t len; + unsigned int key; + usb_device_endpoint_status_struct_t ep_status; + + ep_status.endpointAddress = cfg->addr; + udc_mcux_control(dev, kUSB_DeviceControlGetEndpointStatus, &ep_status); + if (ep_status.endpointStatus == kUSB_DeviceEndpointStateStalled) { + return -EACCES; /* stalled */ + } + + key = irq_lock(); + if (!udc_ep_is_busy(dev, cfg->addr)) { + udc_ep_set_busy(dev, cfg->addr, true); + irq_unlock(key); + + if (USB_EP_DIR_IS_OUT(cfg->addr)) { + len = net_buf_tailroom(buf); +#if defined(CONFIG_NOCACHE_MEMORY) + data = (len == 0 ? NULL : udc_mcux_nocache_alloc(len)); +#else + data = net_buf_tail(buf); +#endif + status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, + cfg->addr, data, len); + } else { + len = buf->len; +#if defined(CONFIG_NOCACHE_MEMORY) + data = (len == 0 ? NULL : udc_mcux_nocache_alloc(len)); + memcpy(data, buf->data, len); +#else + data = buf->data; +#endif + status = mcux_if->deviceSend(priv->mcux_device.controllerHandle, + cfg->addr, data, len); + } + + key = irq_lock(); + if (status != kStatus_USB_Success) { + udc_ep_set_busy(dev, cfg->addr, false); + } + irq_unlock(key); + } else { + irq_unlock(key); + return -EBUSY; + } + + return (status == kStatus_USB_Success ? 0 : -EIO); +} + +/* return success if the ep is busy or stalled. */ +static int udc_mcux_ep_try_feed(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *feed_buf; + + feed_buf = udc_buf_peek(dev, cfg->addr); + if (feed_buf) { + int ret = udc_mcux_ep_feed(dev, cfg, feed_buf); + + return ((ret == -EBUSY || ret == -EACCES || ret == 0) ? 0 : -EIO); + } + + return 0; +} + +/* + * Allocate buffer and initiate a new control OUT transfer. + */ +static int udc_mcux_ctrl_feed_dout(const struct device *dev, + const size_t length) +{ + struct net_buf *buf; + struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + int ret; + + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); + if (buf == NULL) { + return -ENOMEM; + } + + net_buf_put(&cfg->fifo, buf); + + ret = udc_mcux_ep_feed(dev, cfg, buf); + + if (ret) { + net_buf_unref(buf); + return ret; + } + + return 0; +} + +static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_packet *setup) +{ + int err; + struct net_buf *buf; + + LOG_DBG("setup packet"); + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, + sizeof(struct usb_setup_packet)); + if (buf == NULL) { + LOG_ERR("Failed to allocate for setup"); + return -EIO; + } + + udc_ep_buf_set_setup(buf); + memcpy(buf->data, setup, 8); + net_buf_add(buf, 8); + + if (setup->RequestType.type == USB_REQTYPE_TYPE_STANDARD && + setup->RequestType.direction == USB_REQTYPE_DIR_TO_DEVICE && + setup->bRequest == USB_SREQ_SET_ADDRESS && + setup->wLength == 0) { + udc_mcux_control(dev, kUSB_DeviceControlPreSetDeviceAddress, + &setup->wValue); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (!buf->len) { + return -EIO; + } + + if (udc_ctrl_stage_is_data_out(dev)) { + /* Allocate and feed buffer for data OUT stage */ + LOG_DBG("s:%p|feed for -out-", buf); + err = udc_mcux_ctrl_feed_dout(dev, udc_data_stage_length(buf)); + if (err == -ENOMEM) { + err = udc_submit_ep_event(dev, buf, err); + } + } else if (udc_ctrl_stage_is_data_in(dev)) { + err = udc_ctrl_submit_s_in_status(dev); + } else { + err = udc_ctrl_submit_s_status(dev); + } + + return err; +} + +static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *buf, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err = 0; + uint32_t len; + + len = MIN(net_buf_tailroom(buf), mcux_len); +#if defined(CONFIG_NOCACHE_MEMORY) + memcpy(net_buf_tail(buf), mcux_buf, len); + udc_mcux_nocache_free(mcux_buf); +#endif + net_buf_add(buf, len); + if (udc_ctrl_stage_is_status_out(dev)) { + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + /* Status stage finished, notify upper layer */ + err = udc_ctrl_submit_status(dev, buf); + } else { + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + } + + if (udc_ctrl_stage_is_status_in(dev)) { + err = udc_ctrl_submit_s_out_status(dev, buf); + } + + return err; +} + +static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *buf, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err = 0; + uint32_t len; + + len = MIN(buf->len, mcux_len); + buf->data += len; + buf->len -= len; +#if defined(CONFIG_NOCACHE_MEMORY) + udc_mcux_nocache_free(mcux_buf); +#endif + + if (udc_ctrl_stage_is_status_in(dev) || + udc_ctrl_stage_is_no_data(dev)) { + /* Status stage finished, notify upper layer */ + err = udc_ctrl_submit_status(dev, buf); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (udc_ctrl_stage_is_status_out(dev)) { + /* + * IN transfer finished, release buffer, + * control OUT buffer should be already fed. + */ + net_buf_unref(buf); + err = udc_mcux_ctrl_feed_dout(dev, 0u); + } + + return err; +} + +static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep, + struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + uint32_t len; + + len = MIN(buf->len, mcux_len); + buf->data += len; + buf->len -= len; + +#if defined(CONFIG_NOCACHE_MEMORY) + udc_mcux_nocache_free(mcux_buf); +#endif + err = udc_submit_ep_event(dev, buf, 0); + udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); + + return err; +} + +static int udc_mcux_handler_non_ctrl_out(const struct device *dev, uint8_t ep, + struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + uint32_t len; + + len = MIN(net_buf_tailroom(buf), mcux_len); +#if defined(CONFIG_NOCACHE_MEMORY) + memcpy(net_buf_tail(buf), mcux_buf, len); +#endif + net_buf_add(buf, len); + +#if defined(CONFIG_NOCACHE_MEMORY) + udc_mcux_nocache_free(mcux_buf); +#endif + err = udc_submit_ep_event(dev, buf, 0); + udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); + + return err; +} + +static int udc_mcux_handler_out(const struct device *dev, uint8_t ep, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + struct net_buf *buf; + unsigned int key; + + buf = udc_buf_get(dev, ep); + + key = irq_lock(); + udc_ep_set_busy(dev, ep, false); + irq_unlock(key); + + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + + if (ep == USB_CONTROL_EP_OUT) { + err = udc_mcux_handler_ctrl_out(dev, buf, mcux_buf, mcux_len); + } else { + err = udc_mcux_handler_non_ctrl_out(dev, ep, buf, mcux_buf, mcux_len); + } + + return err; +} + +/* return true - zlp is feed; false - no zlp */ +static bool udc_mcux_handler_zlt(const struct device *dev, uint8_t ep, struct net_buf *buf, + uint16_t mcux_len) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + + /* The whole transfer is already done by MCUX controller driver. */ + if (mcux_len >= buf->len) { + if (udc_ep_buf_has_zlp(buf)) { + usb_status_t status; + + udc_ep_buf_clear_zlp(buf); + status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, + ep, NULL, 0); + if (status != kStatus_USB_Success) { + udc_submit_event(dev, UDC_EVT_ERROR, -EIO); + return false; + } + return true; + } + } + + return false; +} + +static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + struct net_buf *buf; + unsigned int key; + + buf = udc_buf_peek(dev, ep); + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + + if (udc_mcux_handler_zlt(dev, ep, buf, mcux_len)) { + return 0; + } + + buf = udc_buf_get(dev, ep); + + key = irq_lock(); + udc_ep_set_busy(dev, ep, false); + irq_unlock(key); + + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + if (ep == USB_CONTROL_EP_IN) { + err = udc_mcux_handler_ctrl_in(dev, buf, mcux_buf, mcux_len); + } else { + err = udc_mcux_handler_non_ctrl_in(dev, ep, buf, mcux_buf, mcux_len); + } + + return err; +} + +/* NXP MCUX controller driver notify transfers/status through this interface */ +usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) +{ + usb_device_callback_message_struct_t *mcux_msg = msg; + uint8_t ep; + usb_device_notification_t mcux_notify; + struct udc_mcux_data *priv; + const struct device *dev; + usb_status_t mcux_status = kStatus_USB_Success; + int err = 0; + + if ((NULL == msg) || (NULL == handle)) { + return kStatus_USB_InvalidHandle; + } + + mcux_notify = (usb_device_notification_t)mcux_msg->code; + priv = (struct udc_mcux_data *)(PRV_DATA_HANDLE(handle)); + dev = priv->dev; + + switch (mcux_notify) { + case kUSB_DeviceNotifyBusReset: + struct udc_ep_config *cfg; + + udc_mcux_control(dev, kUSB_DeviceControlSetDefaultStatus, NULL); + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + if (cfg->stat.enabled) { + udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT); + } + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + if (cfg->stat.enabled) { + udc_ep_disable_internal(dev, USB_CONTROL_EP_IN); + } + if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + return -EIO; + } + + if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + return -EIO; + } + udc_submit_event(dev, UDC_EVT_RESET, 0); + break; + case kUSB_DeviceNotifyError: + udc_submit_event(dev, UDC_EVT_ERROR, -EIO); + break; + case kUSB_DeviceNotifySuspend: + udc_set_suspended(dev, true); + udc_submit_event(dev, UDC_EVT_SUSPEND, 0); + break; + case kUSB_DeviceNotifyResume: + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + break; + case kUSB_DeviceNotifyLPMSleep: + break; + case kUSB_DeviceNotifyDetach: + udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); + break; + case kUSB_DeviceNotifyAttach: + udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); + break; + default: + ep = mcux_msg->code; + if (mcux_msg->isSetup) { + struct usb_setup_packet *setup = + (struct usb_setup_packet *)mcux_msg->buffer; + + err = udc_mcux_handler_setup(dev, setup); + } else if (USB_EP_DIR_IS_IN(ep)) { + err = udc_mcux_handler_in(dev, ep, mcux_msg->buffer, mcux_msg->length); + } else { + err = udc_mcux_handler_out(dev, ep, mcux_msg->buffer, mcux_msg->length); + } + + break; + } + + if (unlikely(err)) { + udc_submit_event(dev, UDC_EVT_ERROR, err); + mcux_status = kStatus_USB_Error; + } + return mcux_status; +} + +static void udc_mcux_isr(const struct device *dev) +{ + struct udc_mcux_data *priv = udc_get_private(dev); + + USB_DeviceEhciIsrFunction((void *)(&priv->mcux_device)); +} + +/* Return actual USB device speed */ +static enum udc_bus_speed udc_mcux_device_speed(const struct device *dev) +{ + int err; + uint8_t mcux_speed; + + err = udc_mcux_control(dev, kUSB_DeviceControlGetSpeed, &mcux_speed); + if (err) { + /* + * In the current version of all NXP USB device drivers, + * no error is returned if the parameter is correct. + */ + return UDC_BUS_SPEED_FS; + } + + switch (mcux_speed) { + case USB_SPEED_HIGH: + return UDC_BUS_SPEED_HS; + case USB_SPEED_LOW: + __ASSERT(false, "Low speed mode not supported"); + __fallthrough; + case USB_SPEED_FULL: + __fallthrough; + default: + return UDC_BUS_SPEED_FS; + } +} + +static int udc_mcux_ep_enqueue(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + udc_buf_put(cfg, buf); + if (cfg->stat.halted) { + LOG_DBG("ep 0x%02x halted", cfg->addr); + return 0; + } + + return udc_mcux_ep_try_feed(dev, cfg); +} + +static int udc_mcux_ep_dequeue(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *buf; + unsigned int key; + + cfg->stat.halted = false; + buf = udc_buf_get_all(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, -ECONNABORTED); + } + + key = irq_lock(); + udc_ep_set_busy(dev, cfg->addr, false); + irq_unlock(key); + + return 0; +} + +static int udc_mcux_ep_set_halt(const struct device *dev, + struct udc_ep_config *const cfg) +{ + return udc_mcux_control(dev, kUSB_DeviceControlEndpointStall, &cfg->addr); +} + +static int udc_mcux_ep_clear_halt(const struct device *dev, + struct udc_ep_config *const cfg) +{ + (void)udc_mcux_control(dev, kUSB_DeviceControlEndpointUnstall, &cfg->addr); + /* transfer is enqueued after stalled */ + return udc_mcux_ep_try_feed(dev, cfg); +} + +static int udc_mcux_ep_enable(const struct device *dev, + struct udc_ep_config *const cfg) +{ + usb_device_endpoint_init_struct_t ep_init; + + LOG_DBG("Enable ep 0x%02x", cfg->addr); + + ep_init.zlt = 0U; + ep_init.interval = cfg->interval; + ep_init.endpointAddress = cfg->addr; + ep_init.maxPacketSize = cfg->mps; + + switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_CONTROL: + ep_init.transferType = USB_ENDPOINT_CONTROL; + break; + case USB_EP_TYPE_BULK: + ep_init.transferType = USB_ENDPOINT_BULK; + break; + case USB_EP_TYPE_INTERRUPT: + ep_init.transferType = USB_ENDPOINT_INTERRUPT; + break; + case USB_EP_TYPE_ISO: + ep_init.transferType = USB_ENDPOINT_ISOCHRONOUS; + break; + default: + return -EINVAL; + } + + return udc_mcux_control(dev, kUSB_DeviceControlEndpointInit, &ep_init); +} + +static int udc_mcux_ep_disable(const struct device *dev, + struct udc_ep_config *const cfg) +{ + LOG_DBG("Disable ep 0x%02x", cfg->addr); + + return udc_mcux_control(dev, kUSB_DeviceControlEndpointDeinit, &cfg->addr); +} + +static int udc_mcux_host_wakeup(const struct device *dev) +{ + return -ENOTSUP; +} + +static int udc_mcux_set_address(const struct device *dev, const uint8_t addr) +{ + uint8_t temp_addr = addr; + + return udc_mcux_control(dev, kUSB_DeviceControlSetDeviceAddress, &temp_addr); +} + +static int udc_mcux_enable(const struct device *dev) +{ + return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL); +} + +static int udc_mcux_disable(const struct device *dev) +{ + return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL); +} + +static int udc_mcux_init(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED + if (config->phy_config != NULL) { + USB_EhciPhyInit(priv->controller_id, 0u, + (usb_phy_config_struct_t *)&config->phy_config); + } +#endif + + /* Init MCUX USB device driver. */ + status = mcux_if->deviceInit(priv->controller_id, + &priv->mcux_device, &(priv->mcux_device.controllerHandle)); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + /* enable USB interrupt */ + config->irq_enable_func(dev); + + LOG_DBG("Initialized USB controller %x", (uint32_t)config->base); + + return 0; +} + +static int udc_mcux_shutdown(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + /* Disable interrupt */ + config->irq_disable_func(dev); + + /* De-init MCUX USB device driver. */ + status = mcux_if->deviceDeinit(priv->mcux_device.controllerHandle); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +static int udc_mcux_lock(const struct device *dev) +{ + return udc_lock_internal(dev, K_FOREVER); +} + +static int udc_mcux_unlock(const struct device *dev) +{ + return udc_unlock_internal(dev); +} + +static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv, + const struct udc_mcux_config *config) +{ + /* + * MCUX USB controller drivers use an ID to tell the HAL drivers + * which controller is being used. This part of the code converts + * the base address to the ID value. + */ +#ifdef USBHS_STACK_BASE_ADDRS + uintptr_t usb_base_addrs[] = USBHS_STACK_BASE_ADDRS; +#else + uintptr_t usb_base_addrs[] = USBHS_BASE_ADDRS; +#endif + + /* get the right controller id */ + priv->controller_id = 0xFFu; /* invalid value */ + for (uint8_t i = 0; i < ARRAY_SIZE(usb_base_addrs); i++) { + if (usb_base_addrs[i] == config->base) { + priv->controller_id = kUSB_ControllerEhci0 + i; + break; + } + } +} + +static int udc_mcux_driver_preinit(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + struct udc_data *data = dev->data; + struct udc_mcux_data *priv = data->priv; + int err; + + udc_mcux_get_hal_driver_id(priv, config); + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + + k_mutex_init(&data->mutex); + + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_out[i].caps.out = 1; + if (i == 0) { + config->ep_cfg_out[i].caps.control = 1; + config->ep_cfg_out[i].caps.mps = 64; + } else { + config->ep_cfg_out[i].caps.bulk = 1; + config->ep_cfg_out[i].caps.interrupt = 1; + config->ep_cfg_out[i].caps.iso = 1; + config->ep_cfg_out[i].caps.mps = 1024; + } + + config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; + err = udc_register_ep(dev, &config->ep_cfg_out[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_in[i].caps.in = 1; + if (i == 0) { + config->ep_cfg_in[i].caps.control = 1; + config->ep_cfg_in[i].caps.mps = 64; + } else { + config->ep_cfg_in[i].caps.bulk = 1; + config->ep_cfg_in[i].caps.interrupt = 1; + config->ep_cfg_in[i].caps.iso = 1; + config->ep_cfg_in[i].caps.mps = 1024; + } + + config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i; + err = udc_register_ep(dev, &config->ep_cfg_in[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + /* Requires udc_mcux_host_wakeup() implementation */ + data->caps.rwup = false; + data->caps.mps0 = USB_MCUX_MPS0; + data->caps.hs = true; + priv->dev = dev; + + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + + return 0; +} + +static const struct udc_api udc_mcux_api = { + .device_speed = udc_mcux_device_speed, + .ep_enqueue = udc_mcux_ep_enqueue, + .ep_dequeue = udc_mcux_ep_dequeue, + .ep_set_halt = udc_mcux_ep_set_halt, + .ep_clear_halt = udc_mcux_ep_clear_halt, + .ep_try_config = NULL, + .ep_enable = udc_mcux_ep_enable, + .ep_disable = udc_mcux_ep_disable, + .host_wakeup = udc_mcux_host_wakeup, + .set_address = udc_mcux_set_address, + .enable = udc_mcux_enable, + .disable = udc_mcux_disable, + .init = udc_mcux_init, + .shutdown = udc_mcux_shutdown, + .lock = udc_mcux_lock, + .unlock = udc_mcux_unlock, +}; + +/* EHCI device driver interface */ +static const usb_device_controller_interface_struct_t udc_mcux_if = { + USB_DeviceEhciInit, USB_DeviceEhciDeinit, USB_DeviceEhciSend, + USB_DeviceEhciRecv, USB_DeviceEhciCancel, USB_DeviceEhciControl +}; + +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED +#define UDC_MCUX_PHY_DEFINE(n) \ +static usb_phy_config_struct_t phy_config_##n = { \ + .D_CAL = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_d_cal, 0), \ + .TXCAL45DP = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dp_ohms, 0), \ + .TXCAL45DM = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dm_ohms, 0), \ +} + +#define UDC_MCUX_PHY_DEFINE_OR(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + (UDC_MCUX_PHY_DEFINE(n)), ()) + +#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) \ + .phy_config = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + (&phy_config_##n), (NULL)) +#else +#define UDC_MCUX_PHY_DEFINE_OR(n) +#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) +#endif + +#define USB_MCUX_EHCI_DEVICE_DEFINE(n) \ + UDC_MCUX_PHY_DEFINE_OR(n); \ + \ + static void udc_irq_enable_func##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + udc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void udc_irq_disable_func##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static struct udc_ep_config \ + ep_cfg_out##n[DT_INST_PROP(n, num_bidir_endpoints)]; \ + static struct udc_ep_config \ + ep_cfg_in##n[DT_INST_PROP(n, num_bidir_endpoints)]; \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct udc_mcux_config priv_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_enable_func = udc_irq_enable_func##n, \ + .irq_disable_func = udc_irq_disable_func##n, \ + .num_of_eps = DT_INST_PROP(n, num_bidir_endpoints), \ + .ep_cfg_in = ep_cfg_in##n, \ + .ep_cfg_out = ep_cfg_out##n, \ + .mcux_if = &udc_mcux_if, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ + }; \ + \ + static struct udc_mcux_data priv_data_##n = { \ + }; \ + \ + static struct udc_data udc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \ + &udc_data_##n, &priv_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &udc_mcux_api); + +DT_INST_FOREACH_STATUS_OKAY(USB_MCUX_EHCI_DEVICE_DEFINE) diff --git a/drivers/usb/udc/udc_mcux_ip3511.c b/drivers/usb/udc/udc_mcux_ip3511.c new file mode 100644 index 00000000000..d3b257e80e7 --- /dev/null +++ b/drivers/usb/udc/udc_mcux_ip3511.c @@ -0,0 +1,890 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define DT_DRV_COMPAT nxp_lpcip3511 + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "udc_common.h" +#include "usb.h" +#include "usb_device_config.h" +#include "usb_device_mcux_drv_port.h" +#include "usb_device_lpcip3511.h" +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED +#include "usb_phy.h" +#endif + +#include +LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL); + +/* + * There is no real advantage to change control endpoint size + * but we can use it for testing UDC driver API and higher layers. + */ +#define USB_MCUX_MPS0 UDC_MPS0_64 +#define USB_MCUX_EP0_SIZE 64 + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct udc_mcux_data, mcux_device) + +struct udc_mcux_config { + const usb_device_controller_interface_struct_t *mcux_if; + void (*irq_enable_func)(const struct device *dev); + void (*irq_disable_func)(const struct device *dev); + size_t num_of_eps; + struct udc_ep_config *ep_cfg_in; + struct udc_ep_config *ep_cfg_out; + uintptr_t base; + const struct pinctrl_dev_config *pincfg; +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED + usb_phy_config_struct_t *phy_config; +#endif +}; + +struct udc_mcux_data { + const struct device *dev; + usb_device_struct_t mcux_device; + uint8_t controller_id; /* 0xFF is invalid value */ +}; + +static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command, + void *param) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + status = mcux_if->deviceControl(priv->mcux_device.controllerHandle, + command, param); + + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +/* If ep is busy, return busy. Otherwise feed the buf to controller */ +static int udc_mcux_ep_feed(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status = kStatus_USB_Success; + uint8_t *data; + uint32_t len; + unsigned int key; + usb_device_endpoint_status_struct_t ep_status; + + ep_status.endpointAddress = cfg->addr; + udc_mcux_control(dev, kUSB_DeviceControlGetEndpointStatus, &ep_status); + if (ep_status.endpointStatus == kUSB_DeviceEndpointStateStalled) { + return -EACCES; /* stalled */ + } + + key = irq_lock(); + if (!udc_ep_is_busy(dev, cfg->addr)) { + udc_ep_set_busy(dev, cfg->addr, true); + irq_unlock(key); + + if (USB_EP_DIR_IS_OUT(cfg->addr)) { + len = net_buf_tailroom(buf); + data = net_buf_tail(buf); + status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, + cfg->addr, data, len); + } else { + len = buf->len; + data = buf->data; + status = mcux_if->deviceSend(priv->mcux_device.controllerHandle, + cfg->addr, data, len); + } + + key = irq_lock(); + if (status != kStatus_USB_Success) { + udc_ep_set_busy(dev, cfg->addr, false); + } + irq_unlock(key); + } else { + irq_unlock(key); + return -EBUSY; + } + + return (status == kStatus_USB_Success ? 0 : -EIO); +} + +/* return success if the ep is busy or stalled. */ +static int udc_mcux_ep_try_feed(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *feed_buf; + + feed_buf = udc_buf_peek(dev, cfg->addr); + if (feed_buf) { + int ret = udc_mcux_ep_feed(dev, cfg, feed_buf); + + return ((ret == -EBUSY || ret == -EACCES || ret == 0) ? 0 : -EIO); + } + + return 0; +} + +/* + * Allocate buffer and initiate a new control OUT transfer. + */ +static int udc_mcux_ctrl_feed_dout(const struct device *dev, + const size_t length) +{ + struct net_buf *buf; + struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + int ret; + + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); + if (buf == NULL) { + return -ENOMEM; + } + + net_buf_put(&cfg->fifo, buf); + + ret = udc_mcux_ep_feed(dev, cfg, buf); + + if (ret) { + net_buf_unref(buf); + return ret; + } + + return 0; +} + +static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_packet *setup) +{ + int err; + struct net_buf *buf; + + LOG_DBG("setup packet"); + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, + sizeof(struct usb_setup_packet)); + if (buf == NULL) { + LOG_ERR("Failed to allocate for setup"); + return -EIO; + } + + udc_ep_buf_set_setup(buf); + memcpy(buf->data, setup, 8); + net_buf_add(buf, 8); + + if (setup->RequestType.type == USB_REQTYPE_TYPE_STANDARD && + setup->RequestType.direction == USB_REQTYPE_DIR_TO_DEVICE && + setup->bRequest == USB_SREQ_SET_ADDRESS && + setup->wLength == 0) { + udc_mcux_control(dev, kUSB_DeviceControlPreSetDeviceAddress, + &setup->wValue); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (!buf->len) { + return -EIO; + } + + if (udc_ctrl_stage_is_data_out(dev)) { + /* Allocate and feed buffer for data OUT stage */ + LOG_DBG("s:%p|feed for -out-", buf); + err = udc_mcux_ctrl_feed_dout(dev, udc_data_stage_length(buf)); + if (err == -ENOMEM) { + err = udc_submit_ep_event(dev, buf, err); + } + } else if (udc_ctrl_stage_is_data_in(dev)) { + err = udc_ctrl_submit_s_in_status(dev); + } else { + err = udc_ctrl_submit_s_status(dev); + } + + return err; +} + +static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *buf, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err = 0; + uint32_t len; + + len = MIN(net_buf_tailroom(buf), mcux_len); + net_buf_add(buf, len); + if (udc_ctrl_stage_is_status_out(dev)) { + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + /* Status stage finished, notify upper layer */ + err = udc_ctrl_submit_status(dev, buf); + } else { + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + } + + if (udc_ctrl_stage_is_status_in(dev)) { + err = udc_ctrl_submit_s_out_status(dev, buf); + } + + return err; +} + +static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *buf, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err = 0; + uint32_t len; + + len = MIN(buf->len, mcux_len); + buf->data += len; + buf->len -= len; + + if (udc_ctrl_stage_is_status_in(dev) || + udc_ctrl_stage_is_no_data(dev)) { + /* Status stage finished, notify upper layer */ + err = udc_ctrl_submit_status(dev, buf); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (udc_ctrl_stage_is_status_out(dev)) { + /* + * IN transfer finished, release buffer, + * control OUT buffer should be already fed. + */ + net_buf_unref(buf); + err = udc_mcux_ctrl_feed_dout(dev, 0u); + } + + return err; +} + +static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep, + struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + uint32_t len; + + len = MIN(buf->len, mcux_len); + buf->data += len; + buf->len -= len; + + err = udc_submit_ep_event(dev, buf, 0); + udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); + + return err; +} + +static int udc_mcux_handler_non_ctrl_out(const struct device *dev, uint8_t ep, + struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + uint32_t len; + + len = MIN(net_buf_tailroom(buf), mcux_len); + net_buf_add(buf, len); + + err = udc_submit_ep_event(dev, buf, 0); + udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); + + return err; +} + +static int udc_mcux_handler_out(const struct device *dev, uint8_t ep, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + struct net_buf *buf; + unsigned int key; + + buf = udc_buf_get(dev, ep); + + key = irq_lock(); + udc_ep_set_busy(dev, ep, false); + irq_unlock(key); + + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + + if (ep == USB_CONTROL_EP_OUT) { + err = udc_mcux_handler_ctrl_out(dev, buf, mcux_buf, mcux_len); + } else { + err = udc_mcux_handler_non_ctrl_out(dev, ep, buf, mcux_buf, mcux_len); + } + + return err; +} + +/* return true - zlp is feed; false - no zlp */ +static bool udc_mcux_handler_zlt(const struct device *dev, uint8_t ep, struct net_buf *buf, + uint16_t mcux_len) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + + /* The whole transfer is already done by MCUX controller driver. */ + if (mcux_len >= buf->len) { + if (udc_ep_buf_has_zlp(buf)) { + usb_status_t status; + + udc_ep_buf_clear_zlp(buf); + status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, + ep, NULL, 0); + if (status != kStatus_USB_Success) { + udc_submit_event(dev, UDC_EVT_ERROR, -EIO); + return false; + } + return true; + } + } + + return false; +} + +static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + struct net_buf *buf; + unsigned int key; + + buf = udc_buf_peek(dev, ep); + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + + if (udc_mcux_handler_zlt(dev, ep, buf, mcux_len)) { + return 0; + } + + buf = udc_buf_get(dev, ep); + + key = irq_lock(); + udc_ep_set_busy(dev, ep, false); + irq_unlock(key); + + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + if (ep == USB_CONTROL_EP_IN) { + err = udc_mcux_handler_ctrl_in(dev, buf, mcux_buf, mcux_len); + } else { + err = udc_mcux_handler_non_ctrl_in(dev, ep, buf, mcux_buf, mcux_len); + } + + return err; +} + +/* NXP MCUX controller driver notify transfers/status through this interface */ +usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) +{ + usb_device_callback_message_struct_t *mcux_msg = msg; + uint8_t ep; + usb_device_notification_t mcux_notify; + struct udc_mcux_data *priv; + const struct device *dev; + usb_status_t mcux_status = kStatus_USB_Success; + int err = 0; + + if ((NULL == msg) || (NULL == handle)) { + return kStatus_USB_InvalidHandle; + } + + mcux_notify = (usb_device_notification_t)mcux_msg->code; + priv = (struct udc_mcux_data *)(PRV_DATA_HANDLE(handle)); + dev = priv->dev; + + switch (mcux_notify) { + case kUSB_DeviceNotifyBusReset: + struct udc_ep_config *cfg; + + udc_mcux_control(dev, kUSB_DeviceControlSetDefaultStatus, NULL); + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + if (cfg->stat.enabled) { + udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT); + } + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + if (cfg->stat.enabled) { + udc_ep_disable_internal(dev, USB_CONTROL_EP_IN); + } + if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + return -EIO; + } + + if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + return -EIO; + } + udc_submit_event(dev, UDC_EVT_RESET, 0); + break; + case kUSB_DeviceNotifyError: + udc_submit_event(dev, UDC_EVT_ERROR, -EIO); + break; + case kUSB_DeviceNotifySuspend: + udc_set_suspended(dev, true); + udc_submit_event(dev, UDC_EVT_SUSPEND, 0); + break; + case kUSB_DeviceNotifyResume: + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + break; + case kUSB_DeviceNotifyLPMSleep: + break; + case kUSB_DeviceNotifyDetach: + udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); + break; + case kUSB_DeviceNotifyAttach: + udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); + break; + default: + ep = mcux_msg->code; + if (mcux_msg->isSetup) { + struct usb_setup_packet *setup = + (struct usb_setup_packet *)mcux_msg->buffer; + + err = udc_mcux_handler_setup(dev, setup); + } else if (USB_EP_DIR_IS_IN(ep)) { + err = udc_mcux_handler_in(dev, ep, mcux_msg->buffer, mcux_msg->length); + } else { + err = udc_mcux_handler_out(dev, ep, mcux_msg->buffer, mcux_msg->length); + } + + break; + } + + if (unlikely(err)) { + udc_submit_event(dev, UDC_EVT_ERROR, err); + mcux_status = kStatus_USB_Error; + } + return mcux_status; +} + +static void udc_mcux_isr(const struct device *dev) +{ + struct udc_mcux_data *priv = udc_get_private(dev); + + USB_DeviceLpcIp3511IsrFunction((void *)(&priv->mcux_device)); +} + +/* Return actual USB device speed */ +static enum udc_bus_speed udc_mcux_device_speed(const struct device *dev) +{ + int err; + uint8_t mcux_speed; + + err = udc_mcux_control(dev, kUSB_DeviceControlGetSpeed, &mcux_speed); + if (err) { + /* + * In the current version of all NXP USB device drivers, + * no error is returned if the parameter is correct. + */ + return UDC_BUS_SPEED_FS; + } + + switch (mcux_speed) { + case USB_SPEED_HIGH: + return UDC_BUS_SPEED_HS; + case USB_SPEED_LOW: + __ASSERT(false, "Low speed mode not supported"); + __fallthrough; + case USB_SPEED_FULL: + __fallthrough; + default: + return UDC_BUS_SPEED_FS; + } +} + +static int udc_mcux_ep_enqueue(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + udc_buf_put(cfg, buf); + if (cfg->stat.halted) { + LOG_DBG("ep 0x%02x halted", cfg->addr); + return 0; + } + + return udc_mcux_ep_try_feed(dev, cfg); +} + +static int udc_mcux_ep_dequeue(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *buf; + unsigned int key; + + cfg->stat.halted = false; + buf = udc_buf_get_all(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, -ECONNABORTED); + } + + key = irq_lock(); + udc_ep_set_busy(dev, cfg->addr, false); + irq_unlock(key); + + return 0; +} + +static int udc_mcux_ep_set_halt(const struct device *dev, + struct udc_ep_config *const cfg) +{ + return udc_mcux_control(dev, kUSB_DeviceControlEndpointStall, &cfg->addr); +} + +static int udc_mcux_ep_clear_halt(const struct device *dev, + struct udc_ep_config *const cfg) +{ + (void)udc_mcux_control(dev, kUSB_DeviceControlEndpointUnstall, &cfg->addr); + /* transfer is enqueued after stalled */ + return udc_mcux_ep_try_feed(dev, cfg); +} + +static int udc_mcux_ep_enable(const struct device *dev, + struct udc_ep_config *const cfg) +{ + usb_device_endpoint_init_struct_t ep_init; + + LOG_DBG("Enable ep 0x%02x", cfg->addr); + + ep_init.zlt = 0U; + ep_init.interval = cfg->interval; + ep_init.endpointAddress = cfg->addr; + ep_init.maxPacketSize = cfg->mps; + + switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_CONTROL: + ep_init.transferType = USB_ENDPOINT_CONTROL; + break; + case USB_EP_TYPE_BULK: + ep_init.transferType = USB_ENDPOINT_BULK; + break; + case USB_EP_TYPE_INTERRUPT: + ep_init.transferType = USB_ENDPOINT_INTERRUPT; + break; + case USB_EP_TYPE_ISO: + ep_init.transferType = USB_ENDPOINT_ISOCHRONOUS; + break; + default: + return -EINVAL; + } + + return udc_mcux_control(dev, kUSB_DeviceControlEndpointInit, &ep_init); +} + +static int udc_mcux_ep_disable(const struct device *dev, + struct udc_ep_config *const cfg) +{ + LOG_DBG("Disable ep 0x%02x", cfg->addr); + + return udc_mcux_control(dev, kUSB_DeviceControlEndpointDeinit, &cfg->addr); +} + +static int udc_mcux_host_wakeup(const struct device *dev) +{ + return -ENOTSUP; +} + +static int udc_mcux_set_address(const struct device *dev, const uint8_t addr) +{ + uint8_t temp_addr = addr; + + return udc_mcux_control(dev, kUSB_DeviceControlSetDeviceAddress, &temp_addr); +} + +static int udc_mcux_enable(const struct device *dev) +{ + return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL); +} + +static int udc_mcux_disable(const struct device *dev) +{ + return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL); +} + +static int udc_mcux_init(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED + if (config->phy_config != NULL) { + USB_EhciPhyInit(priv->controller_id, 0u, + (usb_phy_config_struct_t *)&config->phy_config); + } +#endif + + /* Init MCUX USB device driver. */ + status = mcux_if->deviceInit(priv->controller_id, + &priv->mcux_device, &(priv->mcux_device.controllerHandle)); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + /* enable USB interrupt */ + config->irq_enable_func(dev); + + LOG_DBG("Initialized USB controller %x", (uint32_t)config->base); + + return 0; +} + +static int udc_mcux_shutdown(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + /* Disable interrupt */ + config->irq_disable_func(dev); + + /* De-init MCUX USB device driver. */ + status = mcux_if->deviceDeinit(priv->mcux_device.controllerHandle); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +static int udc_mcux_lock(const struct device *dev) +{ + return udc_lock_internal(dev, K_FOREVER); +} + +static int udc_mcux_unlock(const struct device *dev) +{ + return udc_unlock_internal(dev); +} + +static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv, + const struct udc_mcux_config *config) +{ + /* + * MCUX USB controller drivers use an ID to tell the HAL drivers + * which controller is being used. This part of the code converts + * the base address to the ID value. + */ +#ifdef USB_BASE_ADDRS + uintptr_t ip3511_fs_base[] = USB_BASE_ADDRS; +#endif +#ifdef USBHSD_BASE_ADDRS + uintptr_t ip3511_hs_base[] = USBHSD_BASE_ADDRS; +#endif + + /* get the right controller id */ + priv->controller_id = 0xFFu; /* invalid value */ +#ifdef USB_BASE_ADDRS + for (uint8_t i = 0; i < ARRAY_SIZE(ip3511_fs_base); i++) { + if (ip3511_fs_base[i] == config->base) { + priv->controller_id = kUSB_ControllerLpcIp3511Fs0 + i; + break; + } + } +#endif + +#ifdef USBHSD_BASE_ADDRS + if (priv->controller_id == 0xFF) { + for (uint8_t i = 0; i < ARRAY_SIZE(ip3511_hs_base); i++) { + if (ip3511_hs_base[i] == config->base) { + priv->controller_id = kUSB_ControllerLpcIp3511Hs0 + i; + break; + } + } + } +#endif +} + +static int udc_mcux_driver_preinit(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + struct udc_data *data = dev->data; + struct udc_mcux_data *priv = data->priv; + int err; + + udc_mcux_get_hal_driver_id(priv, config); + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + + k_mutex_init(&data->mutex); + + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_out[i].caps.out = 1; + if (i == 0) { + config->ep_cfg_out[i].caps.control = 1; + config->ep_cfg_out[i].caps.mps = 64; + } else { + config->ep_cfg_out[i].caps.bulk = 1; + config->ep_cfg_out[i].caps.interrupt = 1; + config->ep_cfg_out[i].caps.iso = 1; + config->ep_cfg_out[i].caps.mps = 1024; + } + + config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; + err = udc_register_ep(dev, &config->ep_cfg_out[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_in[i].caps.in = 1; + if (i == 0) { + config->ep_cfg_in[i].caps.control = 1; + config->ep_cfg_in[i].caps.mps = 64; + } else { + config->ep_cfg_in[i].caps.bulk = 1; + config->ep_cfg_in[i].caps.interrupt = 1; + config->ep_cfg_in[i].caps.iso = 1; + config->ep_cfg_in[i].caps.mps = 1024; + } + + config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i; + err = udc_register_ep(dev, &config->ep_cfg_in[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + /* Requires udc_mcux_host_wakeup() implementation */ + data->caps.rwup = false; + data->caps.mps0 = USB_MCUX_MPS0; + if ((priv->controller_id == kUSB_ControllerLpcIp3511Hs0) || + (priv->controller_id == kUSB_ControllerLpcIp3511Hs1)) { + data->caps.hs = true; + } + priv->dev = dev; + + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + + return 0; +} + +static const struct udc_api udc_mcux_api = { + .device_speed = udc_mcux_device_speed, + .ep_enqueue = udc_mcux_ep_enqueue, + .ep_dequeue = udc_mcux_ep_dequeue, + .ep_set_halt = udc_mcux_ep_set_halt, + .ep_clear_halt = udc_mcux_ep_clear_halt, + .ep_try_config = NULL, + .ep_enable = udc_mcux_ep_enable, + .ep_disable = udc_mcux_ep_disable, + .host_wakeup = udc_mcux_host_wakeup, + .set_address = udc_mcux_set_address, + .enable = udc_mcux_enable, + .disable = udc_mcux_disable, + .init = udc_mcux_init, + .shutdown = udc_mcux_shutdown, + .lock = udc_mcux_lock, + .unlock = udc_mcux_unlock, +}; + +/* IP3511 device driver interface */ +static const usb_device_controller_interface_struct_t udc_mcux_if = { + USB_DeviceLpc3511IpInit, USB_DeviceLpc3511IpDeinit, USB_DeviceLpc3511IpSend, + USB_DeviceLpc3511IpRecv, USB_DeviceLpc3511IpCancel, USB_DeviceLpc3511IpControl +}; + +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED +#define UDC_MCUX_PHY_DEFINE(n) \ +static usb_phy_config_struct_t phy_config_##n = { \ + .D_CAL = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_d_cal, 0), \ + .TXCAL45DP = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dp_ohms, 0), \ + .TXCAL45DM = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dm_ohms, 0), \ +} + +#define UDC_MCUX_PHY_DEFINE_OR(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + (UDC_MCUX_PHY_DEFINE(n)), ()) + +#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) \ + .phy_config = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + (&phy_config_##n), (NULL)) +#else +#define UDC_MCUX_PHY_DEFINE_OR(n) +#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) +#endif + +#define USB_MCUX_IP3511_DEVICE_DEFINE(n) \ + UDC_MCUX_PHY_DEFINE_OR(n); \ + \ + static void udc_irq_enable_func##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + udc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void udc_irq_disable_func##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static struct udc_ep_config \ + ep_cfg_out##n[DT_INST_PROP(n, num_bidir_endpoints)]; \ + static struct udc_ep_config \ + ep_cfg_in##n[DT_INST_PROP(n, num_bidir_endpoints)]; \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct udc_mcux_config priv_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_enable_func = udc_irq_enable_func##n, \ + .irq_disable_func = udc_irq_disable_func##n, \ + .num_of_eps = DT_INST_PROP(n, num_bidir_endpoints), \ + .ep_cfg_in = ep_cfg_in##n, \ + .ep_cfg_out = ep_cfg_out##n, \ + .mcux_if = &udc_mcux_if, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ + }; \ + \ + static struct udc_mcux_data priv_data_##n = { \ + }; \ + \ + static struct udc_data udc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \ + &udc_data_##n, &priv_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &udc_mcux_api); + +DT_INST_FOREACH_STATUS_OKAY(USB_MCUX_IP3511_DEVICE_DEFINE) diff --git a/modules/hal_nxp/CMakeLists.txt b/modules/hal_nxp/CMakeLists.txt index 2f2e53d22ea..f02499cbc43 100644 --- a/modules/hal_nxp/CMakeLists.txt +++ b/modules/hal_nxp/CMakeLists.txt @@ -1,5 +1,6 @@ # # Copyright (c) 2021 Linaro, Limited +# Copyright 2024 NXP # # SPDX-License-Identifier: Apache-2.0 # @@ -7,6 +8,7 @@ if(CONFIG_HAS_MCUX OR CONFIG_HAS_IMX_HAL OR CONFIG_HAS_NXP_S32_HAL) add_subdirectory(${ZEPHYR_CURRENT_MODULE_DIR} hal_nxp) add_subdirectory_ifdef(CONFIG_USB_DEVICE_DRIVER usb) + add_subdirectory_ifdef(CONFIG_UDC_DRIVER usb) zephyr_sources_ifdef(CONFIG_PWM_MCUX_CTIMER ${ZEPHYR_CURRENT_MODULE_DIR}/mcux/mcux-sdk/drivers/ctimer/fsl_ctimer.c) zephyr_include_directories_ifdef(CONFIG_PWM_MCUX_CTIMER @@ -22,5 +24,6 @@ if(CONFIG_HAS_MCUX OR CONFIG_HAS_IMX_HAL OR CONFIG_HAS_NXP_S32_HAL) if(CONFIG_NOCACHE_MEMORY) zephyr_compile_definitions_ifdef(CONFIG_USB_DEVICE_DRIVER DATA_SECTION_IS_CACHEABLE=1) + zephyr_compile_definitions_ifdef(CONFIG_UDC_DRIVER DATA_SECTION_IS_CACHEABLE=1) endif() endif() diff --git a/modules/hal_nxp/usb/CMakeLists.txt b/modules/hal_nxp/usb/CMakeLists.txt index 380aa3aef87..da427e3601b 100644 --- a/modules/hal_nxp/usb/CMakeLists.txt +++ b/modules/hal_nxp/usb/CMakeLists.txt @@ -1,6 +1,9 @@ # -# Copyright (c) 2021, NXP +# Copyright (c) 2021,2024 NXP # # SPDX-License-Identifier: Apache-2.0 # zephyr_include_directories(.) +if(CONFIG_DT_HAS_NXP_USBPHY_ENABLED) +zephyr_include_directories(${ZEPHYR_HAL_NXP_MODULE_DIR}/mcux/middleware/mcux-sdk-middleware-usb/phy) +endif() diff --git a/modules/hal_nxp/usb/usb_device_config.h b/modules/hal_nxp/usb/usb_device_config.h index 31a62ffbfeb..a97d9181c1b 100644 --- a/modules/hal_nxp/usb/usb_device_config.h +++ b/modules/hal_nxp/usb/usb_device_config.h @@ -13,6 +13,7 @@ /****************************************************************************** * Definitions *****************************************************************************/ +#ifdef CONFIG_USB_DEVICE_DRIVER /* EHCI instance count */ #ifdef CONFIG_USB_DC_NXP_EHCI #define USB_DEVICE_CONFIG_EHCI (1U) @@ -51,5 +52,57 @@ BUILD_ASSERT(NUM_INSTS <= 1, "Only one USB device supported"); /* Number of endpoints supported */ #define USB_DEVICE_CONFIG_ENDPOINTS (DT_INST_PROP(0, num_bidir_endpoints)) +#else + +#ifdef CONFIG_UDC_NXP_EHCI +#define USB_DEVICE_CONFIG_EHCI (DT_NUM_INST_STATUS_OKAY(nxp_ehci)) +#endif + +#ifdef CONFIG_UDC_NXP_IP3511 + +#if defined(USBHSD_BASE_ADDRS) && defined(USB_BASE_ADDRS) +#define USB_DEVICE_CONFIG_LPCIP3511HS (1U) +#define USB_DEVICE_CONFIG_LPCIP3511FS (1U) + +#else + +#ifdef USBHSD_BASE_ADDRS +#define USB_DEVICE_CONFIG_LPCIP3511HS (DT_NUM_INST_STATUS_OKAY(nxp_lpcip3511)) +#else +#define USB_DEVICE_CONFIG_LPCIP3511HS (0U) +#endif + +#ifdef USB_BASE_ADDRS +#define USB_DEVICE_CONFIG_LPCIP3511FS (DT_NUM_INST_STATUS_OKAY(nxp_lpcip3511)) +#else +#define USB_DEVICE_CONFIG_LPCIP3511FS (0U) +#endif + +#endif +#endif + +/* calculte the num of endponts. + * mcux ip3511 driver doesn't use USB_DEVICE_CONFIG_ENDPOINTS, + * so use ehci endpoint number if ehci is enabled. + */ +#if DT_HAS_COMPAT_STATUS_OKAY(nxp_ehci) +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_ehci +#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_lpcip3511) +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_lpcip3511 +#endif + +/* Number of endpoints supported */ +#define USB_DEVICE_CONFIG_ENDPOINTS (DT_INST_PROP(0, num_bidir_endpoints)) + +#define USB_DEVICE_CONFIG_SELF_POWER (1U) + +#if ((defined(USB_DEVICE_CONFIG_EHCI)) && (USB_DEVICE_CONFIG_EHCI > 0U)) +/*! @brief How many the DTD are supported. */ +#define USB_DEVICE_CONFIG_EHCI_MAX_DTD (16U) +#endif + +#endif #endif /* __USB_DEVICE_CONFIG_H__ */ From 90f3218df218b7399dd99ebe9ecec5f0b3a5a72e Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 6 Jun 2024 00:17:18 +0200 Subject: [PATCH 03/39] [nrf fromtree] doc: usb: rework new USB device support overview description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework new USB device support overview description. Signed-off-by: Johann Fischer (cherry picked from commit 9e80d4f9761489702ad68d105de8fde15e522a49) Signed-off-by: Tomasz Moń --- .../usb/device_next/usb_device.rst | 99 +++++++++++-------- 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/doc/connectivity/usb/device_next/usb_device.rst b/doc/connectivity/usb/device_next/usb_device.rst index a9009d54e2b..a21da33fdf8 100644 --- a/doc/connectivity/usb/device_next/usb_device.rst +++ b/doc/connectivity/usb/device_next/usb_device.rst @@ -1,57 +1,78 @@ .. _usb_device_stack_next: -New experimental USB device support -################################### +New USB device support +###################### Overview ******** -The new USB device support is experimental. It consists of :ref:`udc_api` -and :ref:`usbd_api`. The new device stack brings support for multiple device -controllers, support for multiple configurations, and dynamic registration of -class instances to a configuration at runtime. The stack also provides a specific -class API that should be used to implement the functions (classes). -It will replace :ref:`usb_device_stack`. +USB device support consists of the USB device controller (UDC) drivers +, :ref:`udc_api`, and USB device stack, :ref:`usbd_api`. +The :ref:`udc_api` provides a generic and vendor independent interface to USB +device controllers, and although, there a is clear separation between these +layers, the purpose of :ref:`udc_api` is to serve new Zephyr's USB device stack +exclusively. -If you would like to play around with the new device support, or the new USB -support in general, please try :zephyr:code-sample:`usb-shell` sample. The sample is mainly to help -test the capabilities of the stack and correct implementation of the USB controller -drivers. +The new device stack supports multiple device controllers, meaning that if a +SoC has multiple controllers, they can be used simultaneously. Full and +high-speed device controllers are supported. It also provides support for +registering multiple function or class instances to a configuration at runtime, +or changing the configuration later. It has built-in support for several USB +classes and provides an API to implement custom USB functions. -Supported USB classes -********************* +The new USB device support is considered experimental and will replace +:ref:`usb_device_stack`. -Bluetooth HCI USB transport layer -================================= +Built-in functions +================== + +The USB device stack has built-in USB functions. Some can be used directly in +the user application through a special API, such as HID or Audio class devices, +while others use a general Zephyr RTOS driver API, such as MSC and CDC class +implementations. The *Identification string* identifies a class or function +instance (n) and is used as an argument to the :c:func:`usbd_register_class`. -See :ref:`bluetooth-hci-usb-sample` sample for reference. -To build the sample for the new device support, set the configuration -``-DCONF_FILE=usbd_next_prj.conf`` either directly or via ``west``. ++-----------------------------------+-------------------------+-------------------------+ +| Class or function | User API (if any) | Identification string | ++===================================+=========================+=========================+ +| USB Audio 2 class | :ref:`uac2_device` | uac2_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| USB CDC ACM class | :ref:`uart_api` | cdc_acm_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| USB CDC ECM class | Ethernet device | cdc_ecm_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| USB Mass Storage Class (MSC) | :ref:`disk_access_api` | msc_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| USB Human Interface Devices (HID) | :ref:`usbd_hid_device` | hid_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| Bluetooth HCI USB transport layer | :ref:`bt_hci_raw` | bt_hci_(n) | ++-----------------------------------+-------------------------+-------------------------+ -CDC ACM +Samples ======= -CDC ACM implementation has support for multiple instances. -Description from :ref:`usb_device_cdc_acm` also applies to the new implementation. -See :zephyr:code-sample:`usb-cdc-acm` sample for reference. -To build the sample for the new device support, set the configuration -``-DCONF_FILE=usbd_next_prj.conf`` either directly or via ``west``. +* :zephyr:code-sample:`usb-hid-keyboard` -Mass Storage Class -================== +* :zephyr:code-sample:`uac2-explicit-feedback` + +Samples ported to new USB device support +---------------------------------------- + +To build a sample that supports both the old and new USB device stack, set the +configuration ``-DCONF_FILE=usbd_next_prj.conf`` either directly or via +``west``. + +* :ref:`bluetooth-hci-usb-sample` + +* :zephyr:code-sample:`usb-cdc-acm` -See :zephyr:code-sample:`usb-mass` sample for reference. -To build the sample for the new device support, set the configuration -``-DCONF_FILE=usbd_next_prj.conf`` either directly or via ``west``. +* :zephyr:code-sample:`usb-cdc-acm-console` -Networking -========== +* :zephyr:code-sample:`usb-mass` -At the moment only CDC ECM class is implemented and has support for multiple instances. -It provides a virtual Ethernet connection between the remote (USB host) and -Zephyr network support. +* :zephyr:code-sample:`usb-hid-mouse` -See :zephyr:code-sample:`zperf` for reference. -To build the sample for the new device support, set the configuration overlay file -``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file -``-DDTC_OVERLAY_FILE="usbd_next_ecm.overlay`` either directly or via ``west``. +* :zephyr:code-sample:`zperf` To build the sample for the new device support, + set the configuration overlay file + ``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file + ``-DDTC_OVERLAY_FILE="usbd_next_ecm.overlay`` either directly or via ``west``. From 21e4a0a56ac1d4ad8e3ed36f75f37730ff2cf314 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Fri, 7 Jun 2024 14:48:19 +0200 Subject: [PATCH 04/39] [nrf fromtree] usb: device_next: add helper to register all available class instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helper to register/unregister all available class instances. Signed-off-by: Johann Fischer (cherry picked from commit 4191602f117d2c6f9de9d10615d6ba3c98dcac4c) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usbd.h | 38 ++++++++++++- subsys/usb/device_next/usbd_class.c | 72 +++++++++++++++++++++++++ tests/subsys/usb/device_next/src/main.c | 12 +++++ 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 7afc27656ee..7a643b15db0 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -660,7 +660,7 @@ int usbd_add_configuration(struct usbd_context *uds_ctx, * @param[in] uds_ctx Pointer to USB device support context * @param[in] name Class instance name * @param[in] speed Configuration speed - * @param[in] cfg Configuration value (similar to bConfigurationValue) + * @param[in] cfg Configuration value (bConfigurationValue) * * @return 0 on success, other values on fail. */ @@ -668,6 +668,25 @@ int usbd_register_class(struct usbd_context *uds_ctx, const char *name, const enum usbd_speed speed, uint8_t cfg); +/** + * @brief Register all available USB class instances + * + * Register all available instances. Like usbd_register_class, but does not + * take the instance name and instead registers all available instances. + * + * @note This cannot be combined. If your application calls + * usbd_register_class for any device, configuration number, or instance, + * either usbd_register_class or this function will fail. + * + * @param[in] uds_ctx Pointer to USB device support context + * @param[in] speed Configuration speed + * @param[in] cfg Configuration value (bConfigurationValue) + * + * @return 0 on success, other values on fail. + */ +int usbd_register_all_classes(struct usbd_context *uds_ctx, + const enum usbd_speed speed, uint8_t cfg); + /** * @brief Unregister an USB class instance * @@ -678,7 +697,7 @@ int usbd_register_class(struct usbd_context *uds_ctx, * @param[in] uds_ctx Pointer to USB device support context * @param[in] name Class instance name * @param[in] speed Configuration speed - * @param[in] cfg Configuration value (similar to bConfigurationValue) + * @param[in] cfg Configuration value (bConfigurationValue) * * @return 0 on success, other values on fail. */ @@ -686,6 +705,21 @@ int usbd_unregister_class(struct usbd_context *uds_ctx, const char *name, const enum usbd_speed speed, uint8_t cfg); +/** + * @brief Unregister all available USB class instances + * + * Unregister all available instances. Like usbd_unregister_class, but does not + * take the instance name and instead unregisters all available instances. + * + * @param[in] uds_ctx Pointer to USB device support context + * @param[in] speed Configuration speed + * @param[in] cfg Configuration value (bConfigurationValue) + * + * @return 0 on success, other values on fail. + */ +int usbd_unregister_all_classes(struct usbd_context *uds_ctx, + const enum usbd_speed speed, uint8_t cfg); + /** * @brief Register USB notification message callback * diff --git a/subsys/usb/device_next/usbd_class.c b/subsys/usb/device_next/usbd_class.c index 265bffbe947..465480bd0e3 100644 --- a/subsys/usb/device_next/usbd_class.c +++ b/subsys/usb/device_next/usbd_class.c @@ -340,6 +340,42 @@ int usbd_register_class(struct usbd_context *const uds_ctx, return ret; } +int usbd_register_all_classes(struct usbd_context *const uds_ctx, + const enum usbd_speed speed, const uint8_t cfg) +{ + int ret; + + if (speed == USBD_SPEED_HS) { + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) { + ret = usbd_register_class(uds_ctx, c_nd->c_data->name, + speed, cfg); + if (ret) { + LOG_ERR("Failed to register %s to HS configuration %u", + c_nd->c_data->name, cfg); + return ret; + } + } + + return 0; + } + + if (speed == USBD_SPEED_FS) { + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) { + ret = usbd_register_class(uds_ctx, c_nd->c_data->name, + speed, cfg); + if (ret) { + LOG_ERR("Failed to register %s to FS configuration %u", + c_nd->c_data->name, cfg); + return ret; + } + } + + return 0; + } + + return -ENOTSUP; +} + int usbd_unregister_class(struct usbd_context *const uds_ctx, const char *name, const enum usbd_speed speed, const uint8_t cfg) @@ -407,3 +443,39 @@ int usbd_unregister_class(struct usbd_context *const uds_ctx, usbd_device_unlock(uds_ctx); return ret; } + +int usbd_unregister_all_classes(struct usbd_context *const uds_ctx, + const enum usbd_speed speed, const uint8_t cfg) +{ + int ret; + + if (speed == USBD_SPEED_HS) { + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) { + ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name, + speed, cfg); + if (ret) { + LOG_ERR("Failed to unregister %s to HS configuration %u", + c_nd->c_data->name, cfg); + return ret; + } + } + + return 0; + } + + if (speed == USBD_SPEED_FS) { + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) { + ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name, + speed, cfg); + if (ret) { + LOG_ERR("Failed to unregister %s to FS configuration %u", + c_nd->c_data->name, cfg); + return ret; + } + } + + return 0; + } + + return -ENOTSUP; +} diff --git a/tests/subsys/usb/device_next/src/main.c b/tests/subsys/usb/device_next/src/main.c index 5d6a9da7039..f33b054fcd1 100644 --- a/tests/subsys/usb/device_next/src/main.c +++ b/tests/subsys/usb/device_next/src/main.c @@ -134,10 +134,22 @@ static void *usb_test_enable(void) zassert_equal(err, 0, "Failed to add configuration (%d)"); if (usbd_caps_speed(&test_usbd) == USBD_SPEED_HS) { + err = usbd_register_all_classes(&test_usbd, USBD_SPEED_HS, 1); + zassert_equal(err, 0, "Failed to unregister all instances(%d)"); + + err = usbd_unregister_all_classes(&test_usbd, USBD_SPEED_HS, 1); + zassert_equal(err, 0, "Failed to unregister all instances(%d)"); + err = usbd_register_class(&test_usbd, "loopback_0", USBD_SPEED_HS, 1); zassert_equal(err, 0, "Failed to register loopback_0 class (%d)"); } + err = usbd_register_all_classes(&test_usbd, USBD_SPEED_FS, 1); + zassert_equal(err, 0, "Failed to unregister all instances(%d)"); + + err = usbd_unregister_all_classes(&test_usbd, USBD_SPEED_FS, 1); + zassert_equal(err, 0, "Failed to unregister all instances(%d)"); + err = usbd_register_class(&test_usbd, "loopback_0", USBD_SPEED_FS, 1); zassert_equal(err, 0, "Failed to register loopback_0 class (%d)"); From 6eb881cd9f667d2b25afa5d35c2b8d8db5aa3aa6 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Fri, 7 Jun 2024 15:29:03 +0200 Subject: [PATCH 05/39] [nrf fromtree] samples: usb: rework common code to use usbd_register_all_classes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework common USB samples code to use usbd_register_all_classes(). Signed-off-by: Johann Fischer (cherry picked from commit cf2113e43788ce5e5f55fcf8f8f742a65b35d477) Signed-off-by: Tomasz Moń --- samples/subsys/usb/common/sample_usbd_init.c | 90 +++++--------------- 1 file changed, 22 insertions(+), 68 deletions(-) diff --git a/samples/subsys/usb/common/sample_usbd_init.c b/samples/subsys/usb/common/sample_usbd_init.c index 14fd43993e6..2c559aa789a 100644 --- a/samples/subsys/usb/common/sample_usbd_init.c +++ b/samples/subsys/usb/common/sample_usbd_init.c @@ -9,7 +9,6 @@ #include #include #include -#include #include LOG_MODULE_REGISTER(usbd_sample_config); @@ -51,68 +50,9 @@ static const struct usb_bos_capability_lpm bos_cap_lpm = { USBD_DESC_BOS_DEFINE(sample_usbext, sizeof(bos_cap_lpm), &bos_cap_lpm); -static int register_fs_classes(struct usbd_context *uds_ctx) +static void sample_fix_code_triple(struct usbd_context *uds_ctx, + const enum usbd_speed speed) { - int err = 0; - - STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) { - /* Pull everything that is enabled in our configuration. */ - err = usbd_register_class(uds_ctx, c_nd->c_data->name, - USBD_SPEED_FS, 1); - if (err) { - LOG_ERR("Failed to register FS %s (%d)", - c_nd->c_data->name, err); - return err; - } - - LOG_DBG("Register FS %s", c_nd->c_data->name); - } - - return err; -} - -static int register_hs_classes(struct usbd_context *uds_ctx) -{ - int err = 0; - - STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) { - /* Pull everything that is enabled in our configuration. */ - err = usbd_register_class(uds_ctx, c_nd->c_data->name, - USBD_SPEED_HS, 1); - if (err) { - LOG_ERR("Failed to register HS %s (%d)", - c_nd->c_data->name, err); - return err; - } - - LOG_DBG("Register HS %s", c_nd->c_data->name); - } - - return err; -} - -static int sample_add_configuration(struct usbd_context *uds_ctx, - const enum usbd_speed speed, - struct usbd_config_node *config) -{ - int err; - - err = usbd_add_configuration(uds_ctx, speed, config); - if (err) { - LOG_ERR("Failed to add configuration (%d)", err); - return err; - } - - if (speed == USBD_SPEED_FS) { - err = register_fs_classes(uds_ctx); - } else if (speed == USBD_SPEED_HS) { - err = register_hs_classes(uds_ctx); - } - - if (err) { - return err; - } - /* Always use class code information from Interface Descriptors */ if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) || IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) || @@ -127,8 +67,6 @@ static int sample_add_configuration(struct usbd_context *uds_ctx, } else { usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0); } - - return 0; } struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) @@ -160,21 +98,37 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) } if (usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) { - err = sample_add_configuration(&sample_usbd, USBD_SPEED_HS, - &sample_hs_config); + err = usbd_add_configuration(&sample_usbd, USBD_SPEED_HS, + &sample_hs_config); if (err) { LOG_ERR("Failed to add High-Speed configuration"); return NULL; } + + err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_HS, 1); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + sample_fix_code_triple(&sample_usbd, USBD_SPEED_HS); } - err = sample_add_configuration(&sample_usbd, USBD_SPEED_FS, - &sample_fs_config); + err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS, + &sample_fs_config); if (err) { LOG_ERR("Failed to add Full-Speed configuration"); return NULL; } + err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS); + if (msg_cb != NULL) { err = usbd_msg_register_cb(&sample_usbd, msg_cb); if (err) { From 601baebca024d18b0600f2ddf88c807b1f472603 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 6 Jun 2024 18:33:12 +0200 Subject: [PATCH 06/39] [nrf fromtree] doc: usb: add initial USB device configuraiton howto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add initial documentation how to configure and enable new USB device support. Use literalinclude to pull code snippets from the samples. Signed-off-by: Johann Fischer (cherry picked from commit 8739efe0fc603dc8e413c8006f1ecb6044330351) Signed-off-by: Tomasz Moń --- .../usb/device_next/usb_device.rst | 109 ++++++++++++++++++ samples/subsys/usb/common/sample_usbd_init.c | 23 ++++ samples/subsys/usb/hid-keyboard/src/main.c | 2 + 3 files changed, 134 insertions(+) diff --git a/doc/connectivity/usb/device_next/usb_device.rst b/doc/connectivity/usb/device_next/usb_device.rst index a21da33fdf8..852da003cef 100644 --- a/doc/connectivity/usb/device_next/usb_device.rst +++ b/doc/connectivity/usb/device_next/usb_device.rst @@ -76,3 +76,112 @@ configuration ``-DCONF_FILE=usbd_next_prj.conf`` either directly or via set the configuration overlay file ``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file ``-DDTC_OVERLAY_FILE="usbd_next_ecm.overlay`` either directly or via ``west``. + +How to configure and enable USB device support +********************************************** + +For the USB device support samples in the Zephyr project repository, we have a +common file for instantiation, configuration and initialization, +:zephyr_file:`samples/subsys/usb/common/sample_usbd_init.c`. The following code +snippets from this file are used as examples. USB Samples Kconfig options used +in the USB samples and prefixed with ``SAMPLE_USBD_`` have default values +specific to the Zephyr project and the scope is limited to the project samples. +In the examples below, you will need to replace these Kconfig options and other +defaults with values appropriate for your application or hardware. + +The USB device stack requires a context structure to manage its properties and +runtime data. The preferred way to define a device context is to use the +:c:macro:`USBD_DEVICE_DEFINE` macro. This creates a static +:c:struct:`usbd_context` variable with a given name. Any number of contexts may +be instantiated. A USB controller device can be assigned to multiple contexts, +but only one context can be initialized and used at a time. Context properties +must not be directly accessed or manipulated by the application. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc device instantiation start + :end-before: doc device instantiation end + +Your USB device may have manufacturer, product, and serial number string +descriptors. To instantiate these string descriptors, the application should +use the appropriate :c:macro:`USBD_DESC_MANUFACTURER_DEFINE`, +:c:macro:`USBD_DESC_PRODUCT_DEFINE`, and +:c:macro:`USBD_DESC_SERIAL_NUMBER_DEFINE` macros. String descriptors also +require a single instantiation of the language descriptor using the +:c:macro:`USBD_DESC_LANG_DEFINE` macro. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc string instantiation start + :end-before: doc string instantiation end + +String descriptors must be added to the device context at runtime before +initializing the USB device with :c:func:`usbd_add_descriptor`. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc add string descriptor start + :end-before: doc add string descriptor end + +USB device requires at least one configuration instance per supported speed. +The application should use :c:macro:`USBD_CONFIGURATION_DEFINE` to instantiate +a configuration. Later, USB device functions are assigned to a configuration. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc configuration instantiation start + :end-before: doc configuration instantiation end + +Each configuration instance for a specific speed must be added to the device +context at runtime before the USB device is initialized using +:c:func:`usbd_add_configuration`. Note :c:enumerator:`USBD_SPEED_FS` and +:c:enumerator:`USBD_SPEED_HS`. The first full-speed or high-speed +configuration will get ``bConfigurationValue`` one, and then further upward. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc configuration register start + :end-before: doc configuration register end + + +Although we have already done a lot, this USB device has no function. A device +can have multiple configurations with different set of functions at different +speeds. A function or class can be registered on a USB device before +it is initialized using :c:func:`usbd_register_class`. The desired +configuration is specified using :c:enumerator:`USBD_SPEED_FS` or +:c:enumerator:`USBD_SPEED_HS` and the configuration number. For simple cases, +:c:func:`usbd_register_all_classes` can be used to register all available +instances. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc functions register start + :end-before: doc functions register end + +The last step in the preparation is to initialize the device with +:c:func:`usbd_init`. After this, the configuration of the device cannot be +changed. A device can be deinitialized with :c:func:`usbd_shutdown` and all +instances can be reused, but the previous steps must be repeated. So it is +possible to shutdown a device, register another type of configuration or +function, and initialize it again. At the USB controller level, +:c:func:`usbd_init` does only what is necessary to detect VBUS changes. There +are controller types where the next step is only possible if a VBUS signal is +present. + +A function or class implementation may require its own specific configuration +steps, which should be performed prior to initializing the USB device. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc device init start + :end-before: doc device init end + +The final step to enable the USB device is :c:func:`usbd_enable`, after that, +if the USB device is connected to a USB host controller, the host can start +enumerating the device. The application can disable the USB device using +:c:func:`usbd_disable`. + +.. literalinclude:: ../../../../samples/subsys/usb/hid-keyboard/src/main.c + :language: c + :start-after: doc device enable start + :end-before: doc device enable end diff --git a/samples/subsys/usb/common/sample_usbd_init.c b/samples/subsys/usb/common/sample_usbd_init.c index 2c559aa789a..f6b2963b212 100644 --- a/samples/subsys/usb/common/sample_usbd_init.c +++ b/samples/subsys/usb/common/sample_usbd_init.c @@ -15,27 +15,40 @@ LOG_MODULE_REGISTER(usbd_sample_config); #define ZEPHYR_PROJECT_USB_VID 0x2fe3 +/* doc device instantiation start */ +/* + * Instantiate a context named sample_usbd using the default USB device + * controller, the Zephyr project vendor ID, and the sample product ID. + * Zephyr project vendor ID must not be used outside of Zephyr samples. + */ USBD_DEVICE_DEFINE(sample_usbd, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), ZEPHYR_PROJECT_USB_VID, CONFIG_SAMPLE_USBD_PID); +/* doc device instantiation end */ +/* doc string instantiation start */ USBD_DESC_LANG_DEFINE(sample_lang); USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, CONFIG_SAMPLE_USBD_MANUFACTURER); USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_SAMPLE_USBD_PRODUCT); USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn); +/* doc string instantiation end */ +/* doc configuration instantiation start */ static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ? USB_SCD_SELF_POWERED : 0) | (IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ? USB_SCD_REMOTE_WAKEUP : 0); +/* Full speed configuration */ USBD_CONFIGURATION_DEFINE(sample_fs_config, attributes, CONFIG_SAMPLE_USBD_MAX_POWER); +/* High speed configuration */ USBD_CONFIGURATION_DEFINE(sample_hs_config, attributes, CONFIG_SAMPLE_USBD_MAX_POWER); +/* doc configuration instantiation end */ /* * This does not yet provide valuable information, but rather serves as an @@ -73,6 +86,7 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) { int err; + /* doc add string descriptor start */ err = usbd_add_descriptor(&sample_usbd, &sample_lang); if (err) { LOG_ERR("Failed to initialize language descriptor (%d)", err); @@ -96,6 +110,7 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) LOG_ERR("Failed to initialize SN descriptor (%d)", err); return NULL; } + /* doc add string descriptor end */ if (usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) { err = usbd_add_configuration(&sample_usbd, USBD_SPEED_HS, @@ -114,21 +129,26 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) sample_fix_code_triple(&sample_usbd, USBD_SPEED_HS); } + /* doc configuration register start */ err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS, &sample_fs_config); if (err) { LOG_ERR("Failed to add Full-Speed configuration"); return NULL; } + /* doc configuration register end */ + /* doc functions register start */ err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1); if (err) { LOG_ERR("Failed to add register classes"); return NULL; } + /* doc functions register end */ sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS); + /* doc message callback register start */ if (msg_cb != NULL) { err = usbd_msg_register_cb(&sample_usbd, msg_cb); if (err) { @@ -136,6 +156,7 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) return NULL; } } + /* doc message callback register end */ if (IS_ENABLED(CONFIG_SAMPLE_USBD_20_EXTENSION_DESC)) { (void)usbd_device_set_bcd(&sample_usbd, USBD_SPEED_FS, 0x0201); @@ -148,11 +169,13 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) } } + /* doc device init start */ err = usbd_init(&sample_usbd); if (err) { LOG_ERR("Failed to initialize device support"); return NULL; } + /* doc device init end */ return &sample_usbd; } diff --git a/samples/subsys/usb/hid-keyboard/src/main.c b/samples/subsys/usb/hid-keyboard/src/main.c index 393edfcf06a..b56fe26c0c7 100644 --- a/samples/subsys/usb/hid-keyboard/src/main.c +++ b/samples/subsys/usb/hid-keyboard/src/main.c @@ -184,11 +184,13 @@ int main(void) return -ENODEV; } + /* doc device enable start */ ret = usbd_enable(sample_usbd); if (ret) { LOG_ERR("Failed to enable device support"); return ret; } + /* doc device enable end */ LOG_INF("HID keyboard sample is initialized"); From 938b59e2b8d9e317d6bcd7d1ebbffc8da440a753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 31 May 2024 14:50:41 +0200 Subject: [PATCH 07/39] [nrf fromtree] drivers: udc_dwc2: Add DMA register bit defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add register bit defines for registers needed for DMA operation. Signed-off-by: Tomasz Moń (cherry picked from commit 2c9371ea451c08a27c14ae6f180f1b66489ad573) Signed-off-by: Tomasz Moń --- drivers/usb/common/usb_dwc2_hw.h | 116 +++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/drivers/usb/common/usb_dwc2_hw.h b/drivers/usb/common/usb_dwc2_hw.h index b137a526ede..88d32a9137b 100644 --- a/drivers/usb/common/usb_dwc2_hw.h +++ b/drivers/usb/common/usb_dwc2_hw.h @@ -147,11 +147,45 @@ BUILD_ASSERT(sizeof(struct usb_dwc2_reg) == 0x0D00); /* AHB configuration register */ #define USB_DWC2_GAHBCFG 0x0008UL +#define USB_DWC2_GAHBCFG_LOA_EOP_WORD_POS 27UL +#define USB_DWC2_GAHBCFG_LOA_EOP_WORD_MASK (0x3UL << USB_DWC2_GAHBCFG_LOA_EOP_WORD_POS) +#define USB_DWC2_GAHBCFG_LOA_EOP_WORD_ONE 1 +#define USB_DWC2_GAHBCFG_LOA_EOP_WORD_TWO 2 +#define USB_DWC2_GAHBCFG_LOA_EOP_BYTE_POS 25UL +#define USB_DWC2_GAHBCFG_LOA_EOP_BYTE_MASK (0x3UL << USB_DWC2_GAHBCFG_LOA_EOP_BYTE_POS) +#define USB_DWC2_GAHBCFG_LOA_EOP_BYTE_ONE 1 +#define USB_DWC2_GAHBCFG_LOA_EOP_BYTE_TWO 2 +#define USB_DWC2_GAHBCFG_INVDESCENDIANESS_POS 24UL +#define USB_DWC2_GAHBCFG_INVDESCENDIANESS BIT(USB_DWC2_GAHBCFG_INVDESCENDIANESS_POS) +#define USB_DWC2_GAHBCFG_AHBSINGLE_POS 23UL +#define USB_DWC2_GAHBCFG_AHBSINGLE BIT(USB_DWC2_GAHBCFG_AHBSINGLE_POS) +#define USB_DWC2_GAHBCFG_NOTIALLDMAWRIT_POS 22UL +#define USB_DWC2_GAHBCFG_NOTIALLDMAWRIT BIT(USB_DWC2_GAHBCFG_NOTIALLDMAWRIT_POS) +#define USB_DWC2_GAHBCFG_REMMEMSUPP_POS 21UL +#define USB_DWC2_GAHBCFG_REMMEMSUPP BIT(USB_DWC2_GAHBCFG_REMMEMSUPP_POS) +#define USB_DWC2_GAHBCFG_PTXFEMPLVL_POS 8UL +#define USB_DWC2_GAHBCFG_PTXFEMPLVL BIT(USB_DWC2_GAHBCFG_PTXFEMPLVL_POS) +#define USB_DWC2_GAHBCFG_NPTXFEMPLVL_POS 7UL +#define USB_DWC2_GAHBCFG_NPTXFEMPLVL BIT(USB_DWC2_GAHBCFG_NPTXFEMPLVL_POS) #define USB_DWC2_GAHBCFG_DMAEN_POS 5UL #define USB_DWC2_GAHBCFG_DMAEN BIT(USB_DWC2_GAHBCFG_DMAEN_POS) +#define USB_DWC2_GAHBCFG_HBSTLEN_POS 1UL +#define USB_DWC2_GAHBCFG_HBSTLEN_MASK (0xFUL << USB_DWC2_GAHBCFG_HBSTLEN_POS) +#define USB_DWC2_GAHBCFG_HBSTLEN_SINGLE 0 +#define USB_DWC2_GAHBCFG_HBSTLEN_INCR 1 +#define USB_DWC2_GAHBCFG_HBSTLEN_INCR4 3 +#define USB_DWC2_GAHBCFG_HBSTLEN_INCR8 5 +#define USB_DWC2_GAHBCFG_HBSTLEN_INCR16 7 #define USB_DWC2_GAHBCFG_GLBINTRMASK_POS 0UL #define USB_DWC2_GAHBCFG_GLBINTRMASK BIT(USB_DWC2_GAHBCFG_GLBINTRMASK_POS) +USB_DWC2_SET_FIELD_DEFINE(gahbcfg_loa_eop_word, GAHBCFG_LOA_EOP_WORD) +USB_DWC2_SET_FIELD_DEFINE(gahbcfg_loa_eop_byte, GAHBCFG_LOA_EOP_BYTE) +USB_DWC2_SET_FIELD_DEFINE(gahbcfg_hbstlen, GAHBCFG_HBSTLEN) +USB_DWC2_GET_FIELD_DEFINE(gahbcfg_loa_eop_word, GAHBCFG_LOA_EOP_WORD) +USB_DWC2_GET_FIELD_DEFINE(gahbcfg_loa_eop_byte, GAHBCFG_LOA_EOP_BYTE) +USB_DWC2_GET_FIELD_DEFINE(gahbcfg_hbstlen, GAHBCFG_HBSTLEN) + /* USB configuration register */ #define USB_DWC2_GUSBCFG 0x000CUL #define USB_DWC2_GUSBCFG_FORCEDEVMODE_POS 30UL @@ -411,6 +445,18 @@ USB_DWC2_GET_FIELD_DEFINE(ghwcfg4_numctleps, GHWCFG4_NUMCTLEPS) USB_DWC2_GET_FIELD_DEFINE(ghwcfg4_phydatawidth, GHWCFG4_PHYDATAWIDTH) USB_DWC2_GET_FIELD_DEFINE(ghwcfg4_numdevperioeps, GHWCFG4_NUMDEVPERIOEPS) +/* GDFIFOCFG register */ +#define USB_DWC2_GDFIFOCFG 0x005CUL +#define USB_DWC2_GDFIFOCFG_EPINFOBASEADDR_POS 16UL +#define USB_DWC2_GDFIFOCFG_EPINFOBASEADDR_MASK (0xFFFFUL << USB_DWC2_GDFIFOCFG_EPINFOBASEADDR_POS) +#define USB_DWC2_GDFIFOCFG_GDFIFOCFG_POS 0UL +#define USB_DWC2_GDFIFOCFG_GDFIFOCFG_MASK (0xFFFFUL << USB_DWC2_GDFIFOCFG_GDFIFOCFG_POS) + +USB_DWC2_GET_FIELD_DEFINE(gdfifocfg_epinfobaseaddr, GDFIFOCFG_EPINFOBASEADDR) +USB_DWC2_GET_FIELD_DEFINE(gdfifocfg_gdfifocfg, GDFIFOCFG_GDFIFOCFG) +USB_DWC2_SET_FIELD_DEFINE(gdfifocfg_epinfobaseaddr, GDFIFOCFG_EPINFOBASEADDR) +USB_DWC2_SET_FIELD_DEFINE(gdfifocfg_gdfifocfg, GDFIFOCFG_GDFIFOCFG) + /* Device IN endpoint transmit FIFO size register */ #define USB_DWC2_DIEPTXF1 0x0104UL #define USB_DWC2_DIEPTXF_INEPNTXFDEP_POS 16UL @@ -425,8 +471,38 @@ USB_DWC2_SET_FIELD_DEFINE(dieptxf_inepntxfstaddr, DIEPTXF_INEPNTXFSTADDR) /* Device configuration registers */ #define USB_DWC2_DCFG 0x0800UL +#define USB_DWC2_DCFG_RESVALID_POS 26UL +#define USB_DWC2_DCFG_RESVALID_MASK (0x3FUL << USB_DWC2_DCFG_RESVALID_POS) +#define USB_DWC2_DCFG_PERSCHINTVL_POS 24UL +#define USB_DWC2_DCFG_PERSCHINTVL_MASK (0x3UL << USB_DWC2_DCFG_PERSCHINTVL_POS) +#define USB_DWC2_DCFG_PERSCHINTVL_MF25 0 +#define USB_DWC2_DCFG_PERSCHINTVL_MF50 1 +#define USB_DWC2_DCFG_PERSCHINTVL_MF75 2 +#define USB_DWC2_DCFG_PERSCHINTVL_RESERVED 3 +#define USB_DWC2_DCFG_DESCDMA_POS 23UL +#define USB_DWC2_DCFG_DESCDMA BIT(USB_DWC2_DCFG_DESCDMA_POS) +#define USB_DWC2_DCFG_EPMISCNT_POS 18UL +#define USB_DWC2_DCFG_EPMISCNT_MASK (0x1FUL << USB_DWC2_DCFG_EPMISCNT_POS) +#define USB_DWC2_DCFG_IPGISOCSUPT_POS 17UL +#define USB_DWC2_DCFG_IPGISOCSUPT BIT(USB_DWC2_DCFG_IPGISOCSUPT_POS) +#define USB_DWC2_DCFG_ERRATICINTMSK_POS 15UL +#define USB_DWC2_DCFG_ERRATICINTMSK BIT(USB_DWC2_DCFG_ERRATICINTMSK_POS) +#define USB_DWC2_DCFG_XCVRDLY_POS 14UL +#define USB_DWC2_DCFG_XCVRDLY BIT(USB_DWC2_DCFG_XCVRDLY_POS) +#define USB_DWC2_DCFG_ENDEVOUTNAK_POS 13UL +#define USB_DWC2_DCFG_ENDEVOUTNAK BIT(USB_DWC2_DCFG_ENDEVOUTNAK_POS) +#define USB_DWC2_DCFG_PERFRINT_POS 11UL +#define USB_DWC2_DCFG_PERFRINT_MASK (0x3UL << USB_DWC2_DCFG_PERFRINT_POS) +#define USB_DWC2_DCFG_PERFRINT_EOPF80 0 +#define USB_DWC2_DCFG_PERFRINT_EOPF85 1 +#define USB_DWC2_DCFG_PERFRINT_EOPF90 2 +#define USB_DWC2_DCFG_PERFRINT_EOPF95 3 #define USB_DWC2_DCFG_DEVADDR_POS 4UL #define USB_DWC2_DCFG_DEVADDR_MASK (0x7FUL << USB_DWC2_DCFG_DEVADDR_POS) +#define USB_DWC2_DCFG_ENA32KHZSUSP_POS 3UL +#define USB_DWC2_DCFG_ENA32KHZSUSP BIT(USB_DWC2_DCFG_ENA32KHZSUSP_POS) +#define USB_DWC2_DCFG_NZSTSOUTHSHK_POS 2UL +#define USB_DWC2_DCFG_NZSTSOUTHSHK BIT(USB_DWC2_DCFG_NZSTSOUTHSHK_POS) #define USB_DWC2_DCFG_DEVSPD_POS 0UL #define USB_DWC2_DCFG_DEVSPD_MASK (0x03UL << USB_DWC2_DCFG_DEVSPD_POS) #define USB_DWC2_DCFG_DEVSPD_USBHS20 0 @@ -434,7 +510,17 @@ USB_DWC2_SET_FIELD_DEFINE(dieptxf_inepntxfstaddr, DIEPTXF_INEPNTXFSTADDR) #define USB_DWC2_DCFG_DEVSPD_USBLS116 2 #define USB_DWC2_DCFG_DEVSPD_USBFS1148 3 +USB_DWC2_SET_FIELD_DEFINE(dcfg_resvalid, DCFG_RESVALID) +USB_DWC2_SET_FIELD_DEFINE(dcfg_perschintvl, DCFG_PERSCHINTVL) +USB_DWC2_SET_FIELD_DEFINE(dcfg_epmiscnt, DCFG_EPMISCNT) +USB_DWC2_SET_FIELD_DEFINE(dcfg_perfrint, DCFG_PERFRINT) USB_DWC2_SET_FIELD_DEFINE(dcfg_devaddr, DCFG_DEVADDR) +USB_DWC2_SET_FIELD_DEFINE(dcfg_devspd, DCFG_DEVSPD) +USB_DWC2_GET_FIELD_DEFINE(dcfg_resvalid, DCFG_RESVALID) +USB_DWC2_GET_FIELD_DEFINE(dcfg_perschintvl, DCFG_PERSCHINTVL) +USB_DWC2_GET_FIELD_DEFINE(dcfg_epmiscnt, DCFG_EPMISCNT) +USB_DWC2_GET_FIELD_DEFINE(dcfg_perfrint, DCFG_PERFRINT) +USB_DWC2_GET_FIELD_DEFINE(dcfg_devaddr, DCFG_DEVADDR) USB_DWC2_GET_FIELD_DEFINE(dcfg_devspd, DCFG_DEVSPD) /* Device control register */ @@ -494,6 +580,34 @@ USB_DWC2_GET_FIELD_DEFINE(dsts_enumspd, DSTS_ENUMSPD) #define USB_DWC2_DAINT_OUTEPINT(ep_num) BIT(16UL + ep_num) #define USB_DWC2_DAINT_INEPINT(ep_num) BIT(ep_num) +/* Device threshold control register */ +#define USB_DWC2_DTHRCTL 0x0830UL +#define USB_DWC2_DTHRCTL_ARBPRKEN_POS 27UL +#define USB_DWC2_DTHRCTL_ARBPRKEN BIT(USB_DWC2_DTHRCTL_ARBPRKEN_POS) +#define USB_DWC2_DTHRCTL_RXTHRLEN_POS 17UL +#define USB_DWC2_DTHRCTL_RXTHRLEN_MASK (0x1FFUL << USB_DWC2_DTHRCTL_RXTHRLEN_POS) +#define USB_DWC2_DTHRCTL_RXTHREN_POS 16UL +#define USB_DWC2_DTHRCTL_RXTHREN BIT(USB_DWC2_DTHRCTL_RXTHREN_POS) +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_POS 11UL +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_MASK (0x3UL << USB_DWC2_DTHRCTL_AHBTHRRATIO_POS) +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_THRESZERO 0 +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_THRESONE 1 +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_THRESTWO 2 +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_THRESTHREE 3 +#define USB_DWC2_DTHRCTL_TXTHRLEN_POS 2UL +#define USB_DWC2_DTHRCTL_TXTHRLEN_MASK (0x1FFUL << USB_DWC2_DTHRCTL_TXTHRLEN_POS) +#define USB_DWC2_DTHRCTL_ISOTHREN_POS 1UL +#define USB_DWC2_DTHRCTL_ISOTHREN BIT(USB_DWC2_DTHRCTL_ISOTHREN_POS) +#define USB_DWC2_DTHRCTL_NONISOTHREN_POS 0UL +#define USB_DWC2_DTHRCTL_NONISOTHREN BIT(USB_DWC2_DTHRCTL_NONISOTHREN_POS) + +USB_DWC2_GET_FIELD_DEFINE(dthrctl_rxthrlen, DTHRCTL_RXTHRLEN) +USB_DWC2_GET_FIELD_DEFINE(dthrctl_ahbthrratio, DTHRCTL_AHBTHRRATIO) +USB_DWC2_GET_FIELD_DEFINE(dthrctl_txthrlen, DTHRCTL_TXTHRLEN) +USB_DWC2_SET_FIELD_DEFINE(dthrctl_rxthrlen, DTHRCTL_RXTHRLEN) +USB_DWC2_SET_FIELD_DEFINE(dthrctl_ahbthrratio, DTHRCTL_AHBTHRRATIO) +USB_DWC2_SET_FIELD_DEFINE(dthrctl_txthrlen, DTHRCTL_TXTHRLEN) + /* * Device IN/OUT endpoint control register * IN endpoint offsets 0x0900 + (0x20 * n), n = 0 .. x, @@ -648,6 +762,8 @@ USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_xfersize, DEPTSIZ0_XFERSIZE) USB_DWC2_GET_FIELD_DEFINE(deptsizn_pktcnt, DEPTSIZN_PKTCNT) USB_DWC2_GET_FIELD_DEFINE(deptsizn_xfersize, DEPTSIZN_XFERSIZE) +USB_DWC2_SET_FIELD_DEFINE(deptsizn_pktcnt, DEPTSIZN_PKTCNT) +USB_DWC2_SET_FIELD_DEFINE(deptsizn_xfersize, DEPTSIZN_XFERSIZE) /* * Device IN/OUT endpoint transfer size register From 046b7b2cf6f2deed04d8180f9d924d2fc26146d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 3 Jun 2024 10:37:12 +0200 Subject: [PATCH 08/39] [nrf fromtree] drivers: udc_dwc2: Avoid unnecessary register accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not use sys_clear_bits() followed by sys_set_bits() on DCTL register to avoid writing to DCTL register twice - first with zeroed out address, and then with the new address. Change the code to write the address in one DCTL register write. Do not use sys_set_bits() to set test mode, but rather prepare the correct value first. Set DCFG and GUSBCFG registers in one go. There is no point in reading back the value or doing multiple subsequent writes to these registers. Signed-off-by: Tomasz Moń (cherry picked from commit 59702c7633e2b3358a74c8ba39765c8026fbf425) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index e97001ca9c3..5fb27945371 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -1437,13 +1437,16 @@ static int udc_dwc2_set_address(const struct device *dev, const uint8_t addr) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg; + uint32_t dcfg; if (addr > (USB_DWC2_DCFG_DEVADDR_MASK >> USB_DWC2_DCFG_DEVADDR_POS)) { return -EINVAL; } - sys_clear_bits(dcfg_reg, USB_DWC2_DCFG_DEVADDR_MASK); - sys_set_bits(dcfg_reg, usb_dwc2_set_dcfg_devaddr(addr)); + dcfg = sys_read32(dcfg_reg); + dcfg &= ~USB_DWC2_DCFG_DEVADDR_MASK; + dcfg |= usb_dwc2_set_dcfg_devaddr(addr); + sys_write32(dcfg, dcfg_reg); LOG_DBG("Set new address %u for %p", addr, dev); return 0; @@ -1454,14 +1457,14 @@ static int udc_dwc2_test_mode(const struct device *dev, { struct usb_dwc2_reg *const base = dwc2_get_base(dev); mem_addr_t dctl_reg = (mem_addr_t)&base->dctl; - uint32_t tstctl; + uint32_t dctl; if (mode == 0U || mode > USB_DWC2_DCTL_TSTCTL_TESTFE) { return -EINVAL; } - tstctl = usb_dwc2_get_dctl_tstctl(sys_read32(dctl_reg)); - if (tstctl != USB_DWC2_DCTL_TSTCTL_DISABLED) { + dctl = sys_read32(dctl_reg); + if (usb_dwc2_get_dctl_tstctl(dctl) != USB_DWC2_DCTL_TSTCTL_DISABLED) { return -EALREADY; } @@ -1470,7 +1473,8 @@ static int udc_dwc2_test_mode(const struct device *dev, return 0; } - sys_set_bits(dctl_reg, usb_dwc2_set_dctl_tstctl(mode)); + dctl |= usb_dwc2_set_dctl_tstctl(mode); + sys_write32(dctl, dctl_reg); LOG_DBG("Enable Test Mode %u", mode); return 0; @@ -1548,6 +1552,7 @@ static int udc_dwc2_init_controller(const struct device *dev) struct usb_dwc2_reg *const base = config->base; mem_addr_t gusbcfg_reg = (mem_addr_t)&base->gusbcfg; mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg; + uint32_t dcfg; uint32_t gusbcfg; uint32_t ghwcfg2; uint32_t ghwcfg3; @@ -1616,19 +1621,24 @@ static int udc_dwc2_init_controller(const struct device *dev) LOG_DBG("LPM mode is %s", (ghwcfg3 & USB_DWC2_GHWCFG3_LPMMODE) ? "enabled" : "disabled"); + dcfg = sys_read32(dcfg_reg); + /* Configure PHY and device speed */ + dcfg &= ~USB_DWC2_DCFG_DEVSPD_MASK; switch (usb_dwc2_get_ghwcfg2_hsphytype(ghwcfg2)) { case USB_DWC2_GHWCFG2_HSPHYTYPE_UTMIPLUSULPI: __fallthrough; case USB_DWC2_GHWCFG2_HSPHYTYPE_ULPI: gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB20 | USB_DWC2_GUSBCFG_ULPI_UTMI_SEL_ULPI; - sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBHS20); + dcfg |= USB_DWC2_DCFG_DEVSPD_USBHS20 + << USB_DWC2_DCFG_DEVSPD_POS; break; case USB_DWC2_GHWCFG2_HSPHYTYPE_UTMIPLUS: gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB20 | USB_DWC2_GUSBCFG_ULPI_UTMI_SEL_UTMI; - sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBHS20); + dcfg |= USB_DWC2_DCFG_DEVSPD_USBHS20 + << USB_DWC2_DCFG_DEVSPD_POS; break; case USB_DWC2_GHWCFG2_HSPHYTYPE_NO_HS: __fallthrough; @@ -1638,7 +1648,8 @@ static int udc_dwc2_init_controller(const struct device *dev) gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB11; } - sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBFS1148); + dcfg |= USB_DWC2_DCFG_DEVSPD_USBFS1148 + << USB_DWC2_DCFG_DEVSPD_POS; } if (usb_dwc2_get_ghwcfg4_phydatawidth(ghwcfg4)) { @@ -1646,7 +1657,8 @@ static int udc_dwc2_init_controller(const struct device *dev) } /* Update PHY configuration */ - sys_set_bits(gusbcfg_reg, gusbcfg); + sys_write32(gusbcfg, gusbcfg_reg); + sys_write32(dcfg, dcfg_reg); priv->outeps = 0U; for (uint8_t i = 0U; i < priv->numdeveps; i++) { From df1a1c607a0b255a327ebf8a3fce2175935b0b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 28 May 2024 13:26:30 +0200 Subject: [PATCH 09/39] [nrf fromtree] drivers: udc_dwc2: Initial Buffer DMA support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement absolute minimum necessary to get Buffer DMA working. Require Data Cache to be disabled if DWC2 OTG has DMA enabled because the stack does not currently guarantee cache line alignment for allocated buffers. Set EPENA bit before disabling IN endpoints because it seems to be necessary in Buffer DMA mode (without EPENA the wait for INEPNAKEFF does time out). Setting EPENA should have no impact on Completer mode operation (where EPENA is not necessary for INEPNAKEFF to trigger). Programming Guide recommends programming SUPCnt to 3, but the only advantage would be to be able to tell how many back-to-back SETUP packets were received if there was no more than 3 back-to-back SETUPs. This information doesn't seem to be useful. The disadvantage is that the buffer needed for receiving SETUP packet must be able to hold SUPCnt multiple of 8 bytes. Use SUPCnt 1 so the 8 bytes buffer is enough. Make sure to clear StsPhseRcvd interrupt bit in Buffer DMA mode because the DMA seems to prevent the SETUP Phase Done interrupt from triggering if StsPhseRcvd is set. Clearing this bit doesn't seem to be necessary in Completer mode. This bit is set on control transfers with data stage from host to device. Both Buffer DMA and Completer mode operation on nRF54H20DK was verified using USB2CV Chapter 9 Tests and MSC Tests with Mass Storage sample. Signed-off-by: Tomasz Moń (cherry picked from commit e1b95eb6df951c0559209b973f38ea4101710a7a) Signed-off-by: Tomasz Moń --- drivers/usb/udc/Kconfig.dwc2 | 7 + drivers/usb/udc/udc_dwc2.c | 305 ++++++++++++++++++++++++++++++----- 2 files changed, 272 insertions(+), 40 deletions(-) diff --git a/drivers/usb/udc/Kconfig.dwc2 b/drivers/usb/udc/Kconfig.dwc2 index a1a1c9d22ce..a14481794c7 100644 --- a/drivers/usb/udc/Kconfig.dwc2 +++ b/drivers/usb/udc/Kconfig.dwc2 @@ -10,6 +10,13 @@ config UDC_DWC2 help DWC2 USB device controller driver. +config UDC_DWC2_DMA + bool "DWC2 USB DMA support" + default n + depends on UDC_DWC2 + help + Enable Buffer DMA if DWC2 USB controller supports Internal DMA. + config UDC_DWC2_STACK_SIZE int "UDC DWC2 driver internal thread stack size" depends on UDC_DWC2 diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 5fb27945371..6ca435f4537 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -69,6 +70,7 @@ struct udc_dwc2_data { uint32_t max_pktcnt; uint32_t tx_len[16]; unsigned int dynfifosizing : 1; + unsigned int bufferdma : 1; /* Number of endpoints including control endpoint */ uint8_t numdeveps; /* Number of IN endpoints including control endpoint */ @@ -248,6 +250,25 @@ static void dwc2_set_epint(const struct device *dev, } } +static bool dwc2_dma_buffer_ok_to_use(const struct device *dev, void *buf, + uint32_t xfersize, uint16_t mps) +{ + ARG_UNUSED(dev); + + if (!IS_ALIGNED(buf, 4)) { + LOG_ERR("Buffer not aligned"); + return false; + } + + /* If Max Packet Size is not */ + if (unlikely(mps % 4) && (xfersize > mps)) { + LOG_ERR("Padding not supported"); + return false; + } + + return true; +} + /* Can be called from ISR context */ static int dwc2_tx_fifo_write(const struct device *dev, struct udc_ep_config *const cfg, struct net_buf *const buf) @@ -320,27 +341,44 @@ static int dwc2_tx_fifo_write(const struct device *dev, /* Set number of packets and transfer size */ sys_write32((pktcnt << USB_DWC2_DEPTSIZN_PKTCNT_POS) | len, dieptsiz_reg); + if (priv->bufferdma) { + if (!dwc2_dma_buffer_ok_to_use(dev, buf->data, len, cfg->mps)) { + /* Cannot continue unless buffer is bounced. Device will + * cease to function. Is fatal error appropriate here? + */ + irq_unlock(key); + return -ENOTSUP; + } + + sys_write32((uint32_t)buf->data, + (mem_addr_t)&base->in_ep[ep_idx].diepdma); + + sys_cache_data_flush_range(buf->data, len); + } + /* Clear NAK and set endpoint enable */ sys_set_bits(diepctl_reg, USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK); /* Clear IN Endpoint NAK Effective interrupt in case it was set */ sys_write32(USB_DWC2_DIEPINT_INEPNAKEFF, diepint_reg); - /* FIFO access is always in 32-bit words */ + if (!priv->bufferdma) { + /* FIFO access is always in 32-bit words */ - for (uint32_t i = 0UL; i < len; i += d) { - uint32_t val = buf->data[i]; + for (uint32_t i = 0UL; i < len; i += d) { + uint32_t val = buf->data[i]; - if (i + 1 < len) { - val |= ((uint32_t)buf->data[i + 1UL]) << 8; - } - if (i + 2 < len) { - val |= ((uint32_t)buf->data[i + 2UL]) << 16; - } - if (i + 3 < len) { - val |= ((uint32_t)buf->data[i + 3UL]) << 24; - } + if (i + 1 < len) { + val |= ((uint32_t)buf->data[i + 1UL]) << 8; + } + if (i + 2 < len) { + val |= ((uint32_t)buf->data[i + 2UL]) << 16; + } + if (i + 3 < len) { + val |= ((uint32_t)buf->data[i + 3UL]) << 24; + } - sys_write32(val, UDC_DWC2_EP_FIFO(base, ep_idx)); + sys_write32(val, UDC_DWC2_EP_FIFO(base, ep_idx)); + } } irq_unlock(key); @@ -380,23 +418,66 @@ static inline int dwc2_read_fifo(const struct device *dev, const uint8_t ep, return 0; } +static uint32_t dwc2_rx_xfer_size(struct udc_dwc2_data *const priv, + struct udc_ep_config *const cfg, + struct net_buf *buf) +{ + uint32_t size; + + if (priv->bufferdma) { + size = net_buf_tailroom(buf); + + /* Do as many packets in a single DMA as possible */ + if (size > priv->max_xfersize) { + size = ROUND_DOWN(priv->max_xfersize, cfg->mps); + } + } else { + /* Completer mode can always program Max Packet Size, RxFLvl + * interrupt will drop excessive data if necessary (i.e. buffer + * is too short). + */ + size = cfg->mps; + } + + return size; +} + /* Can be called from ISR and we call it only when there is a buffer in the queue */ -static void dwc2_prep_rx(const struct device *dev, +static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, struct udc_ep_config *const cfg, const bool ncnak) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); + struct udc_dwc2_data *const priv = udc_get_private(dev); uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); mem_addr_t doeptsiz_reg = (mem_addr_t)&base->out_ep[ep_idx].doeptsiz; mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx); uint32_t doeptsiz; + uint32_t xfersize; + + xfersize = dwc2_rx_xfer_size(priv, cfg, buf); - doeptsiz = (1 << USB_DWC2_DOEPTSIZ0_PKTCNT_POS) | cfg->mps; + doeptsiz = xfersize | usb_dwc2_set_deptsizn_pktcnt(DIV_ROUND_UP(xfersize, cfg->mps)); if (cfg->addr == USB_CONTROL_EP_OUT) { - doeptsiz |= (3 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS); + /* Use 1 to allow 8 byte long buffers for SETUP data */ + doeptsiz |= (1 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS); } sys_write32(doeptsiz, doeptsiz_reg); + if (priv->bufferdma) { + if (!dwc2_dma_buffer_ok_to_use(dev, buf->data, xfersize, cfg->mps)) { + /* Cannot continue unless buffer is bounced. Device will + * cease to function. Is fatal error appropriate here? + */ + return; + } + + sys_write32((uint32_t)buf->data, + (mem_addr_t)&base->out_ep[ep_idx].doepdma); + + sys_cache_data_invd_range(buf->data, xfersize); + } + if (ncnak) { sys_set_bits(doepctl_reg, USB_DWC2_DEPCTL_EPENA); } else { @@ -417,7 +498,7 @@ static void dwc2_handle_xfer_next(const struct device *dev, } if (USB_EP_DIR_IS_OUT(cfg->addr)) { - dwc2_prep_rx(dev, cfg, 0); + dwc2_prep_rx(dev, buf, cfg, 0); } else { if (dwc2_tx_fifo_write(dev, cfg, buf)) { LOG_ERR("Failed to start write to TX FIFO, ep 0x%02x", @@ -430,6 +511,7 @@ static void dwc2_handle_xfer_next(const struct device *dev, static int dwc2_ctrl_feed_dout(const struct device *dev, const size_t length) { + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); struct net_buf *buf; buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); @@ -437,8 +519,8 @@ static int dwc2_ctrl_feed_dout(const struct device *dev, const size_t length) return -ENOMEM; } - udc_buf_put(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT), buf); - dwc2_prep_rx(dev, udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT), 0); + udc_buf_put(ep_cfg, buf); + dwc2_prep_rx(dev, buf, ep_cfg, 0); LOG_DBG("feed buf %p", buf); return 0; @@ -642,6 +724,7 @@ static void dwc2_on_bus_reset(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); struct udc_dwc2_data *const priv = udc_get_private(dev); + uint32_t doepmsk; /* Set the NAK bit for all OUT endpoints */ for (uint8_t i = 0U; i < priv->numdeveps; i++) { @@ -656,10 +739,21 @@ static void dwc2_on_bus_reset(const struct device *dev) } } - sys_write32(USB_DWC2_DOEPINT_SETUP, (mem_addr_t)&base->doepmsk); - sys_set_bits((mem_addr_t)&base->gintmsk, USB_DWC2_GINTSTS_RXFLVL); + doepmsk = USB_DWC2_DOEPINT_SETUP; + if (priv->bufferdma) { + doepmsk |= USB_DWC2_DOEPINT_XFERCOMPL | + USB_DWC2_DOEPINT_STSPHSERCVD; + } + + sys_write32(doepmsk, (mem_addr_t)&base->doepmsk); sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL); + /* Software has to handle RxFLvl interrupt only in Completer mode */ + if (!priv->bufferdma) { + sys_set_bits((mem_addr_t)&base->gintmsk, + USB_DWC2_GINTSTS_RXFLVL); + } + /* Clear device address during reset. */ sys_clear_bits((mem_addr_t)&base->dcfg, USB_DWC2_DCFG_DEVADDR_MASK); } @@ -744,7 +838,7 @@ static inline void dwc2_handle_rxflvl(const struct device *dev) } if (net_buf_tailroom(buf) && evt.bcnt == ep_cfg->mps) { - dwc2_prep_rx(dev, ep_cfg, 0); + dwc2_prep_rx(dev, buf, ep_cfg, 0); } else { k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); } @@ -763,8 +857,8 @@ static inline void dwc2_handle_rxflvl(const struct device *dev) } } -static inline void dwc2_handle_xfercompl(const struct device *dev, - const uint8_t ep_idx) +static inline void dwc2_handle_in_xfercompl(const struct device *dev, + const uint8_t ep_idx) { struct udc_dwc2_data *const priv = udc_get_private(dev); struct udc_ep_config *ep_cfg; @@ -814,7 +908,7 @@ static inline void dwc2_handle_iepint(const struct device *dev) n | USB_EP_DIR_IN, status); if (status & USB_DWC2_DIEPINT_XFERCOMPL) { - dwc2_handle_xfercompl(dev, n); + dwc2_handle_in_xfercompl(dev, n); } } @@ -824,9 +918,52 @@ static inline void dwc2_handle_iepint(const struct device *dev) sys_write32(USB_DWC2_GINTSTS_IEPINT, (mem_addr_t)&base->gintsts); } +static inline void dwc2_handle_out_xfercompl(const struct device *dev, + const uint8_t ep_idx) +{ + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep_idx); + struct udc_dwc2_data *const priv = udc_get_private(dev); + struct usb_dwc2_reg *const base = dwc2_get_base(dev); + struct dwc2_drv_event evt; + struct net_buf *buf; + uint32_t doeptsiz; + + doeptsiz = sys_read32((mem_addr_t)&base->out_ep[ep_idx].doeptsiz); + + buf = udc_buf_peek(dev, ep_cfg->addr); + if (!buf) { + LOG_ERR("No buffer for ep 0x%02x", ep_cfg->addr); + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return; + } + + evt.type = DWC2_DRV_EVT_DOUT; + evt.ep = ep_cfg->addr; + /* Assume udc buffer and endpoint config is the same as it was when + * transfer was scheduled in dwc2_prep_rx(). The original transfer size + * value is necessary here because controller decreases the value for + * every byte stored. + */ + evt.bcnt = dwc2_rx_xfer_size(priv, ep_cfg, buf) - + usb_dwc2_get_deptsizn_xfersize(doeptsiz); + + if (priv->bufferdma) { + sys_cache_data_invd_range(buf->data, evt.bcnt); + } + + net_buf_add(buf, evt.bcnt); + + if (((evt.bcnt % ep_cfg->mps) == 0) && net_buf_tailroom(buf)) { + dwc2_prep_rx(dev, buf, ep_cfg, 0); + } else { + k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + } +} + static inline void dwc2_handle_oepint(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); + struct udc_dwc2_data *const priv = udc_get_private(dev); const uint8_t n_max = 16; uint32_t doepmsk; uint32_t daint; @@ -839,23 +976,59 @@ static inline void dwc2_handle_oepint(const struct device *dev) uint32_t doepint; uint32_t status; - if (daint & USB_DWC2_DAINT_OUTEPINT(n)) { - /* Read and clear interrupt status */ - doepint = sys_read32(doepint_reg); - status = doepint & doepmsk; - sys_write32(status, doepint_reg); + if (!(daint & USB_DWC2_DAINT_OUTEPINT(n))) { + continue; + } - LOG_DBG("ep 0x%02x interrupt status: 0x%x", n, status); + /* Read and clear interrupt status */ + doepint = sys_read32(doepint_reg); + status = doepint & doepmsk; + sys_write32(status, doepint_reg); - if (status & USB_DWC2_DOEPINT_SETUP) { - struct dwc2_drv_event evt = { - .type = DWC2_DRV_EVT_SETUP, - .ep = USB_CONTROL_EP_OUT, - .bcnt = 8, - }; + LOG_DBG("ep 0x%02x interrupt status: 0x%x", n, status); - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); - } + /* StupPktRcvd is not enabled for interrupt, but must be checked + * when XferComp hits to determine if SETUP token was received. + */ + if (priv->bufferdma && (status & USB_DWC2_DOEPINT_XFERCOMPL) && + (doepint & USB_DWC2_DOEPINT_STUPPKTRCVD)) { + uint32_t addr; + + sys_write32(USB_DWC2_DOEPINT_STUPPKTRCVD, doepint_reg); + status &= ~USB_DWC2_DOEPINT_XFERCOMPL; + + /* DMAAddr points past the memory location where the + * SETUP data was stored. Copy the received SETUP data + * to temporary location used also in Completer mode + * which allows common SETUP interrupt handling. + */ + addr = sys_read32((mem_addr_t)&base->out_ep[0].doepdma); + sys_cache_data_invd_range((void *)(addr - 8), 8); + memcpy(priv->setup, (void *)(addr - 8), sizeof(priv->setup)); + } + + if (status & USB_DWC2_DOEPINT_SETUP) { + struct dwc2_drv_event evt = { + .type = DWC2_DRV_EVT_SETUP, + .ep = USB_CONTROL_EP_OUT, + .bcnt = 8, + }; + + k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + } + + if (status & USB_DWC2_DOEPINT_STSPHSERCVD) { + /* Driver doesn't need any special handling, but it is + * mandatory that the bit is cleared in Buffer DMA mode. + * If the bit is not cleared (i.e. when this interrupt + * bit is masked), then SETUP interrupts will cease + * after first control transfer with data stage from + * device to host. + */ + } + + if (status & USB_DWC2_DOEPINT_XFERCOMPL) { + dwc2_handle_out_xfercompl(dev, n); } } @@ -1105,6 +1278,23 @@ static int udc_dwc2_ep_activate(const struct device *dev, return -EINVAL; } + if (priv->bufferdma && (cfg->mps % 4)) { + /* TODO: In Buffer DMA mode, DMA will insert padding bytes in + * between packets if endpoint Max Packet Size is not multiple + * of 4 (DWORD) and single transfer spans across multiple + * packets. + * + * In order to support such Max Packet Sizes, the driver would + * have to remove the padding in between the packets. Besides + * just driver shuffling the data, the buffers would have to be + * large enough to temporarily hold the paddings. + * + * For the time being just error out early. + */ + LOG_ERR("Driver requires MPS to be multiple of 4"); + return -EINVAL; + } + dxepctl = sys_read32(dxepctl_reg); /* Set max packet size */ dxepctl &= ~USB_DWC2_DEPCTL_MPS_MASK; @@ -1281,7 +1471,7 @@ static void udc_dwc2_ep_disable(const struct device *dev, diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint; - dxepctl |= USB_DWC2_DEPCTL_SNAK; + dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_SNAK; if (stall) { /* For IN endpoints STALL is set in addition to SNAK */ dxepctl |= USB_DWC2_DEPCTL_STALL; @@ -1550,10 +1740,12 @@ static int udc_dwc2_init_controller(const struct device *dev) const struct udc_dwc2_config *const config = dev->config; struct udc_dwc2_data *const priv = udc_get_private(dev); struct usb_dwc2_reg *const base = config->base; + mem_addr_t gahbcfg_reg = (mem_addr_t)&base->gahbcfg; mem_addr_t gusbcfg_reg = (mem_addr_t)&base->gusbcfg; mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg; uint32_t dcfg; uint32_t gusbcfg; + uint32_t gahbcfg; uint32_t ghwcfg2; uint32_t ghwcfg3; uint32_t ghwcfg4; @@ -1582,6 +1774,18 @@ static int udc_dwc2_init_controller(const struct device *dev) sys_write32(gusbcfg, gusbcfg_reg); k_msleep(25); + /* Buffer DMA is always supported in Internal DMA mode. + * TODO: check and support descriptor DMA if available + */ + priv->bufferdma = (usb_dwc2_get_ghwcfg2_otgarch(ghwcfg2) == + USB_DWC2_GHWCFG2_OTGARCH_INTERNALDMA); + + if (!IS_ENABLED(CONFIG_UDC_DWC2_DMA)) { + priv->bufferdma = 0; + } else if (priv->bufferdma) { + LOG_WRN("Experimental DMA enabled"); + } + if (ghwcfg2 & USB_DWC2_GHWCFG2_DYNFIFOSIZING) { LOG_DBG("Dynamic FIFO Sizing is enabled"); priv->dynfifosizing = true; @@ -1621,8 +1825,21 @@ static int udc_dwc2_init_controller(const struct device *dev) LOG_DBG("LPM mode is %s", (ghwcfg3 & USB_DWC2_GHWCFG3_LPMMODE) ? "enabled" : "disabled"); + /* Configure AHB, select Completer or DMA mode */ + gahbcfg = sys_read32(gahbcfg_reg); + + if (priv->bufferdma) { + gahbcfg |= USB_DWC2_GAHBCFG_DMAEN; + } else { + gahbcfg &= ~USB_DWC2_GAHBCFG_DMAEN; + } + + sys_write32(gahbcfg, gahbcfg_reg); + dcfg = sys_read32(dcfg_reg); + dcfg &= ~USB_DWC2_DCFG_DESCDMA; + /* Configure PHY and device speed */ dcfg &= ~USB_DWC2_DCFG_DEVSPD_MASK; switch (usb_dwc2_get_ghwcfg2_hsphytype(ghwcfg2)) { @@ -2028,6 +2245,14 @@ static const struct udc_api udc_dwc2_api = { .ghwcfg4 = DT_INST_PROP(n, ghwcfg4), \ }; \ \ + IF_ENABLED(CONFIG_DCACHE, (BUILD_ASSERT( \ + !IS_ENABLED(CONFIG_UDC_DWC2_DMA) || \ + (DT_INST_PROP(n, ghwcfg2) & USB_DWC2_GHWCFG2_OTGARCH_MASK) != \ + (USB_DWC2_GHWCFG2_OTGARCH_INTERNALDMA << \ + USB_DWC2_GHWCFG2_OTGARCH_POS), \ + "USB stack does not provide D-Cache aware buffers yet"); \ + )) \ + \ static struct udc_dwc2_data udc_priv_##n = { \ }; \ \ From 1425d6c1a7ace225b00f8dea7ba1c6da7db6d115 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Fri, 14 Jun 2024 18:34:14 +0200 Subject: [PATCH 10/39] [nrf fromtree] doc: usb: document USB message notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add documentation about USB message notifications. Use literalinclude to pull code snippets from the samples. Signed-off-by: Johann Fischer (cherry picked from commit 7e123084e2413c52a2598da3dabe8fb195e26ee7) Signed-off-by: Tomasz Moń --- .../usb/device_next/usb_device.rst | 28 ++++++++++++++ samples/subsys/usb/common/sample_usbd_init.c | 4 +- samples/subsys/usb/hid-keyboard/src/main.c | 38 +++++++++++++++---- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/doc/connectivity/usb/device_next/usb_device.rst b/doc/connectivity/usb/device_next/usb_device.rst index 852da003cef..97864c29070 100644 --- a/doc/connectivity/usb/device_next/usb_device.rst +++ b/doc/connectivity/usb/device_next/usb_device.rst @@ -185,3 +185,31 @@ enumerating the device. The application can disable the USB device using :language: c :start-after: doc device enable start :end-before: doc device enable end + +USB Message notifications +========================= + +The application can register a callback using :c:func:`usbd_msg_register_cb` to +receive message notification from the USB device support subsystem. The +messages are mostly about the common device state changes, and a few specific +types from the USB CDC ACM implementation. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :dedent: + :start-after: doc device init-and-msg start + :end-before: doc device init-and-msg end + +The helper function :c:func:`usbd_msg_type_string()` can be used to convert +:c:enumerator:`usbd_msg_type` to a human readable form for logging. + +If the controller supports VBUS state change detection, the battery-powered +application may want to enable the USB device only when it is connected to a +host. A generic application should use :c:func:`usbd_can_detect_vbus` to check +for this capability. + +.. literalinclude:: ../../../../samples/subsys/usb/hid-keyboard/src/main.c + :language: c + :dedent: + :start-after: doc device msg-cb start + :end-before: doc device msg-cb end diff --git a/samples/subsys/usb/common/sample_usbd_init.c b/samples/subsys/usb/common/sample_usbd_init.c index f6b2963b212..1cca20d85d2 100644 --- a/samples/subsys/usb/common/sample_usbd_init.c +++ b/samples/subsys/usb/common/sample_usbd_init.c @@ -148,15 +148,15 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS); - /* doc message callback register start */ if (msg_cb != NULL) { + /* doc device init-and-msg start */ err = usbd_msg_register_cb(&sample_usbd, msg_cb); if (err) { LOG_ERR("Failed to register message callback"); return NULL; } + /* doc device init-and-msg end */ } - /* doc message callback register end */ if (IS_ENABLED(CONFIG_SAMPLE_USBD_20_EXTENSION_DESC)) { (void)usbd_device_set_bcd(&sample_usbd, USBD_SPEED_FS, 0x0201); diff --git a/samples/subsys/usb/hid-keyboard/src/main.c b/samples/subsys/usb/hid-keyboard/src/main.c index b56fe26c0c7..c10ae9e468b 100644 --- a/samples/subsys/usb/hid-keyboard/src/main.c +++ b/samples/subsys/usb/hid-keyboard/src/main.c @@ -141,6 +141,28 @@ struct hid_device_ops kb_ops = { .output_report = kb_output_report, }; +/* doc device msg-cb start */ +static void msg_cb(struct usbd_context *const usbd_ctx, + const struct usbd_msg *const msg) +{ + LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); + + if (usbd_can_detect_vbus(usbd_ctx)) { + if (msg->type == USBD_MSG_VBUS_READY) { + if (usbd_enable(usbd_ctx)) { + LOG_ERR("Failed to enable device support"); + } + } + + if (msg->type == USBD_MSG_VBUS_REMOVED) { + if (usbd_disable(usbd_ctx)) { + LOG_ERR("Failed to disable device support"); + } + } + } +} +/* doc device msg-cb end */ + int main(void) { struct usbd_context *sample_usbd; @@ -178,19 +200,21 @@ int main(void) return ret; } - sample_usbd = sample_usbd_init_device(NULL); + sample_usbd = sample_usbd_init_device(msg_cb); if (sample_usbd == NULL) { LOG_ERR("Failed to initialize USB device"); return -ENODEV; } - /* doc device enable start */ - ret = usbd_enable(sample_usbd); - if (ret) { - LOG_ERR("Failed to enable device support"); - return ret; + if (!usbd_can_detect_vbus(sample_usbd)) { + /* doc device enable start */ + ret = usbd_enable(sample_usbd); + if (ret) { + LOG_ERR("Failed to enable device support"); + return ret; + } + /* doc device enable end */ } - /* doc device enable end */ LOG_INF("HID keyboard sample is initialized"); From 3b225d7c96754706e0919b044e69bd4f1721899b Mon Sep 17 00:00:00 2001 From: Mark Wang Date: Mon, 17 Jun 2024 11:18:16 +0800 Subject: [PATCH 11/39] [nrf fromtree] drivers: udc: mcux: remove CONFIG_DT_HAS_NXP_USBPHY_ENABLED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit remove CONFIG_DT_HAS_NXP_USBPHY_ENABLED to make codes more simple Signed-off-by: Mark Wang (cherry picked from commit e2ec54bb9d05bd065fffa94cbf63bec3d14c03f9) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_mcux_ehci.c | 13 ++----------- drivers/usb/udc/udc_mcux_ip3511.c | 13 ++----------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index e5e56adb469..80d92ac1de9 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -20,9 +20,7 @@ #include "usb_device_config.h" #include "usb_device_mcux_drv_port.h" #include "usb_device_ehci.h" -#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED #include "usb_phy.h" -#endif #include LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL); @@ -45,9 +43,7 @@ struct udc_mcux_config { struct udc_ep_config *ep_cfg_out; uintptr_t base; const struct pinctrl_dev_config *pincfg; -#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED usb_phy_config_struct_t *phy_config; -#endif }; struct udc_mcux_data { @@ -856,7 +852,6 @@ static const usb_device_controller_interface_struct_t udc_mcux_if = { USB_DeviceEhciRecv, USB_DeviceEhciCancel, USB_DeviceEhciControl }; -#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED #define UDC_MCUX_PHY_DEFINE(n) \ static usb_phy_config_struct_t phy_config_##n = { \ .D_CAL = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_d_cal, 0), \ @@ -869,12 +864,8 @@ static usb_phy_config_struct_t phy_config_##n = { \ (UDC_MCUX_PHY_DEFINE(n)), ()) #define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) \ - .phy_config = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ (&phy_config_##n), (NULL)) -#else -#define UDC_MCUX_PHY_DEFINE_OR(n) -#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) -#endif #define USB_MCUX_EHCI_DEVICE_DEFINE(n) \ UDC_MCUX_PHY_DEFINE_OR(n); \ @@ -910,7 +901,7 @@ static usb_phy_config_struct_t phy_config_##n = { \ .ep_cfg_out = ep_cfg_out##n, \ .mcux_if = &udc_mcux_if, \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ - UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ + .phy_config = UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ }; \ \ static struct udc_mcux_data priv_data_##n = { \ diff --git a/drivers/usb/udc/udc_mcux_ip3511.c b/drivers/usb/udc/udc_mcux_ip3511.c index d3b257e80e7..4c709fdf137 100644 --- a/drivers/usb/udc/udc_mcux_ip3511.c +++ b/drivers/usb/udc/udc_mcux_ip3511.c @@ -20,9 +20,7 @@ #include "usb_device_config.h" #include "usb_device_mcux_drv_port.h" #include "usb_device_lpcip3511.h" -#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED #include "usb_phy.h" -#endif #include LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL); @@ -45,9 +43,7 @@ struct udc_mcux_config { struct udc_ep_config *ep_cfg_out; uintptr_t base; const struct pinctrl_dev_config *pincfg; -#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED usb_phy_config_struct_t *phy_config; -#endif }; struct udc_mcux_data { @@ -817,7 +813,6 @@ static const usb_device_controller_interface_struct_t udc_mcux_if = { USB_DeviceLpc3511IpRecv, USB_DeviceLpc3511IpCancel, USB_DeviceLpc3511IpControl }; -#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED #define UDC_MCUX_PHY_DEFINE(n) \ static usb_phy_config_struct_t phy_config_##n = { \ .D_CAL = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_d_cal, 0), \ @@ -830,12 +825,8 @@ static usb_phy_config_struct_t phy_config_##n = { \ (UDC_MCUX_PHY_DEFINE(n)), ()) #define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) \ - .phy_config = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ (&phy_config_##n), (NULL)) -#else -#define UDC_MCUX_PHY_DEFINE_OR(n) -#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) -#endif #define USB_MCUX_IP3511_DEVICE_DEFINE(n) \ UDC_MCUX_PHY_DEFINE_OR(n); \ @@ -871,7 +862,7 @@ static usb_phy_config_struct_t phy_config_##n = { \ .ep_cfg_out = ep_cfg_out##n, \ .mcux_if = &udc_mcux_if, \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ - UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ + .phy_config = UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ }; \ \ static struct udc_mcux_data priv_data_##n = { \ From f6084da70341c75f33b7e4d2fa6513777f00a140 Mon Sep 17 00:00:00 2001 From: Abe Kohandel Date: Sun, 10 Mar 2024 23:35:50 +0000 Subject: [PATCH 12/39] [nrf fromtree] usb: correct udc_dev parameter name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename uhc_dev parameter of USBD_DEVICE_DEFINE macro to udc_dev to reflect that this is a USB Device Controller device and not a USB Host Controller device. Signed-off-by: Abe Kohandel (cherry picked from commit de69ebd1f88854d14ca5b7c259870c35570ddfcb) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usbd.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 7a643b15db0..10a5c4b07b3 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -388,7 +388,7 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c return c_data->priv; } -#define USBD_DEVICE_DEFINE(device_name, uhc_dev, vid, pid) \ +#define USBD_DEVICE_DEFINE(device_name, udc_dev, vid, pid) \ static struct usb_device_descriptor \ fs_desc_##device_name = { \ .bLength = sizeof(struct usb_device_descriptor), \ @@ -425,7 +425,7 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c }; \ static STRUCT_SECTION_ITERABLE(usbd_context, device_name) = { \ .name = STRINGIFY(device_name), \ - .dev = uhc_dev, \ + .dev = udc_dev, \ .fs_desc = &fs_desc_##device_name, \ .hs_desc = &hs_desc_##device_name, \ } From 2cb6456bb5772881d310057f240af917e5d3d56c Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Wed, 22 May 2024 12:47:34 +0200 Subject: [PATCH 13/39] [nrf fromtree] samples: hid-mouse: protect report buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the message queue to pass the new report from the input callback, and use a semaphore to protect the report buffer until it is transferred to the host. Signed-off-by: Johann Fischer (cherry picked from commit 67a31ef2d781231aacb44b6a99681750a01123d5) Signed-off-by: Tomasz Moń --- samples/subsys/usb/hid-mouse/prj.conf | 1 + samples/subsys/usb/hid-mouse/src/main.c | 45 +++++++++++++++---------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/samples/subsys/usb/hid-mouse/prj.conf b/samples/subsys/usb/hid-mouse/prj.conf index 1fbf7592628..b797a7382e8 100644 --- a/samples/subsys/usb/hid-mouse/prj.conf +++ b/samples/subsys/usb/hid-mouse/prj.conf @@ -10,3 +10,4 @@ CONFIG_USB_DEVICE_LOG_LEVEL_ERR=y CONFIG_GPIO=y CONFIG_INPUT=y +CONFIG_INPUT_MODE_SYNCHRONOUS=y diff --git a/samples/subsys/usb/hid-mouse/src/main.c b/samples/subsys/usb/hid-mouse/src/main.c index 28b27877acb..45021d5a4bd 100644 --- a/samples/subsys/usb/hid-mouse/src/main.c +++ b/samples/subsys/usb/hid-mouse/src/main.c @@ -37,8 +37,8 @@ enum mouse_report_idx { MOUSE_REPORT_COUNT = 4, }; -static uint8_t __aligned(sizeof(void *)) report[MOUSE_REPORT_COUNT]; -static K_SEM_DEFINE(report_sem, 0, 1); +K_MSGQ_DEFINE(mouse_msgq, MOUSE_REPORT_COUNT, 2, 1); +static K_SEM_DEFINE(ep_write_sem, 0, 1); static inline void status_cb(enum usb_dc_status_code status, const uint8_t *param) { @@ -57,9 +57,7 @@ static ALWAYS_INLINE void rwup_if_suspended(void) static void input_cb(struct input_event *evt) { - uint8_t tmp[MOUSE_REPORT_COUNT]; - - (void)memcpy(tmp, report, sizeof(tmp)); + static uint8_t tmp[MOUSE_REPORT_COUNT]; switch (evt->code) { case INPUT_KEY_0: @@ -88,10 +86,13 @@ static void input_cb(struct input_event *evt) return; } - if (memcmp(tmp, report, sizeof(tmp))) { - memcpy(report, tmp, sizeof(report)); - k_sem_give(&report_sem); + if (k_msgq_put(&mouse_msgq, tmp, K_NO_WAIT) != 0) { + LOG_ERR("Failed to put new input event"); } + + tmp[MOUSE_X_REPORT_IDX] = 0U; + tmp[MOUSE_Y_REPORT_IDX] = 0U; + } INPUT_CALLBACK_DEFINE(NULL, input_cb); @@ -120,6 +121,16 @@ static int enable_usb_device_next(void) } #endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK_NEXT) */ +static void int_in_ready_cb(const struct device *dev) +{ + ARG_UNUSED(dev); + k_sem_give(&ep_write_sem); +} + +static const struct hid_ops ops = { + .int_in_ready = int_in_ready_cb, +}; + int main(void) { const struct device *hid_dev; @@ -148,7 +159,7 @@ int main(void) usb_hid_register_device(hid_dev, hid_report_desc, sizeof(hid_report_desc), - NULL); + &ops); usb_hid_init(hid_dev); @@ -163,19 +174,17 @@ int main(void) } while (true) { - k_sem_take(&report_sem, K_FOREVER); + uint8_t __aligned(sizeof(void *)) report[MOUSE_REPORT_COUNT]; + + k_msgq_get(&mouse_msgq, &report, K_FOREVER); ret = hid_int_ep_write(hid_dev, report, sizeof(report), NULL); - report[MOUSE_X_REPORT_IDX] = 0U; - report[MOUSE_Y_REPORT_IDX] = 0U; if (ret) { LOG_ERR("HID write error, %d", ret); - } - - /* Toggle LED on sent report */ - ret = gpio_pin_toggle(led0.port, led0.pin); - if (ret < 0) { - LOG_ERR("Failed to toggle the LED pin, error: %d", ret); + } else { + k_sem_take(&ep_write_sem, K_FOREVER); + /* Toggle LED on sent report */ + (void)gpio_pin_toggle(led0.port, led0.pin); } } return 0; From 917fe3471835b40edae256308847cfd1c8efa67f Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Mon, 24 Jun 2024 16:59:02 +0200 Subject: [PATCH 14/39] [nrf fromtree] include: usbd: add missing macro documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing documentation to USBD_DEVICE_DEFINE, USBD_CONFIGURATION_DEFINE and USBD_DEFINE_CLASS macros. Signed-off-by: Johann Fischer (cherry picked from commit 1248e26b8984730f1f88b3df8eafc9c2ee33408f) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usbd.h | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 10a5c4b07b3..77784ac1989 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -388,6 +388,26 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c return c_data->priv; } +/** + * @brief Define USB device context structure + * + * Macro defines a USB device structure needed by the stack to manage its + * properties and runtime data. The @p vid and @p pid parameters can also be + * changed using usbd_device_set_vid() and usbd_device_set_pid(). + * + * Example of use: + * + * @code{.c} + * USBD_DEVICE_DEFINE(sample_usbd, + * DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + * YOUR_VID, YOUR_PID); + * @endcode + * + * @param device_name USB device context name + * @param udc_dev Pointer to UDC device structure + * @param vid Vendor ID + * @param pid Product ID + */ #define USBD_DEVICE_DEFINE(device_name, udc_dev, vid, pid) \ static struct usb_device_descriptor \ fs_desc_##device_name = { \ @@ -430,6 +450,20 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c .hs_desc = &hs_desc_##device_name, \ } +/** + * @brief Define USB device configuration + * + * USB device requires at least one configuration instance per supported speed. + * @p attrib is a combination of `USB_SCD_SELF_POWERED` or `USB_SCD_REMOTE_WAKEUP`, + * depending on which characteristic the USB device should have in this + * configuration. + * + * @param name Configuration name + * @param attrib Configuration characteristics. Attributes can also be updated + * with usbd_config_attrib_rwup() and usbd_config_attrib_self() + * @param power bMaxPower value in 2 mA units. This value can also be set with + * usbd_config_maxpower() + */ #define USBD_CONFIGURATION_DEFINE(name, attrib, power) \ static struct usb_cfg_descriptor \ cfg_desc_##name = { \ @@ -563,6 +597,17 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c .bDescriptorType = USB_DESC_BOS, \ } +/** + * @brief Define USB device support class data + * + * Macro defines class (function) data, as well as corresponding node + * structures used internally by the stack. + * + * @param class_name Class name + * @param class_api Pointer to struct usbd_class_api + * @param class_priv Class private data + * @param class_v_reqs Pointer to struct usbd_cctx_vendor_req + */ #define USBD_DEFINE_CLASS(class_name, class_api, class_priv, class_v_reqs) \ static struct usbd_class_data class_name = { \ .name = STRINGIFY(class_name), \ From 5633d0b2af173f04d5f2d3ee44ad4763cf73789d Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Sat, 20 Jul 2024 00:06:22 +0200 Subject: [PATCH 15/39] [nrf fromtree] include: usb: fix designator order in usbd.h header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix "designator order for field 'usbd_desc_node::ptr' does not match declaration order in 'usbd_desc_node'" error when building applications with CONFIG_CPP enabled. Signed-off-by: Johann Fischer (cherry picked from commit 18c624e8dfe9ec216745cbbd91e81f7dc8e6bf69) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usbd.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 77784ac1989..2d21bdffac9 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -497,13 +497,13 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c #define USBD_DESC_LANG_DEFINE(name) \ static uint16_t langid_##name = sys_cpu_to_le16(0x0409); \ static struct usbd_desc_node name = { \ - .bLength = sizeof(struct usb_string_descriptor), \ - .bDescriptorType = USB_DESC_STRING, \ .str = { \ .idx = 0, \ .utype = USBD_DUT_STRING_LANG, \ }, \ .ptr = &langid_##name, \ + .bLength = sizeof(struct usb_string_descriptor), \ + .bDescriptorType = USB_DESC_STRING, \ } /** @@ -524,9 +524,9 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c .utype = d_utype, \ .ascii7 = true, \ }, \ + .ptr = &ascii_##d_name, \ .bLength = USB_STRING_DESCRIPTOR_LENGTH(d_string), \ .bDescriptorType = USB_DESC_STRING, \ - .ptr = &ascii_##d_name, \ } /** From 3f649ced2b3f91624e9053011afb8712b17f2da2 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Tue, 23 Jul 2024 15:28:27 +0200 Subject: [PATCH 16/39] [nrf fromtree] usb: device_next: remove unnecessary usbd_device_unlock() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary usbd_device_unlock() in usbd_add_configuration(). Signed-off-by: Johann Fischer (cherry picked from commit 1540ba19d7151921f3ed72b2b74565676b0f9a02) Signed-off-by: Tomasz Moń --- subsys/usb/device_next/usbd_config.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/subsys/usb/device_next/usbd_config.c b/subsys/usb/device_next/usbd_config.c index 25a2a082bc7..ce08775c456 100644 --- a/subsys/usb/device_next/usbd_config.c +++ b/subsys/usb/device_next/usbd_config.c @@ -321,8 +321,6 @@ int usbd_add_configuration(struct usbd_context *const uds_ctx, sys_slist_append(configs, &cfg_nd->node); - usbd_device_unlock(uds_ctx); - add_configuration_exit: usbd_device_unlock(uds_ctx); return ret; From d61d0f2880b7928fbe115aa0ed224acdada51f7b Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Tue, 9 Jul 2024 13:52:44 +0200 Subject: [PATCH 17/39] [nrf fromtree] usb: device_next: add support for configuration string descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change USBD_CONFIGURATION_DEFINE macro to take the address of a string descriptor node as an argument. This is a breaking change for macro users, but quite convenient and easy to implement. Signed-off-by: Johann Fischer (cherry picked from commit 28b2051bdaf56b51a8b8c888cd6c048f07f383cf) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usbd.h | 21 +++++++++++++++++++- samples/subsys/usb/common/sample_usbd_init.c | 7 +++++-- subsys/usb/device_next/usbd_config.c | 10 ++++++++++ subsys/usb/device_next/usbd_desc.c | 3 ++- subsys/usb/device_next/usbd_shell.c | 8 ++++---- tests/subsys/usb/device_next/src/main.c | 4 ++-- 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 2d21bdffac9..9a0c0a35698 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -67,6 +67,7 @@ enum usbd_str_desc_utype { USBD_DUT_STRING_MANUFACTURER, USBD_DUT_STRING_PRODUCT, USBD_DUT_STRING_SERIAL_NUMBER, + USBD_DUT_STRING_CONFIG, USBD_DUT_STRING_INTERFACE, }; @@ -131,6 +132,8 @@ struct usbd_config_node { sys_snode_t node; /** Pointer to configuration descriptor */ void *desc; + /** Optional pointer to string descriptor node */ + struct usbd_desc_node *str_desc_nd; /** List of registered classes (functions) */ sys_slist_t class_list; }; @@ -463,8 +466,11 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c * with usbd_config_attrib_rwup() and usbd_config_attrib_self() * @param power bMaxPower value in 2 mA units. This value can also be set with * usbd_config_maxpower() + * @param desc_nd Address of the string descriptor node used to describe the + * configuration, see USBD_DESC_CONFIG_DEFINE(). + * String descriptors are optional and the parameter can be NULL. */ -#define USBD_CONFIGURATION_DEFINE(name, attrib, power) \ +#define USBD_CONFIGURATION_DEFINE(name, attrib, power, desc_nd) \ static struct usb_cfg_descriptor \ cfg_desc_##name = { \ .bLength = sizeof(struct usb_cfg_descriptor), \ @@ -479,6 +485,7 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c BUILD_ASSERT((power) < 256, "Too much power"); \ static struct usbd_config_node name = { \ .desc = &cfg_desc_##name, \ + .str_desc_nd = desc_nd, \ } /** @@ -577,6 +584,18 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c .bDescriptorType = USB_DESC_STRING, \ } +/** + * @brief Create a string descriptor node for configuration descriptor + * + * This macro defines a descriptor node whose address can be used as an + * argument for the USBD_CONFIGURATION_DEFINE() macro. + * + * @param d_name String descriptor node identifier. + * @param d_string ASCII7 encoded configuration description string literal + */ +#define USBD_DESC_CONFIG_DEFINE(d_name, d_string) \ + USBD_DESC_STRING_DEFINE(d_name, d_string, USBD_DUT_STRING_CONFIG) + /** * @brief Define BOS Device Capability descriptor node * diff --git a/samples/subsys/usb/common/sample_usbd_init.c b/samples/subsys/usb/common/sample_usbd_init.c index 1cca20d85d2..768b7550a5a 100644 --- a/samples/subsys/usb/common/sample_usbd_init.c +++ b/samples/subsys/usb/common/sample_usbd_init.c @@ -33,6 +33,9 @@ USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_SAMPLE_USBD_PRODUCT); USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn); /* doc string instantiation end */ +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); + /* doc configuration instantiation start */ static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ? USB_SCD_SELF_POWERED : 0) | @@ -42,12 +45,12 @@ static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ? /* Full speed configuration */ USBD_CONFIGURATION_DEFINE(sample_fs_config, attributes, - CONFIG_SAMPLE_USBD_MAX_POWER); + CONFIG_SAMPLE_USBD_MAX_POWER, &fs_cfg_desc); /* High speed configuration */ USBD_CONFIGURATION_DEFINE(sample_hs_config, attributes, - CONFIG_SAMPLE_USBD_MAX_POWER); + CONFIG_SAMPLE_USBD_MAX_POWER, &hs_cfg_desc); /* doc configuration instantiation end */ /* diff --git a/subsys/usb/device_next/usbd_config.c b/subsys/usb/device_next/usbd_config.c index ce08775c456..9d8627028a9 100644 --- a/subsys/usb/device_next/usbd_config.c +++ b/subsys/usb/device_next/usbd_config.c @@ -319,6 +319,16 @@ int usbd_add_configuration(struct usbd_context *const uds_ctx, usbd_set_num_configs(uds_ctx, speed, num); } + if (cfg_nd->str_desc_nd != NULL) { + ret = usbd_add_descriptor(uds_ctx, cfg_nd->str_desc_nd); + if (ret != 0) { + LOG_ERR("Failed to add configuration string descriptor"); + goto add_configuration_exit; + } + + desc->iConfiguration = usbd_str_desc_get_idx(cfg_nd->str_desc_nd); + } + sys_slist_append(configs, &cfg_nd->node); add_configuration_exit: diff --git a/subsys/usb/device_next/usbd_desc.c b/subsys/usb/device_next/usbd_desc.c index 158cdb1ba22..469953d1d27 100644 --- a/subsys/usb/device_next/usbd_desc.c +++ b/subsys/usb/device_next/usbd_desc.c @@ -109,7 +109,8 @@ int usbd_desc_remove_all(struct usbd_context *const uds_ctx) while ((node = sys_dlist_get(&uds_ctx->descriptors))) { tmp = CONTAINER_OF(node, struct usbd_desc_node, node); - LOG_DBG("Remove descriptor node %p", tmp); + LOG_DBG("Remove descriptor node %p type %u", + (void *)tmp, tmp->str.utype); } return 0; diff --git a/subsys/usb/device_next/usbd_shell.c b/subsys/usb/device_next/usbd_shell.c index 173a2a07627..82815306777 100644 --- a/subsys/usb/device_next/usbd_shell.c +++ b/subsys/usb/device_next/usbd_shell.c @@ -15,10 +15,10 @@ #include /* Default configurations used in the shell context. */ -USBD_CONFIGURATION_DEFINE(config_1_fs, USB_SCD_REMOTE_WAKEUP, 200); -USBD_CONFIGURATION_DEFINE(config_1_hs, USB_SCD_REMOTE_WAKEUP, 200); -USBD_CONFIGURATION_DEFINE(config_2_fs, USB_SCD_SELF_POWERED, 200); -USBD_CONFIGURATION_DEFINE(config_2_hs, USB_SCD_SELF_POWERED, 200); +USBD_CONFIGURATION_DEFINE(config_1_fs, USB_SCD_REMOTE_WAKEUP, 200, NULL); +USBD_CONFIGURATION_DEFINE(config_1_hs, USB_SCD_REMOTE_WAKEUP, 200, NULL); +USBD_CONFIGURATION_DEFINE(config_2_fs, USB_SCD_SELF_POWERED, 200, NULL); +USBD_CONFIGURATION_DEFINE(config_2_hs, USB_SCD_SELF_POWERED, 200, NULL); static struct usbd_shell_config { struct usbd_config_node *cfg_nd; diff --git a/tests/subsys/usb/device_next/src/main.c b/tests/subsys/usb/device_next/src/main.c index f33b054fcd1..a0fcb0e8396 100644 --- a/tests/subsys/usb/device_next/src/main.c +++ b/tests/subsys/usb/device_next/src/main.c @@ -19,11 +19,11 @@ LOG_MODULE_REGISTER(usb_test, LOG_LEVEL_INF); USBD_CONFIGURATION_DEFINE(test_fs_config, USB_SCD_SELF_POWERED | USB_SCD_REMOTE_WAKEUP, - 200); + 200, NULL); USBD_CONFIGURATION_DEFINE(test_hs_config, USB_SCD_SELF_POWERED | USB_SCD_REMOTE_WAKEUP, - 200); + 200, NULL); USBD_DESC_LANG_DEFINE(test_lang); From 476ad4fd34ac5a58d19acbc09db59aa4c556d922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 18 Jun 2024 11:33:25 +0200 Subject: [PATCH 18/39] [nrf fromtree] drivers: udc_dwc2: Respect maximum fifo depth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When dynamic fifo sizing is enabled the driver can assign fifo size lower or equal to the reset value. Software must respect the maximum sizes because fifo width registers are optimized during synthesis and the larger values simply get discarded. Use 16-bit values for FIFO depths because maximum FIFO depth the vendor can configure the DWC2 otg is 32768. Signed-off-by: Tomasz Moń (cherry picked from commit f0677e5a3b77c16c0abdd177ee37ab9bb220e109) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 67 ++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 6ca435f4537..d95be3e022c 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -64,11 +64,12 @@ struct udc_dwc2_data { uint32_t ghwcfg1; uint32_t enumspd; uint32_t txf_set; - uint32_t grxfsiz; - uint32_t dfifodepth; uint32_t max_xfersize; uint32_t max_pktcnt; uint32_t tx_len[16]; + uint16_t dfifodepth; + uint16_t rxfifo_depth; + uint16_t max_txfifo_depth[16]; unsigned int dynfifosizing : 1; unsigned int bufferdma : 1; /* Number of endpoints including control endpoint */ @@ -1155,11 +1156,17 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, txfaddr = dwc2_get_txfdep(dev, ep_idx - 2) + dwc2_get_txfaddr(dev, ep_idx - 2); } else { - txfaddr = UDC_DWC2_FIFO0_DEPTH + priv->grxfsiz; + txfaddr = priv->rxfifo_depth + + MAX(UDC_DWC2_FIFO0_DEPTH, priv->max_txfifo_depth[0]); + } + + /* Make sure to not set TxFIFO greater than hardware allows */ + txfdep = DIV_ROUND_UP(cfg->mps, 4U); + if (txfdep > priv->max_txfifo_depth[ep_idx]) { + return -ENOMEM; } /* Set FIFO depth (32-bit words) and address */ - txfdep = cfg->mps / 4U; dwc2_set_txf(dev, ep_idx - 1, txfdep, txfaddr); } else { txfdep = dwc2_get_txfdep(dev, ep_idx - 1); @@ -1186,8 +1193,6 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, static int dwc2_ep_control_enable(const struct device *dev, struct udc_ep_config *const cfg) { - struct usb_dwc2_reg *const base = dwc2_get_base(dev); - struct udc_dwc2_data *const priv = udc_get_private(dev); mem_addr_t dxepctl0_reg; uint32_t dxepctl0; @@ -1214,21 +1219,6 @@ static int dwc2_ep_control_enable(const struct device *dev, dxepctl0 |= USB_DWC2_DEPCTL_USBACTEP; - /* - * The following applies to the Control IN endpoint only. - * - * Set endpoint 0 TxFIFO depth when dynfifosizing is enabled. - * Note that only dedicated mode is supported at this time. - */ - if (cfg->addr == USB_CONTROL_EP_IN && priv->dynfifosizing) { - uint32_t gnptxfsiz; - - gnptxfsiz = usb_dwc2_set_gnptxfsiz_nptxfdep(UDC_DWC2_FIFO0_DEPTH) | - usb_dwc2_set_gnptxfsiz_nptxfstaddr(priv->grxfsiz); - - sys_write32(gnptxfsiz, (mem_addr_t)&base->gnptxfsiz); - } - if (cfg->addr == USB_CONTROL_EP_OUT) { int ret; @@ -1740,6 +1730,7 @@ static int udc_dwc2_init_controller(const struct device *dev) const struct udc_dwc2_config *const config = dev->config; struct udc_dwc2_data *const priv = udc_get_private(dev); struct usb_dwc2_reg *const base = config->base; + mem_addr_t grxfsiz_reg = (mem_addr_t)&base->grxfsiz; mem_addr_t gahbcfg_reg = (mem_addr_t)&base->gahbcfg; mem_addr_t gusbcfg_reg = (mem_addr_t)&base->gusbcfg; mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg; @@ -1749,6 +1740,7 @@ static int udc_dwc2_init_controller(const struct device *dev) uint32_t ghwcfg2; uint32_t ghwcfg3; uint32_t ghwcfg4; + uint32_t val; int ret; ret = dwc2_core_soft_reset(dev); @@ -1892,15 +1884,40 @@ static int udc_dwc2_init_controller(const struct device *dev) LOG_DBG("Number of OUT endpoints %u", priv->outeps); + /* Read and store all TxFIFO depths because Programmed FIFO Depths must + * not exceed the power-on values. + */ + val = sys_read32((mem_addr_t)&base->gnptxfsiz); + priv->max_txfifo_depth[0] = usb_dwc2_get_gnptxfsiz_nptxfdep(val); + for (uint8_t i = 1; i < priv->ineps; i++) { + priv->max_txfifo_depth[i] = dwc2_get_txfdep(dev, i - 1); + } + + priv->rxfifo_depth = usb_dwc2_get_grxfsiz(sys_read32(grxfsiz_reg)); + if (priv->dynfifosizing) { - priv->grxfsiz = UDC_DWC2_GRXFSIZ_DEFAULT + priv->outeps * 2U; - sys_write32(usb_dwc2_set_grxfsiz(priv->grxfsiz), (mem_addr_t)&base->grxfsiz); + uint32_t gnptxfsiz; + + /* Driver does not dynamically resize RxFIFO so there is no need + * to store reset value. Read the reset value and make sure that + * the programmed value is not greater than what driver sets. + */ + priv->rxfifo_depth = MIN(priv->rxfifo_depth, + UDC_DWC2_GRXFSIZ_DEFAULT + priv->outeps * 2U); + sys_write32(usb_dwc2_set_grxfsiz(priv->rxfifo_depth), grxfsiz_reg); + + /* Set TxFIFO 0 depth */ + val = MAX(UDC_DWC2_FIFO0_DEPTH, priv->max_txfifo_depth[0]); + gnptxfsiz = usb_dwc2_set_gnptxfsiz_nptxfdep(val) | + usb_dwc2_set_gnptxfsiz_nptxfstaddr(priv->rxfifo_depth); + + sys_write32(gnptxfsiz, (mem_addr_t)&base->gnptxfsiz); } - LOG_DBG("RX FIFO size %u bytes", priv->grxfsiz * 4); + LOG_DBG("RX FIFO size %u bytes", priv->rxfifo_depth * 4); for (uint8_t i = 1U; i < priv->ineps; i++) { LOG_DBG("TX FIFO%u depth %u addr %u", - i, dwc2_get_txfdep(dev, i), dwc2_get_txfaddr(dev, i)); + i, priv->max_txfifo_depth[i], dwc2_get_txfaddr(dev, i)); } if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, From c971f05558173b43179b8f3837d6803537d07a70 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Fri, 7 Jun 2024 19:35:45 +0200 Subject: [PATCH 19/39] [nrf fromtree] usb: device_next: introduce UDC_BUF_POOL_*_DEFINE macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce UDC_BUF_POOL_*_DEFINE macros based on NET_BUF_POOL_*_DEFINE macros but use our own version of alloc and unref callbacks to get buffers with specific alignment and granularity. Also, do not use ref callback because it breaks alignment. Also introduces helper macros for defining and checking UDC driver-compliant static buffers. Signed-off-by: Johann Fischer (cherry picked from commit 67caae3aca0847dc3e33a67ace5ccc0405a661d2) Signed-off-by: Tomasz Moń --- doc/connectivity/usb/device_next/api/udc.rst | 2 + drivers/usb/udc/Kconfig | 7 + drivers/usb/udc/udc_common.c | 33 +++- include/zephyr/drivers/usb/udc.h | 2 +- include/zephyr/drivers/usb/udc_buf.h | 154 ++++++++++++++++++ include/zephyr/usb/usbd.h | 2 +- samples/subsys/usb/hid-keyboard/src/main.c | 4 +- samples/subsys/usb/hid-mouse/src/main.c | 4 +- .../usb/uac2_explicit_feedback/src/main.c | 3 +- subsys/usb/device_next/class/bt_hci.c | 6 +- subsys/usb/device_next/class/usbd_cdc_acm.c | 6 +- subsys/usb/device_next/class/usbd_cdc_ecm.c | 8 +- subsys/usb/device_next/class/usbd_hid.c | 2 + .../usb/device_next/class/usbd_hid_macros.h | 2 +- subsys/usb/device_next/class/usbd_msc.c | 6 +- subsys/usb/device_next/class/usbd_uac2.c | 4 +- 16 files changed, 222 insertions(+), 23 deletions(-) create mode 100644 include/zephyr/drivers/usb/udc_buf.h diff --git a/doc/connectivity/usb/device_next/api/udc.rst b/doc/connectivity/usb/device_next/api/udc.rst index 1e62c6a66ac..752cca59479 100644 --- a/doc/connectivity/usb/device_next/api/udc.rst +++ b/doc/connectivity/usb/device_next/api/udc.rst @@ -16,3 +16,5 @@ API reference ************* .. doxygengroup:: udc_api + +.. doxygengroup:: udc_buf diff --git a/drivers/usb/udc/Kconfig b/drivers/usb/udc/Kconfig index b4d1df0114a..bd6f71eb731 100644 --- a/drivers/usb/udc/Kconfig +++ b/drivers/usb/udc/Kconfig @@ -25,6 +25,13 @@ config UDC_BUF_POOL_SIZE help Total amount of memory available for UDC requests. +config UDC_BUF_FORCE_NOCACHE + bool "Place the buffer pools in the nocache memory region" + depends on NOCACHE_MEMORY && DCACHE + help + Place the buffer pools in the nocache memory region if the driver + cannot handle buffers in cached memory. + config UDC_WORKQUEUE bool "Use a dedicate work queue for UDC drivers" help diff --git a/drivers/usb/udc/udc_common.c b/drivers/usb/udc/udc_common.c index eeed077ebc4..009fb085a5d 100644 --- a/drivers/usb/udc/udc_common.c +++ b/drivers/usb/udc/udc_common.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "udc_common.h" #include @@ -20,9 +21,39 @@ #endif LOG_MODULE_REGISTER(udc, CONFIG_UDC_DRIVER_LOG_LEVEL); +static inline uint8_t *udc_pool_data_alloc(struct net_buf *const buf, + size_t *const size, k_timeout_t timeout) +{ + struct net_buf_pool *const buf_pool = net_buf_pool_get(buf->pool_id); + struct k_heap *const pool = buf_pool->alloc->alloc_data; + void *b; + + *size = ROUND_UP(*size, UDC_BUF_GRANULARITY); + b = k_heap_aligned_alloc(pool, UDC_BUF_ALIGN, *size, timeout); + if (b == NULL) { + *size = 0; + return NULL; + } + + return b; +} + +static inline void udc_pool_data_unref(struct net_buf *buf, uint8_t *const data) +{ + struct net_buf_pool *buf_pool = net_buf_pool_get(buf->pool_id); + struct k_heap *pool = buf_pool->alloc->alloc_data; + + k_heap_free(pool, data); +} + +const struct net_buf_data_cb net_buf_dma_cb = { + .alloc = udc_pool_data_alloc, + .unref = udc_pool_data_unref, +}; + static inline void udc_buf_destroy(struct net_buf *buf); -NET_BUF_POOL_VAR_DEFINE(udc_ep_pool, +UDC_BUF_POOL_VAR_DEFINE(udc_ep_pool, CONFIG_UDC_BUF_COUNT, CONFIG_UDC_BUF_POOL_SIZE, sizeof(struct udc_buf_info), udc_buf_destroy); diff --git a/include/zephyr/drivers/usb/udc.h b/include/zephyr/drivers/usb/udc.h index 2e4d4dbcf32..7d7c8ee07c5 100644 --- a/include/zephyr/drivers/usb/udc.h +++ b/include/zephyr/drivers/usb/udc.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include diff --git a/include/zephyr/drivers/usb/udc_buf.h b/include/zephyr/drivers/usb/udc_buf.h new file mode 100644 index 00000000000..aae4cf28fba --- /dev/null +++ b/include/zephyr/drivers/usb/udc_buf.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Buffers for USB device support + */ + +#ifndef ZEPHYR_INCLUDE_UDC_BUF_H +#define ZEPHYR_INCLUDE_UDC_BUF_H + +#include +#include + +#if defined(CONFIG_DCACHE) && !defined(CONFIG_UDC_BUF_FORCE_NOCACHE) +/* + * Here we try to get DMA-safe buffers, but we lack a consistent source of + * information about data cache properties, such as line cache size, and a + * consistent source of information about what part of memory is DMA'able. + * For now, we simply assume that all available memory is DMA'able and use + * Kconfig option DCACHE_LINE_SIZE for alignment and granularity. + */ +#define Z_UDC_BUF_ALIGN CONFIG_DCACHE_LINE_SIZE +#define Z_UDC_BUF_GRANULARITY CONFIG_DCACHE_LINE_SIZE +#else +/* + * Default alignment and granularity to pointer size if the platform does not + * have a data cache or buffers are placed in nocache memory region. + */ +#define Z_UDC_BUF_ALIGN sizeof(void *) +#define Z_UDC_BUF_GRANULARITY sizeof(void *) +#endif + +/** + * @brief Buffer macros and definitions used in USB device support + * @defgroup udc_buf Buffer macros and definitions used in USB device support + * @ingroup usb + * @{ + */ + +/** Buffer alignment required by the UDC driver */ +#define UDC_BUF_ALIGN Z_UDC_BUF_ALIGN + +/** Buffer granularity required by the UDC driver */ +#define UDC_BUF_GRANULARITY Z_UDC_BUF_GRANULARITY + +/** + * @brief Define a UDC driver-compliant static buffer + * + * This macro should be used if the application defines its own buffers to be + * used for USB transfers. + * + * @param name Buffer name + * @param size Buffer size + */ +#define UDC_STATIC_BUF_DEFINE(name, size) \ + static uint8_t __aligned(UDC_BUF_ALIGN) name[ROUND_UP(size, UDC_BUF_GRANULARITY)]; + +/** + * @brief Verify that the buffer is aligned as required by the UDC driver + * + * @see IS_ALIGNED + * + * @param buf Buffer pointer + */ +#define IS_UDC_ALIGNED(buf) IS_ALIGNED(buf, UDC_BUF_ALIGN) + +/** + * @cond INTERNAL_HIDDEN + */ +#define UDC_HEAP_DEFINE(name, bytes, in_section) \ + uint8_t in_section __aligned(UDC_BUF_ALIGN) \ + kheap_##name[MAX(bytes, Z_HEAP_MIN_SIZE)]; \ + STRUCT_SECTION_ITERABLE(k_heap, name) = { \ + .heap = { \ + .init_mem = kheap_##name, \ + .init_bytes = MAX(bytes, Z_HEAP_MIN_SIZE), \ + }, \ + } + +#define UDC_K_HEAP_DEFINE(name, size) \ + COND_CODE_1(CONFIG_UDC_BUF_FORCE_NOCACHE, \ + (UDC_HEAP_DEFINE(name, size, __nocache)), \ + (UDC_HEAP_DEFINE(name, size, __noinit))) + +extern const struct net_buf_data_cb net_buf_dma_cb; +/** @endcond */ + +/** + * @brief Define a new pool for UDC buffers with variable-size payloads + * + * This macro is similar to `NET_BUF_POOL_VAR_DEFINE`, but provides buffers + * with alignment and granularity suitable for use by UDC driver. + * + * @see NET_BUF_POOL_VAR_DEFINE + * + * @param pname Name of the pool variable. + * @param count Number of buffers in the pool. + * @param size Maximum data payload per buffer. + * @param ud_size User data space to reserve per buffer. + * @param fdestroy Optional destroy callback when buffer is freed. + */ +#define UDC_BUF_POOL_VAR_DEFINE(pname, count, size, ud_size, fdestroy) \ + _NET_BUF_ARRAY_DEFINE(pname, count, ud_size); \ + UDC_K_HEAP_DEFINE(net_buf_mem_pool_##pname, size); \ + static const struct net_buf_data_alloc net_buf_data_alloc_##pname = { \ + .cb = &net_buf_dma_cb, \ + .alloc_data = &net_buf_mem_pool_##pname, \ + .max_alloc_size = 0, \ + }; \ + static STRUCT_SECTION_ITERABLE(net_buf_pool, pname) = \ + NET_BUF_POOL_INITIALIZER(pname, &net_buf_data_alloc_##pname, \ + _net_buf_##pname, count, ud_size, \ + fdestroy) + +/** + * @brief Define a new pool for UDC buffers based on fixed-size data + * + * This macro is similar to `NET_BUF_POOL_DEFINE`, but provides buffers + * with alignment and granularity suitable for use by UDC driver. + * + * @see NET_BUF_POOL_DEFINE + + * @param pname Name of the pool variable. + * @param count Number of buffers in the pool. + * @param size Maximum data payload per buffer. + * @param ud_size User data space to reserve per buffer. + * @param fdestroy Optional destroy callback when buffer is freed. + */ +#define UDC_BUF_POOL_DEFINE(pname, count, size, ud_size, fdestroy) \ + _NET_BUF_ARRAY_DEFINE(pname, count, ud_size); \ + static uint8_t __nocache __aligned(UDC_BUF_ALIGN) \ + net_buf_data_##pname[count][size]; \ + static const struct net_buf_pool_fixed net_buf_fixed_##pname = { \ + .data_pool = (uint8_t *)net_buf_data_##pname, \ + }; \ + static const struct net_buf_data_alloc net_buf_fixed_alloc_##pname = { \ + .cb = &net_buf_fixed_cb, \ + .alloc_data = (void *)&net_buf_fixed_##pname, \ + .max_alloc_size = size, \ + }; \ + static STRUCT_SECTION_ITERABLE(net_buf_pool, pname) = \ + NET_BUF_POOL_INITIALIZER(pname, &net_buf_fixed_alloc_##pname, \ + _net_buf_##pname, count, ud_size, \ + fdestroy) + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_UDC_BUF_H */ diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 9a0c0a35698..b4a5e20fcc1 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/samples/subsys/usb/hid-keyboard/src/main.c b/samples/subsys/usb/hid-keyboard/src/main.c index c10ae9e468b..a9e3bcd46dc 100644 --- a/samples/subsys/usb/hid-keyboard/src/main.c +++ b/samples/subsys/usb/hid-keyboard/src/main.c @@ -51,7 +51,7 @@ struct kb_event { K_MSGQ_DEFINE(kb_msgq, sizeof(struct kb_event), 2, 1); -static uint8_t __aligned(sizeof(void *)) report[KB_REPORT_COUNT]; +UDC_STATIC_BUF_DEFINE(report, KB_REPORT_COUNT); static uint32_t kb_duration; static bool kb_ready; @@ -273,7 +273,7 @@ int main(void) continue; } - ret = hid_device_submit_report(hid_dev, sizeof(report), report); + ret = hid_device_submit_report(hid_dev, KB_REPORT_COUNT, report); if (ret) { LOG_ERR("HID submit report error, %d", ret); } diff --git a/samples/subsys/usb/hid-mouse/src/main.c b/samples/subsys/usb/hid-mouse/src/main.c index 45021d5a4bd..c7888b5fdfd 100644 --- a/samples/subsys/usb/hid-mouse/src/main.c +++ b/samples/subsys/usb/hid-mouse/src/main.c @@ -174,11 +174,11 @@ int main(void) } while (true) { - uint8_t __aligned(sizeof(void *)) report[MOUSE_REPORT_COUNT]; + UDC_STATIC_BUF_DEFINE(report, MOUSE_REPORT_COUNT); k_msgq_get(&mouse_msgq, &report, K_FOREVER); - ret = hid_int_ep_write(hid_dev, report, sizeof(report), NULL); + ret = hid_int_ep_write(hid_dev, report, MOUSE_REPORT_COUNT, NULL); if (ret) { LOG_ERR("HID write error, %d", ret); } else { diff --git a/samples/subsys/usb/uac2_explicit_feedback/src/main.c b/samples/subsys/usb/uac2_explicit_feedback/src/main.c index a09cb2d5d85..e448416c745 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/src/main.c +++ b/samples/subsys/usb/uac2_explicit_feedback/src/main.c @@ -36,7 +36,8 @@ LOG_MODULE_REGISTER(uac2_sample, LOG_LEVEL_INF); * when USB host decides to perform rapid terminal enable/disable cycles. */ #define I2S_BUFFERS_COUNT 7 -K_MEM_SLAB_DEFINE_STATIC(i2s_tx_slab, MAX_BLOCK_SIZE, I2S_BUFFERS_COUNT, 4); +K_MEM_SLAB_DEFINE_STATIC(i2s_tx_slab, ROUND_UP(MAX_BLOCK_SIZE, UDC_BUF_GRANULARITY), + I2S_BUFFERS_COUNT, UDC_BUF_ALIGN); struct usb_i2s_ctx { const struct device *i2s_dev; diff --git a/subsys/usb/device_next/class/bt_hci.c b/subsys/usb/device_next/class/bt_hci.c index fb45713440d..273a9104062 100644 --- a/subsys/usb/device_next/class/bt_hci.c +++ b/subsys/usb/device_next/class/bt_hci.c @@ -73,9 +73,9 @@ static K_FIFO_DEFINE(bt_hci_tx_queue); * REVISE: global (bulk, interrupt, iso) specific pools would be more * RAM usage efficient. */ -NET_BUF_POOL_FIXED_DEFINE(bt_hci_ep_pool, - 3, 512, - sizeof(struct udc_buf_info), NULL); +UDC_BUF_POOL_DEFINE(bt_hci_ep_pool, + 3, 512, + sizeof(struct udc_buf_info), NULL); /* HCI RX/TX threads */ static K_KERNEL_STACK_DEFINE(rx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE); static struct k_thread rx_thread_data; diff --git a/subsys/usb/device_next/class/usbd_cdc_acm.c b/subsys/usb/device_next/class/usbd_cdc_acm.c index 6241914887b..7c2b6504284 100644 --- a/subsys/usb/device_next/class/usbd_cdc_acm.c +++ b/subsys/usb/device_next/class/usbd_cdc_acm.c @@ -32,9 +32,9 @@ #endif LOG_MODULE_REGISTER(usbd_cdc_acm, CONFIG_USBD_CDC_ACM_LOG_LEVEL); -NET_BUF_POOL_FIXED_DEFINE(cdc_acm_ep_pool, - DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, - 512, sizeof(struct udc_buf_info), NULL); +UDC_BUF_POOL_DEFINE(cdc_acm_ep_pool, + DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, + 512, sizeof(struct udc_buf_info), NULL); #define CDC_ACM_DEFAULT_LINECODING {sys_cpu_to_le32(115200), 0, 0, 8} #define CDC_ACM_DEFAULT_INT_EP_MPS 16 diff --git a/subsys/usb/device_next/class/usbd_cdc_ecm.c b/subsys/usb/device_next/class/usbd_cdc_ecm.c index d23c1dd3fe7..3c3072182be 100644 --- a/subsys/usb/device_next/class/usbd_cdc_ecm.c +++ b/subsys/usb/device_next/class/usbd_cdc_ecm.c @@ -36,10 +36,10 @@ enum { * Transfers through two endpoints proceed in a synchronous manner, * with maximum block of NET_ETH_MAX_FRAME_SIZE. */ -NET_BUF_POOL_FIXED_DEFINE(cdc_ecm_ep_pool, - DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, - NET_ETH_MAX_FRAME_SIZE, - sizeof(struct udc_buf_info), NULL); +UDC_BUF_POOL_DEFINE(cdc_ecm_ep_pool, + DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, + NET_ETH_MAX_FRAME_SIZE, + sizeof(struct udc_buf_info), NULL); struct cdc_ecm_notification { union { diff --git a/subsys/usb/device_next/class/usbd_hid.c b/subsys/usb/device_next/class/usbd_hid.c index 1b39aa182cf..d32715f6ed4 100644 --- a/subsys/usb/device_next/class/usbd_hid.c +++ b/subsys/usb/device_next/class/usbd_hid.c @@ -496,6 +496,8 @@ static struct net_buf *hid_buf_alloc_ext(struct hid_device_data *const ddata, struct net_buf *buf = NULL; struct udc_buf_info *bi; + __ASSERT(IS_UDC_ALIGNED(data), "Application provided unaligned buffer"); + buf = net_buf_alloc_with_data(ddata->pool_in, data, size, K_NO_WAIT); if (!buf) { return NULL; diff --git a/subsys/usb/device_next/class/usbd_hid_macros.h b/subsys/usb/device_next/class/usbd_hid_macros.h index c00a18b227a..ec6c23fa2b8 100644 --- a/subsys/usb/device_next/class/usbd_hid_macros.h +++ b/subsys/usb/device_next/class/usbd_hid_macros.h @@ -1156,7 +1156,7 @@ #define HID_OUT_POOL_DEFINE(n) \ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, out_report_size), \ - (NET_BUF_POOL_DEFINE(hid_buf_pool_out_##n, \ + (UDC_BUF_POOL_DEFINE(hid_buf_pool_out_##n, \ CONFIG_USBD_HID_OUT_BUF_COUNT, \ DT_INST_PROP(n, out_report_size), \ sizeof(struct udc_buf_info), NULL)), \ diff --git a/subsys/usb/device_next/class/usbd_msc.c b/subsys/usb/device_next/class/usbd_msc.c index a608a2da61a..b3a7771fa9f 100644 --- a/subsys/usb/device_next/class/usbd_msc.c +++ b/subsys/usb/device_next/class/usbd_msc.c @@ -63,9 +63,9 @@ struct CSW { /* Can be 64 if device is not High-Speed capable */ #define MSC_BUF_SIZE 512 -NET_BUF_POOL_FIXED_DEFINE(msc_ep_pool, - MSC_NUM_INSTANCES * 2, MSC_BUF_SIZE, - sizeof(struct udc_buf_info), NULL); +UDC_BUF_POOL_DEFINE(msc_ep_pool, + MSC_NUM_INSTANCES * 2, MSC_BUF_SIZE, + sizeof(struct udc_buf_info), NULL); struct msc_event { struct usbd_class_data *c_data; diff --git a/subsys/usb/device_next/class/usbd_uac2.c b/subsys/usb/device_next/class/usbd_uac2.c index 3229be15f49..5e861689a84 100644 --- a/subsys/usb/device_next/class/usbd_uac2.c +++ b/subsys/usb/device_next/class/usbd_uac2.c @@ -40,7 +40,7 @@ LOG_MODULE_REGISTER(usbd_uac2, CONFIG_USBD_UAC2_LOG_LEVEL); * "wasted memory" here is likely to be smaller than the memory overhead for * more complex "only as much as needed" schemes (e.g. heap). */ -NET_BUF_POOL_DEFINE(uac2_pool, UAC2_NUM_ENDPOINTS, 6, +UDC_BUF_POOL_DEFINE(uac2_pool, UAC2_NUM_ENDPOINTS, 6, sizeof(struct udc_buf_info), NULL); /* 5.2.2 Control Request Layout */ @@ -204,6 +204,8 @@ uac2_buf_alloc(const uint8_t ep, void *data, uint16_t size) struct net_buf *buf = NULL; struct udc_buf_info *bi; + __ASSERT(IS_UDC_ALIGNED(data), "Application provided unaligned buffer"); + buf = net_buf_alloc_with_data(&uac2_pool, data, size, K_NO_WAIT); if (!buf) { return NULL; From 046ce3074adc3a1bb0a209c1449fa3e6176cb32a Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 20 Jun 2024 22:25:53 +0200 Subject: [PATCH 20/39] [nrf fromtree] drivers: udc_mcux_ehci: remove bounce buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UDC_BUF_POOL_*_DEFINE macro will place buffer pool in __nocache section if NOCACHE_MEMORY is enabled. Signed-off-by: Johann Fischer (cherry picked from commit 9bf32320f2e025a57a5a3e1e6ebd46aea60b62b2) Signed-off-by: Tomasz Moń --- drivers/usb/udc/Kconfig.mcux | 1 + drivers/usb/udc/udc_mcux_ehci.c | 56 --------------------------------- 2 files changed, 1 insertion(+), 56 deletions(-) diff --git a/drivers/usb/udc/Kconfig.mcux b/drivers/usb/udc/Kconfig.mcux index f36eab115b0..590e772600e 100644 --- a/drivers/usb/udc/Kconfig.mcux +++ b/drivers/usb/udc/Kconfig.mcux @@ -6,6 +6,7 @@ config UDC_NXP_EHCI default y depends on DT_HAS_NXP_EHCI_ENABLED select NOCACHE_MEMORY if HAS_MCUX_CACHE && CPU_HAS_DCACHE + imply UDC_BUF_FORCE_NOCACHE help NXP MCUX USB Device Controller Driver for EHCI. diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index 80d92ac1de9..451066bbbf5 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -52,37 +52,6 @@ struct udc_mcux_data { uint8_t controller_id; /* 0xFF is invalid value */ }; -/* TODO: implement the cache maintenance - * solution1: Use the non-cached buf to do memcpy before/after giving buffer to usb controller. - * solution2: Use cache API to flush/invalid cache. but it needs the given buffer is - * cache line size aligned and the buffer range cover multiple of cache line size block. - * Need to change the usb stack to implement it, will try to implement it later. - */ -#if defined(CONFIG_NOCACHE_MEMORY) -K_HEAP_DEFINE_NOCACHE(mcux_packet_alloc_pool, USB_DEVICE_CONFIG_ENDPOINTS * 2u * 1024u); - -/* allocate non-cached buffer for usb */ -static void *udc_mcux_nocache_alloc(uint32_t size) -{ - void *p = (void *)k_heap_alloc(&mcux_packet_alloc_pool, size, K_NO_WAIT); - - if (p != NULL) { - (void)memset(p, 0, size); - } - - return p; -} - -/* free the allocated non-cached buffer */ -static void udc_mcux_nocache_free(void *p) -{ - if (p == NULL) { - return; - } - k_heap_free(&mcux_packet_alloc_pool, p); -} -#endif - static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command, void *param) { @@ -128,21 +97,12 @@ static int udc_mcux_ep_feed(const struct device *dev, if (USB_EP_DIR_IS_OUT(cfg->addr)) { len = net_buf_tailroom(buf); -#if defined(CONFIG_NOCACHE_MEMORY) - data = (len == 0 ? NULL : udc_mcux_nocache_alloc(len)); -#else data = net_buf_tail(buf); -#endif status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, cfg->addr, data, len); } else { len = buf->len; -#if defined(CONFIG_NOCACHE_MEMORY) - data = (len == 0 ? NULL : udc_mcux_nocache_alloc(len)); - memcpy(data, buf->data, len); -#else data = buf->data; -#endif status = mcux_if->deviceSend(priv->mcux_device.controllerHandle, cfg->addr, data, len); } @@ -258,10 +218,6 @@ static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *b uint32_t len; len = MIN(net_buf_tailroom(buf), mcux_len); -#if defined(CONFIG_NOCACHE_MEMORY) - memcpy(net_buf_tail(buf), mcux_buf, len); - udc_mcux_nocache_free(mcux_buf); -#endif net_buf_add(buf, len); if (udc_ctrl_stage_is_status_out(dev)) { /* Update to next stage of control transfer */ @@ -289,9 +245,6 @@ static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *bu len = MIN(buf->len, mcux_len); buf->data += len; buf->len -= len; -#if defined(CONFIG_NOCACHE_MEMORY) - udc_mcux_nocache_free(mcux_buf); -#endif if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { @@ -324,9 +277,6 @@ static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep, buf->data += len; buf->len -= len; -#if defined(CONFIG_NOCACHE_MEMORY) - udc_mcux_nocache_free(mcux_buf); -#endif err = udc_submit_ep_event(dev, buf, 0); udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); @@ -340,14 +290,8 @@ static int udc_mcux_handler_non_ctrl_out(const struct device *dev, uint8_t ep, uint32_t len; len = MIN(net_buf_tailroom(buf), mcux_len); -#if defined(CONFIG_NOCACHE_MEMORY) - memcpy(net_buf_tail(buf), mcux_buf, len); -#endif net_buf_add(buf, len); -#if defined(CONFIG_NOCACHE_MEMORY) - udc_mcux_nocache_free(mcux_buf); -#endif err = udc_submit_ep_event(dev, buf, 0); udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); From 939d72dfecd12869c48ad8e19c47a379acf94154 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Tue, 2 Jul 2024 22:59:48 +0200 Subject: [PATCH 21/39] [nrf fromtree] drivers: udc_dwc2: enable Internal DMA support by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove BUILD_ASSERT for Internal DMA and DCACHE if both are enabled, and enable Internal DMA support by default. Signed-off-by: Johann Fischer (cherry picked from commit 76fadf90f790539d414ed77fd2be50442936c9e6) Signed-off-by: Tomasz Moń --- drivers/usb/udc/Kconfig.dwc2 | 2 +- drivers/usb/udc/udc_dwc2.c | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/usb/udc/Kconfig.dwc2 b/drivers/usb/udc/Kconfig.dwc2 index a14481794c7..fc38e07e60d 100644 --- a/drivers/usb/udc/Kconfig.dwc2 +++ b/drivers/usb/udc/Kconfig.dwc2 @@ -12,7 +12,7 @@ config UDC_DWC2 config UDC_DWC2_DMA bool "DWC2 USB DMA support" - default n + default y depends on UDC_DWC2 help Enable Buffer DMA if DWC2 USB controller supports Internal DMA. diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index d95be3e022c..eefba16ecda 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -2262,14 +2262,6 @@ static const struct udc_api udc_dwc2_api = { .ghwcfg4 = DT_INST_PROP(n, ghwcfg4), \ }; \ \ - IF_ENABLED(CONFIG_DCACHE, (BUILD_ASSERT( \ - !IS_ENABLED(CONFIG_UDC_DWC2_DMA) || \ - (DT_INST_PROP(n, ghwcfg2) & USB_DWC2_GHWCFG2_OTGARCH_MASK) != \ - (USB_DWC2_GHWCFG2_OTGARCH_INTERNALDMA << \ - USB_DWC2_GHWCFG2_OTGARCH_POS), \ - "USB stack does not provide D-Cache aware buffers yet"); \ - )) \ - \ static struct udc_dwc2_data udc_priv_##n = { \ }; \ \ From e2f5b5d9bfa86e3e3cd6ccee8f8b68e7e5272be7 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Mon, 22 Jul 2024 13:26:31 +0200 Subject: [PATCH 22/39] [nrf fromtree] drivers: udc_stm32: remove wrong header and fix udc_ep_enable() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver includes wrong header zephyr/usb/usb_device.h and uses defines from include/zephyr/drivers/usb/usb_dc.h. Also fix udc_ep_enable() implementation in general. HAL_PCD_EP_Open() takes the ep_type parameter as uint8_t integer type and the shim driver should not just pass int type. It is recommended that drivers use ep_cfg or cfg for struct udc_ep_config, fix this as well. Signed-off-by: Johann Fischer (cherry picked from commit 043aa837fdadeb319fb50d22a509fcfb19d9fbe0) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_stm32.c | 50 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index e86893b1b18..e91367224dc 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "udc_common.h" @@ -603,44 +602,43 @@ static int udc_stm32_host_wakeup(const struct device *dev) return 0; } -static inline int eptype2hal(enum usb_dc_ep_transfer_type eptype) -{ - switch (eptype) { - case USB_DC_EP_CONTROL: - return EP_TYPE_CTRL; - case USB_DC_EP_ISOCHRONOUS: - return EP_TYPE_ISOC; - case USB_DC_EP_BULK: - return EP_TYPE_BULK; - case USB_DC_EP_INTERRUPT: - return EP_TYPE_INTR; - default: - return -EINVAL; - } - - return -EINVAL; -} - static int udc_stm32_ep_enable(const struct device *dev, - struct udc_ep_config *ep) + struct udc_ep_config *ep_cfg) { - enum usb_dc_ep_transfer_type type = ep->attributes & USB_EP_TRANSFER_TYPE_MASK; struct udc_stm32_data *priv = udc_get_private(dev); HAL_StatusTypeDef status; + uint8_t ep_type; int ret; - LOG_DBG("Enable ep 0x%02x", ep->addr); + LOG_DBG("Enable ep 0x%02x", ep_cfg->addr); + + switch (ep_cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_CONTROL: + ep_type = EP_TYPE_CTRL; + break; + case USB_EP_TYPE_BULK: + ep_type = EP_TYPE_BULK; + break; + case USB_EP_TYPE_INTERRUPT: + ep_type = EP_TYPE_INTR; + break; + case USB_EP_TYPE_ISO: + ep_type = EP_TYPE_ISOC; + break; + default: + return -EINVAL; + } - ret = udc_stm32_ep_mem_config(dev, ep, true); + ret = udc_stm32_ep_mem_config(dev, ep_cfg, true); if (ret) { return ret; } - status = HAL_PCD_EP_Open(&priv->pcd, ep->addr, ep->mps, - eptype2hal(type)); + status = HAL_PCD_EP_Open(&priv->pcd, ep_cfg->addr, ep_cfg->mps, + ep_type); if (status != HAL_OK) { LOG_ERR("HAL_PCD_EP_Open failed(0x%02x), %d", - ep->addr, (int)status); + ep_cfg->addr, (int)status); return -EIO; } From 3592f826671ee4c6cf2aab1e880e1d3c118d73e5 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Sat, 27 Jul 2024 18:20:01 +0100 Subject: [PATCH 23/39] [nrf fromtree] input: add a user_data pointer to the callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a void *user_data pointer to the input callback structure. This is useful for driver to get back the driver data structure and avoid defining wrapper functions. Signed-off-by: Fabio Baltieri (cherry picked from commit 716fa268f4d448a59e34712ce596e3a46079d47e) Signed-off-by: Tomasz Moń --- drivers/kscan/kscan_input.c | 11 ++++------ include/zephyr/input/input.h | 8 ++++++-- modules/lvgl/include/lvgl_common_input.h | 7 ++----- modules/lvgl/input/lvgl_button_input.c | 3 ++- modules/lvgl/input/lvgl_encoder_input.c | 3 ++- modules/lvgl/input/lvgl_keypad_input.c | 3 ++- modules/lvgl/input/lvgl_pointer_input.c | 3 ++- samples/subsys/usb/hid-keyboard/src/main.c | 6 ++++-- samples/subsys/usb/hid-mouse/src/main.c | 6 ++++-- subsys/input/input.c | 2 +- subsys/input/input_keymap.c | 10 ++++------ subsys/input/input_longpress.c | 11 ++++------ subsys/input/input_utils.c | 12 +++++++---- .../drivers/input/gpio_kbd_matrix/src/main.c | 4 ++-- tests/drivers/input/gpio_keys/src/main.c | 4 ++-- tests/drivers/input/kbd_matrix/src/main.c | 4 ++-- tests/subsys/input/api/src/main.c | 20 +++++++++---------- tests/subsys/input/longpress/src/main.c | 8 ++++---- 18 files changed, 65 insertions(+), 60 deletions(-) diff --git a/drivers/kscan/kscan_input.c b/drivers/kscan/kscan_input.c index 76df309b96b..17481fc57d6 100644 --- a/drivers/kscan/kscan_input.c +++ b/drivers/kscan/kscan_input.c @@ -23,8 +23,9 @@ struct kscan_input_data { bool pressed; }; -static void kscan_input_cb(const struct device *dev, struct input_event *evt) +static void kscan_input_cb(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; struct kscan_input_data *data = dev->data; switch (evt->code) { @@ -100,13 +101,9 @@ static const struct kscan_driver_api kscan_input_driver_api = { }; #define KSCAN_INPUT_INIT(index) \ - static void kscan_input_cb_##index(struct input_event *evt) \ - { \ - kscan_input_cb(DEVICE_DT_GET(DT_INST(index, DT_DRV_COMPAT)), \ - evt); \ - } \ INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(index)), \ - kscan_input_cb_##index); \ + kscan_input_cb, \ + (void *)DEVICE_DT_INST_GET(index)); \ static const struct kscan_input_config kscan_input_config_##index = { \ .input_dev = DEVICE_DT_GET(DT_INST_PARENT(index)), \ }; \ diff --git a/include/zephyr/input/input.h b/include/zephyr/input/input.h index 32e9bf9fa8e..a58feecbd95 100644 --- a/include/zephyr/input/input.h +++ b/include/zephyr/input/input.h @@ -124,7 +124,9 @@ struct input_callback { /** @ref device pointer or NULL. */ const struct device *dev; /** The callback function. */ - void (*callback)(struct input_event *evt); + void (*callback)(struct input_event *evt, void *user_data); + /** User data pointer. */ + void *user_data; }; /** @@ -136,12 +138,14 @@ struct input_callback { * * @param _dev @ref device pointer or NULL. * @param _callback The callback function. + * @param _user_data Pointer to user specified data. */ -#define INPUT_CALLBACK_DEFINE(_dev, _callback) \ +#define INPUT_CALLBACK_DEFINE(_dev, _callback, _user_data) \ static const STRUCT_SECTION_ITERABLE(input_callback, \ _input_callback__##_callback) = { \ .dev = _dev, \ .callback = _callback, \ + .user_data = _user_data, \ } #ifdef __cplusplus diff --git a/modules/lvgl/include/lvgl_common_input.h b/modules/lvgl/include/lvgl_common_input.h index c34dabe1a8a..2d553e6dbdd 100644 --- a/modules/lvgl/include/lvgl_common_input.h +++ b/modules/lvgl/include/lvgl_common_input.h @@ -36,11 +36,8 @@ int lvgl_init_input_devices(void); #define LVGL_KEY_VALID(key) IN_RANGE(key, 0, UINT8_MAX) #define LVGL_INPUT_DEFINE(inst, type, msgq_size, process_evt_cb) \ - static void lvgl_input_cb_##_##inst(struct input_event *evt) \ - { \ - process_evt_cb(DEVICE_DT_INST_GET(inst), evt); \ - } \ - INPUT_CALLBACK_DEFINE(LVGL_INPUT_DEVICE(inst), lvgl_input_cb_##_##inst); \ + INPUT_CALLBACK_DEFINE(LVGL_INPUT_DEVICE(inst), process_evt_cb, \ + (void *)DEVICE_DT_INST_GET(inst)); \ K_MSGQ_DEFINE(lvgl_input_msgq_##type##_##inst, sizeof(lv_indev_data_t), msgq_size, 4) #ifdef __cplusplus diff --git a/modules/lvgl/input/lvgl_button_input.c b/modules/lvgl/input/lvgl_button_input.c index 8ac96b7fed5..9976df48ca7 100644 --- a/modules/lvgl/input/lvgl_button_input.c +++ b/modules/lvgl/input/lvgl_button_input.c @@ -20,8 +20,9 @@ struct lvgl_button_input_config { const lv_coord_t *coordinates; }; -static void lvgl_button_process_event(const struct device *dev, struct input_event *evt) +static void lvgl_button_process_event(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; struct lvgl_common_input_data *data = dev->data; const struct lvgl_button_input_config *cfg = dev->config; uint8_t i; diff --git a/modules/lvgl/input/lvgl_encoder_input.c b/modules/lvgl/input/lvgl_encoder_input.c index b68ca67b871..4191dc7ab99 100644 --- a/modules/lvgl/input/lvgl_encoder_input.c +++ b/modules/lvgl/input/lvgl_encoder_input.c @@ -19,8 +19,9 @@ struct lvgl_encoder_input_config { int button_input_code; }; -static void lvgl_encoder_process_event(const struct device *dev, struct input_event *evt) +static void lvgl_encoder_process_event(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; struct lvgl_common_input_data *data = dev->data; const struct lvgl_encoder_input_config *cfg = dev->config; diff --git a/modules/lvgl/input/lvgl_keypad_input.c b/modules/lvgl/input/lvgl_keypad_input.c index fb131f47a29..963784df096 100644 --- a/modules/lvgl/input/lvgl_keypad_input.c +++ b/modules/lvgl/input/lvgl_keypad_input.c @@ -21,8 +21,9 @@ struct lvgl_keypad_input_config { uint8_t num_codes; }; -static void lvgl_keypad_process_event(const struct device *dev, struct input_event *evt) +static void lvgl_keypad_process_event(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; struct lvgl_common_input_data *data = dev->data; const struct lvgl_keypad_input_config *cfg = dev->config; uint8_t i; diff --git a/modules/lvgl/input/lvgl_pointer_input.c b/modules/lvgl/input/lvgl_pointer_input.c index e1b12243750..4bc40db05c4 100644 --- a/modules/lvgl/input/lvgl_pointer_input.c +++ b/modules/lvgl/input/lvgl_pointer_input.c @@ -27,8 +27,9 @@ struct lvgl_pointer_input_data { uint32_t point_y; }; -static void lvgl_pointer_process_event(const struct device *dev, struct input_event *evt) +static void lvgl_pointer_process_event(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; const struct lvgl_pointer_input_config *cfg = dev->config; struct lvgl_pointer_input_data *data = dev->data; lv_disp_t *disp = lv_disp_get_default(); diff --git a/samples/subsys/usb/hid-keyboard/src/main.c b/samples/subsys/usb/hid-keyboard/src/main.c index a9e3bcd46dc..7f423c87935 100644 --- a/samples/subsys/usb/hid-keyboard/src/main.c +++ b/samples/subsys/usb/hid-keyboard/src/main.c @@ -55,10 +55,12 @@ UDC_STATIC_BUF_DEFINE(report, KB_REPORT_COUNT); static uint32_t kb_duration; static bool kb_ready; -static void input_cb(struct input_event *evt) +static void input_cb(struct input_event *evt, void *user_data) { struct kb_event kb_evt; + ARG_UNUSED(user_data); + kb_evt.code = evt->code; kb_evt.value = evt->value; if (k_msgq_put(&kb_msgq, &kb_evt, K_NO_WAIT) != 0) { @@ -66,7 +68,7 @@ static void input_cb(struct input_event *evt) } } -INPUT_CALLBACK_DEFINE(NULL, input_cb); +INPUT_CALLBACK_DEFINE(NULL, input_cb, NULL); static void kb_iface_ready(const struct device *dev, const bool ready) { diff --git a/samples/subsys/usb/hid-mouse/src/main.c b/samples/subsys/usb/hid-mouse/src/main.c index c7888b5fdfd..c812a6cbd46 100644 --- a/samples/subsys/usb/hid-mouse/src/main.c +++ b/samples/subsys/usb/hid-mouse/src/main.c @@ -55,10 +55,12 @@ static ALWAYS_INLINE void rwup_if_suspended(void) } } -static void input_cb(struct input_event *evt) +static void input_cb(struct input_event *evt, void *user_data) { static uint8_t tmp[MOUSE_REPORT_COUNT]; + ARG_UNUSED(user_data); + switch (evt->code) { case INPUT_KEY_0: rwup_if_suspended(); @@ -95,7 +97,7 @@ static void input_cb(struct input_event *evt) } -INPUT_CALLBACK_DEFINE(NULL, input_cb); +INPUT_CALLBACK_DEFINE(NULL, input_cb, NULL); #if defined(CONFIG_USB_DEVICE_STACK_NEXT) static int enable_usb_device_next(void) diff --git a/subsys/input/input.c b/subsys/input/input.c index 907788e1853..560f7a02614 100644 --- a/subsys/input/input.c +++ b/subsys/input/input.c @@ -22,7 +22,7 @@ static void input_process(struct input_event *evt) { STRUCT_SECTION_FOREACH(input_callback, callback) { if (callback->dev == NULL || callback->dev == evt->dev) { - callback->callback(evt); + callback->callback(evt, callback->user_data); } } } diff --git a/subsys/input/input_keymap.c b/subsys/input/input_keymap.c index 938d238b2b0..895f1702831 100644 --- a/subsys/input/input_keymap.c +++ b/subsys/input/input_keymap.c @@ -29,8 +29,9 @@ struct keymap_data { bool pressed; }; -static void keymap_cb(const struct device *dev, struct input_event *evt) +static void keymap_cb(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; const struct keymap_config *cfg = dev->config; struct keymap_data *data = dev->data; const uint16_t *codes = cfg->codes; @@ -98,11 +99,8 @@ static int keymap_init(const struct device *dev) KEYMAP_ENTRY_CODE(DT_PROP_BY_IDX(node_id, prop, idx)), #define INPUT_KEYMAP_DEFINE(inst) \ - static void keymap_cb_##inst(struct input_event *evt) \ - { \ - keymap_cb(DEVICE_DT_INST_GET(inst), evt); \ - } \ - INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(inst)), keymap_cb_##inst); \ + INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(inst)), keymap_cb, \ + (void *)DEVICE_DT_INST_GET(inst)); \ \ DT_INST_FOREACH_PROP_ELEM(inst, keymap, KEYMAP_ENTRY_VALIDATE) \ \ diff --git a/subsys/input/input_longpress.c b/subsys/input/input_longpress.c index b7ce3c7c8d6..d53d2ce9be7 100644 --- a/subsys/input/input_longpress.c +++ b/subsys/input/input_longpress.c @@ -46,8 +46,9 @@ static void longpress_deferred(struct k_work *work) entry->long_fired = true; } -static void longpress_cb(const struct device *dev, struct input_event *evt) +static void longpress_cb(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; const struct longpress_config *cfg = dev->config; struct longpress_data_entry *entry; int i; @@ -105,15 +106,11 @@ static int longpress_init(const struct device *dev) #define INPUT_LONGPRESS_DEFINE(inst) \ BUILD_ASSERT((DT_INST_PROP_LEN(inst, input_codes) == \ DT_INST_PROP_LEN_OR(inst, short_codes, 0)) || \ - !DT_INST_NODE_HAS_PROP(inst, short_codes)); \ + !DT_INST_NODE_HAS_PROP(inst, short_codes)); \ BUILD_ASSERT(DT_INST_PROP_LEN(inst, input_codes) == DT_INST_PROP_LEN(inst, long_codes)); \ \ - static void longpress_cb_##inst(struct input_event *evt) \ - { \ - longpress_cb(DEVICE_DT_INST_GET(inst), evt); \ - } \ INPUT_CALLBACK_DEFINE(DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, input)), \ - longpress_cb_##inst); \ + longpress_cb, (void *)DEVICE_DT_INST_GET(inst)); \ \ static const uint16_t longpress_input_codes_##inst[] = DT_INST_PROP(inst, input_codes); \ \ diff --git a/subsys/input/input_utils.c b/subsys/input/input_utils.c index 9e093b27b46..85125b45a8d 100644 --- a/subsys/input/input_utils.c +++ b/subsys/input/input_utils.c @@ -51,8 +51,10 @@ static bool input_dump_enabled(void) } #endif /* CONFIG_INPUT_SHELL */ -static void input_dump_cb(struct input_event *evt) +static void input_dump_cb(struct input_event *evt, void *user_data) { + ARG_UNUSED(user_data); + if (!input_dump_enabled()) { return; } @@ -64,7 +66,7 @@ static void input_dump_cb(struct input_event *evt) evt->code, evt->value); } -INPUT_CALLBACK_DEFINE(NULL, input_dump_cb); +INPUT_CALLBACK_DEFINE(NULL, input_dump_cb, NULL); #endif /* CONFIG_INPUT_EVENT_DUMP */ #ifdef CONFIG_INPUT_SHELL @@ -162,12 +164,14 @@ static void kbd_matrix_state_log_entry(char *header, kbd_row_t *data) kbd_matrix_state_dev->name, header, kbd_matrix_buf, count); } -static void kbd_matrix_state_log(struct input_event *evt) +static void kbd_matrix_state_log(struct input_event *evt, void *user_data) { const struct input_kbd_matrix_common_config *cfg; static uint32_t row, col; static bool val; + ARG_UNUSED(user_data); + if (kbd_matrix_state_dev == NULL || kbd_matrix_state_dev != evt->dev) { return; } @@ -212,7 +216,7 @@ static void kbd_matrix_state_log(struct input_event *evt) kbd_matrix_state_log_entry("state", kbd_matrix_state); } -INPUT_CALLBACK_DEFINE(NULL, kbd_matrix_state_log); +INPUT_CALLBACK_DEFINE(NULL, kbd_matrix_state_log, NULL); static int input_cmd_kbd_matrix_state_dump(const struct shell *sh, size_t argc, char *argv[]) diff --git a/tests/drivers/input/gpio_kbd_matrix/src/main.c b/tests/drivers/input/gpio_kbd_matrix/src/main.c index 9600b510f0a..c7e0e933a4a 100644 --- a/tests/drivers/input/gpio_kbd_matrix/src/main.c +++ b/tests/drivers/input/gpio_kbd_matrix/src/main.c @@ -195,7 +195,7 @@ static int last_checked_event_count; zassert_equal(_val, test_event_data.val); \ } -static void test_cb(struct input_event *evt) +static void test_cb(struct input_event *evt, void *user_data) { static int row, col, val; @@ -220,7 +220,7 @@ static void test_cb(struct input_event *evt) test_event_data.event_count, row, col, val); } } -INPUT_CALLBACK_DEFINE(NULL, test_cb); +INPUT_CALLBACK_DEFINE(NULL, test_cb, NULL); /* actual tests */ diff --git a/tests/drivers/input/gpio_keys/src/main.c b/tests/drivers/input/gpio_keys/src/main.c index b5764d7283a..a8f12a8be6b 100644 --- a/tests/drivers/input/gpio_keys/src/main.c +++ b/tests/drivers/input/gpio_keys/src/main.c @@ -35,7 +35,7 @@ ZTEST_SUITE(gpio_keys, NULL, NULL, NULL, NULL, NULL); static int event_count; static uint16_t last_code; static bool last_val; -static void test_gpio_keys_cb_handler(struct input_event *evt) +static void test_gpio_keys_cb_handler(struct input_event *evt, void *user_data) { TC_PRINT("GPIO_KEY %s pressed, zephyr_code=%u, value=%d\n", evt->dev->name, evt->code, evt->value); @@ -43,7 +43,7 @@ static void test_gpio_keys_cb_handler(struct input_event *evt) last_code = evt->code; last_val = evt->value; } -INPUT_CALLBACK_DEFINE(test_gpio_keys_dev, test_gpio_keys_cb_handler); +INPUT_CALLBACK_DEFINE(test_gpio_keys_dev, test_gpio_keys_cb_handler, NULL); /** * @brief TestPurpose: Verify gpio_keys_config pressed raw. diff --git a/tests/drivers/input/kbd_matrix/src/main.c b/tests/drivers/input/kbd_matrix/src/main.c index bd09d4441fc..8477a6cc50b 100644 --- a/tests/drivers/input/kbd_matrix/src/main.c +++ b/tests/drivers/input/kbd_matrix/src/main.c @@ -103,7 +103,7 @@ static int last_checked_event_count; zassert_equal(_val, test_event_data.val); \ } -static void test_cb(struct input_event *evt) +static void test_cb(struct input_event *evt, void *user_data) { static int row, col, val; @@ -128,7 +128,7 @@ static void test_cb(struct input_event *evt) test_event_data.event_count, row, col, val); } } -INPUT_CALLBACK_DEFINE(test_dev, test_cb); +INPUT_CALLBACK_DEFINE(test_dev, test_cb, NULL); #define WAIT_FOR_IDLE_TIMEOUT_US (5 * USEC_PER_SEC) diff --git a/tests/subsys/input/api/src/main.c b/tests/subsys/input/api/src/main.c index c61e3c58551..0dffadedbd3 100644 --- a/tests/subsys/input/api/src/main.c +++ b/tests/subsys/input/api/src/main.c @@ -17,7 +17,7 @@ static int message_count_unfiltered; static K_SEM_DEFINE(cb_start, 1, 1); static K_SEM_DEFINE(cb_done, 1, 1); -static void input_cb_filtered(struct input_event *evt) +static void input_cb_filtered(struct input_event *evt, void *user_data) { TC_PRINT("%s: %d\n", __func__, message_count_filtered); @@ -29,9 +29,9 @@ static void input_cb_filtered(struct input_event *evt) k_sem_give(&cb_start); } -INPUT_CALLBACK_DEFINE(&fake_dev, input_cb_filtered); +INPUT_CALLBACK_DEFINE(&fake_dev, input_cb_filtered, NULL); -static void input_cb_unfiltered(struct input_event *evt) +static void input_cb_unfiltered(struct input_event *evt, void *user_data) { TC_PRINT("%s: %d\n", __func__, message_count_unfiltered); @@ -42,7 +42,7 @@ static void input_cb_unfiltered(struct input_event *evt) k_sem_give(&cb_done); } } -INPUT_CALLBACK_DEFINE(NULL, input_cb_unfiltered); +INPUT_CALLBACK_DEFINE(NULL, input_cb_unfiltered, NULL); ZTEST(input_api, test_sequence_thread) { @@ -85,19 +85,19 @@ ZTEST(input_api, test_sequence_thread) #else /* CONFIG_INPUT_MODE_THREAD */ -static void input_cb_filtered(struct input_event *evt) +static void input_cb_filtered(struct input_event *evt, void *user_data) { if (evt->dev == &fake_dev) { message_count_filtered++; } } -INPUT_CALLBACK_DEFINE(&fake_dev, input_cb_filtered); +INPUT_CALLBACK_DEFINE(&fake_dev, input_cb_filtered, NULL); -static void input_cb_unfiltered(struct input_event *evt) +static void input_cb_unfiltered(struct input_event *evt, void *user_data) { message_count_unfiltered++; } -INPUT_CALLBACK_DEFINE(NULL, input_cb_unfiltered); +INPUT_CALLBACK_DEFINE(NULL, input_cb_unfiltered, NULL); ZTEST(input_api, test_synchronous) { @@ -118,11 +118,11 @@ ZTEST(input_api, test_synchronous) static struct input_event last_event; -static void input_cb_last_event(struct input_event *evt) +static void input_cb_last_event(struct input_event *evt, void *user_data) { memcpy(&last_event, evt, sizeof(last_event)); } -INPUT_CALLBACK_DEFINE(NULL, input_cb_last_event); +INPUT_CALLBACK_DEFINE(NULL, input_cb_last_event, NULL); ZTEST(input_api, test_report_apis) { diff --git a/tests/subsys/input/longpress/src/main.c b/tests/subsys/input/longpress/src/main.c index 4f81901a742..83ea8979a8b 100644 --- a/tests/subsys/input/longpress/src/main.c +++ b/tests/subsys/input/longpress/src/main.c @@ -22,7 +22,7 @@ DEVICE_DT_DEFINE(DT_INST(0, vnd_input_device), NULL, NULL, NULL, NULL, static int event_count; static struct input_event last_events[2]; -static void test_cb(struct input_event *evt) +static void test_cb(struct input_event *evt, void *user_data) { TC_PRINT("%s: %d %x %d\n", __func__, event_count, evt->code, evt->value); @@ -30,11 +30,11 @@ static void test_cb(struct input_event *evt) memcpy(&last_events[1], &last_events[0], sizeof(struct input_event)); memcpy(&last_events[0], evt, sizeof(struct input_event)); } -INPUT_CALLBACK_DEFINE(longpress_dev, test_cb); +INPUT_CALLBACK_DEFINE(longpress_dev, test_cb, NULL); static int event_count_no_short; static struct input_event last_events_no_short[2]; -static void test_cb_no_short(struct input_event *evt) +static void test_cb_no_short(struct input_event *evt, void *user_data) { TC_PRINT("%s: %d %x %d\n", __func__, event_count_no_short, evt->code, evt->value); @@ -42,7 +42,7 @@ static void test_cb_no_short(struct input_event *evt) memcpy(&last_events_no_short[1], &last_events_no_short[0], sizeof(struct input_event)); memcpy(&last_events_no_short[0], evt, sizeof(struct input_event)); } -INPUT_CALLBACK_DEFINE(longpress_no_short_dev, test_cb_no_short); +INPUT_CALLBACK_DEFINE(longpress_no_short_dev, test_cb_no_short, NULL); ZTEST(longpress, test_longpress_test) { From 19481c3160259373b1cfc758eee0ac6c815f1e4c Mon Sep 17 00:00:00 2001 From: Mark Wang Date: Mon, 5 Aug 2024 15:24:26 +0800 Subject: [PATCH 24/39] [nrf fromtree] drivers: udc: mcux: support the UDC_EVT_SOF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the MCUX USB controller driver sof, then convert it to zephyr UDC_EVT_SOF event. Signed-off-by: Mark Wang (cherry picked from commit 564f34861288fd7c659146fce110528d92e25826) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_mcux_ehci.c | 3 +++ drivers/usb/udc/udc_mcux_ip3511.c | 3 +++ modules/hal_nxp/usb/usb_device_config.h | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index 451066bbbf5..abfb35cf142 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -454,6 +454,9 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) case kUSB_DeviceNotifyAttach: udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); break; + case kUSB_DeviceNotifySOF: + udc_submit_event(dev, UDC_EVT_SOF, 0); + break; default: ep = mcux_msg->code; if (mcux_msg->isSetup) { diff --git a/drivers/usb/udc/udc_mcux_ip3511.c b/drivers/usb/udc/udc_mcux_ip3511.c index 4c709fdf137..87978f0ff08 100644 --- a/drivers/usb/udc/udc_mcux_ip3511.c +++ b/drivers/usb/udc/udc_mcux_ip3511.c @@ -454,6 +454,9 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) case kUSB_DeviceNotifyAttach: udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); break; + case kUSB_DeviceNotifySOF: + udc_submit_event(dev, UDC_EVT_SOF, 0); + break; default: ep = mcux_msg->code; if (mcux_msg->isSetup) { diff --git a/modules/hal_nxp/usb/usb_device_config.h b/modules/hal_nxp/usb/usb_device_config.h index a97d9181c1b..78fc2a6303f 100644 --- a/modules/hal_nxp/usb/usb_device_config.h +++ b/modules/hal_nxp/usb/usb_device_config.h @@ -103,6 +103,11 @@ BUILD_ASSERT(NUM_INSTS <= 1, "Only one USB device supported"); #define USB_DEVICE_CONFIG_EHCI_MAX_DTD (16U) #endif +/* TODO: After Kconfig item that enable/disable sof is added, + * use the Kconfig item to control this macro. + */ +#define USB_DEVICE_CONFIG_SOF_NOTIFICATIONS (1U) + #endif #endif /* __USB_DEVICE_CONFIG_H__ */ From 7b980dc7fcd9b0ce8d2880c07e017c1ee4c9e2bf Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Wed, 24 Jul 2024 13:49:40 +0200 Subject: [PATCH 25/39] [nrf fromtree] usb: device_next: add device configuration change notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Notify only if the device configuration has changed. Pass only the configuration value as the message status, the actual device speed can be obtained with usbd_bus_speed(). Signed-off-by: Johann Fischer (cherry picked from commit f851cad162b8b37884c26b601a92946894b7bdc7) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usbd_msg.h | 3 +++ samples/subsys/usb/hid-keyboard/src/main.c | 4 ++++ subsys/usb/device_next/usbd_ch9.c | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/include/zephyr/usb/usbd_msg.h b/include/zephyr/usb/usbd_msg.h index a11cff7d1da..7f2f888e3f8 100644 --- a/include/zephyr/usb/usbd_msg.h +++ b/include/zephyr/usb/usbd_msg.h @@ -40,6 +40,8 @@ enum usbd_msg_type { USBD_MSG_SUSPEND, /** Bus reset detected */ USBD_MSG_RESET, + /** Device changed configuration */ + USBD_MSG_CONFIGURATION, /** Non-correctable UDC error message */ USBD_MSG_UDC_ERROR, /** Unrecoverable device stack error message */ @@ -59,6 +61,7 @@ static const char *const usbd_msg_type_list[] = { "Device resumed", "Device suspended", "Bus reset", + "New device configuration", "Controller error", "Stack error", "CDC ACM line coding", diff --git a/samples/subsys/usb/hid-keyboard/src/main.c b/samples/subsys/usb/hid-keyboard/src/main.c index 7f423c87935..14f360beb3a 100644 --- a/samples/subsys/usb/hid-keyboard/src/main.c +++ b/samples/subsys/usb/hid-keyboard/src/main.c @@ -149,6 +149,10 @@ static void msg_cb(struct usbd_context *const usbd_ctx, { LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); + if (msg->type == USBD_MSG_CONFIGURATION) { + LOG_INF("\tConfiguration value %d", msg->status); + } + if (usbd_can_detect_vbus(usbd_ctx)) { if (msg->type == USBD_MSG_VBUS_READY) { if (usbd_enable(usbd_ctx)) { diff --git a/subsys/usb/device_next/usbd_ch9.c b/subsys/usb/device_next/usbd_ch9.c index c9b05073314..1de59c1cc39 100644 --- a/subsys/usb/device_next/usbd_ch9.c +++ b/subsys/usb/device_next/usbd_ch9.c @@ -19,6 +19,7 @@ #include "usbd_class.h" #include "usbd_class_api.h" #include "usbd_interface.h" +#include "usbd_msg.h" #include LOG_MODULE_REGISTER(usbd_ch9, CONFIG_USBD_LOG_LEVEL); @@ -171,6 +172,10 @@ static int sreq_set_configuration(struct usbd_context *const uds_ctx) uds_ctx->ch9_data.state = USBD_STATE_CONFIGURED; } + if (ret == 0) { + usbd_msg_pub_simple(uds_ctx, USBD_MSG_CONFIGURATION, setup->wValue); + } + return ret; } From aca09d837dcaf5219e90cafd20df29b0cae6be18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 19 Jul 2024 08:09:22 +0200 Subject: [PATCH 26/39] [nrf fromtree] usb: Add missing wMaxPacketSize endian conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wMaxPacketSize in endpoint descriptor is stored in little-endian order, but the mps parameter passed to functions is in host order. Signed-off-by: Tomasz Moń (cherry picked from commit 60835aa393bbe750f154eafb3459ef03e71ccff6) Signed-off-by: Tomasz Moń --- subsys/usb/device/usb_descriptor.c | 2 +- subsys/usb/device_next/class/bt_hci.c | 8 ++--- subsys/usb/device_next/class/loopback.c | 4 +-- .../usb/device_next/class/usbd_uac2_macros.h | 2 +- subsys/usb/device_next/usbd_endpoint.c | 2 +- subsys/usb/device_next/usbd_init.c | 10 +++--- tests/drivers/udc/src/main.c | 34 +++++++++++-------- 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/subsys/usb/device/usb_descriptor.c b/subsys/usb/device/usb_descriptor.c index 325989dc7fd..bc933b19820 100644 --- a/subsys/usb/device/usb_descriptor.c +++ b/subsys/usb/device/usb_descriptor.c @@ -242,7 +242,7 @@ static int usb_validate_ep_cfg_data(struct usb_ep_descriptor * const ep_descr, ep_cfg.ep_type = (ep_descr->bmAttributes & USB_EP_TRANSFER_TYPE_MASK); - ep_cfg.ep_mps = ep_descr->wMaxPacketSize; + ep_cfg.ep_mps = sys_le16_to_cpu(ep_descr->wMaxPacketSize); ep_cfg.ep_addr = ep_descr->bEndpointAddress; if (ep_cfg.ep_addr & USB_EP_DIR_IN) { if ((*requested_ep & (1U << (idx + 16U)))) { diff --git a/subsys/usb/device_next/class/bt_hci.c b/subsys/usb/device_next/class/bt_hci.c index 273a9104062..2213fb88bce 100644 --- a/subsys/usb/device_next/class/bt_hci.c +++ b/subsys/usb/device_next/class/bt_hci.c @@ -564,7 +564,7 @@ static struct usbd_bt_hci_desc bt_hci_desc_##n = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = BT_HCI_EP_VOICE_IN, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = 0, \ + .wMaxPacketSize = sys_cpu_to_le16(0), \ .bInterval = BT_HCI_EP_INTERVAL_VOICE, \ }, \ \ @@ -573,7 +573,7 @@ static struct usbd_bt_hci_desc bt_hci_desc_##n = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = BT_HCI_EP_VOICE_OUT, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = 0, \ + .wMaxPacketSize = sys_cpu_to_le16(0), \ .bInterval = BT_HCI_EP_INTERVAL_VOICE, \ }, \ \ @@ -594,7 +594,7 @@ static struct usbd_bt_hci_desc bt_hci_desc_##n = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = BT_HCI_EP_VOICE_IN, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = BT_HCI_EP_MPS_VOICE, \ + .wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_MPS_VOICE), \ .bInterval = BT_HCI_EP_INTERVAL_VOICE, \ }, \ \ @@ -603,7 +603,7 @@ static struct usbd_bt_hci_desc bt_hci_desc_##n = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = BT_HCI_EP_VOICE_OUT, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = BT_HCI_EP_MPS_VOICE, \ + .wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_MPS_VOICE), \ .bInterval = BT_HCI_EP_INTERVAL_VOICE, \ }, \ \ diff --git a/subsys/usb/device_next/class/loopback.c b/subsys/usb/device_next/class/loopback.c index 8a9688dde53..2237cabcc79 100644 --- a/subsys/usb/device_next/class/loopback.c +++ b/subsys/usb/device_next/class/loopback.c @@ -270,7 +270,7 @@ static struct loopback_desc lb_desc_##x = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x83, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = 0, \ + .wMaxPacketSize = sys_cpu_to_le16(0), \ .bInterval = LB_ISO_EP_INTERVAL, \ }, \ \ @@ -279,7 +279,7 @@ static struct loopback_desc lb_desc_##x = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x03, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = 0, \ + .wMaxPacketSize = sys_cpu_to_le16(0), \ .bInterval = LB_ISO_EP_INTERVAL, \ }, \ \ diff --git a/subsys/usb/device_next/class/usbd_uac2_macros.h b/subsys/usb/device_next/class/usbd_uac2_macros.h index 58716214b56..7182e8e34b6 100644 --- a/subsys/usb/device_next/class/usbd_uac2_macros.h +++ b/subsys/usb/device_next/class/usbd_uac2_macros.h @@ -464,7 +464,7 @@ USB_DESC_ENDPOINT, /* bDescriptorType */ \ FIRST_IN_EP_ADDR, /* bEndpointAddress */ \ USB_EP_TYPE_INTERRUPT, /* bmAttributes */ \ - 0x06, /* wMaxPacketSize */ \ + U16_LE(0x06), /* wMaxPacketSize */ \ 0x01, /* bInterval */ \ #define AC_ENDPOINT_DESCRIPTOR_ARRAY(node) \ diff --git a/subsys/usb/device_next/usbd_endpoint.c b/subsys/usb/device_next/usbd_endpoint.c index 5d49e0df8ea..1d41f4c99a2 100644 --- a/subsys/usb/device_next/usbd_endpoint.c +++ b/subsys/usb/device_next/usbd_endpoint.c @@ -24,7 +24,7 @@ int usbd_ep_enable(const struct device *dev, int ret; ret = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), ed->bInterval); if (ret == 0) { usbd_ep_bm_set(ep_bm, ed->bEndpointAddress); } diff --git a/subsys/usb/device_next/usbd_init.c b/subsys/usb/device_next/usbd_init.c index 69de723b221..551dc32164f 100644 --- a/subsys/usb/device_next/usbd_init.c +++ b/subsys/usb/device_next/usbd_init.c @@ -28,7 +28,7 @@ static int assign_ep_addr(const struct device *dev, int ret = -ENODEV; for (unsigned int idx = 1; idx < 16U; idx++) { - uint16_t mps = ed->wMaxPacketSize; + uint16_t mps = sys_le16_to_cpu(ed->wMaxPacketSize); uint8_t ep; if (USB_EP_DIR_IS_IN(ed->bEndpointAddress)) { @@ -50,7 +50,7 @@ static int assign_ep_addr(const struct device *dev, if (ret == 0) { LOG_DBG("ep 0x%02x -> 0x%02x", ed->bEndpointAddress, ep); ed->bEndpointAddress = ep; - ed->wMaxPacketSize = mps; + ed->wMaxPacketSize = sys_cpu_to_le16(mps); usbd_ep_bm_set(class_ep_bm, ed->bEndpointAddress); usbd_ep_bm_set(config_ep_bm, ed->bEndpointAddress); @@ -166,8 +166,10 @@ static int init_configuration_inst(struct usbd_context *const uds_ctx, return ret; } - LOG_INF("\tep 0x%02x mps %u interface ep-bm 0x%08x", - ed->bEndpointAddress, ed->wMaxPacketSize, class_ep_bm); + LOG_INF("\tep 0x%02x mps 0x%04x interface ep-bm 0x%08x", + ed->bEndpointAddress, + sys_le16_to_cpu(ed->wMaxPacketSize), + class_ep_bm); } dhp++; diff --git a/tests/drivers/udc/src/main.c b/tests/drivers/udc/src/main.c index 24e1eb4d1f2..86565aaad9b 100644 --- a/tests/drivers/udc/src/main.c +++ b/tests/drivers/udc/src/main.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -91,7 +92,7 @@ static void test_udc_thread(void *p1, void *p2, void *p3) static void test_udc_ep_try_config(const struct device *dev, struct usb_ep_descriptor *ed) { - uint16_t mps = ed->wMaxPacketSize; + uint16_t mps = sys_le16_to_cpu(ed->wMaxPacketSize); int err; err = udc_ep_try_config(dev, ed->bEndpointAddress, @@ -125,13 +126,17 @@ static void test_udc_ep_enable(const struct device *dev, int err1, err2, err3, err4; err1 = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); err2 = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); err3 = udc_ep_enable(dev, FALSE_EP_ADDR, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); err4 = udc_ep_enable(dev, ctrl_ep, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); if (!udc_is_initialized(dev) && !udc_is_enabled(dev)) { zassert_equal(err1, -EPERM, "Not failed to enable endpoint"); @@ -188,7 +193,7 @@ static struct net_buf *test_udc_ep_buf_alloc(const struct device *dev, struct net_buf *buf; buf = udc_ep_buf_alloc(dev, ed->bEndpointAddress, - ed->wMaxPacketSize); + sys_le16_to_cpu(ed->wMaxPacketSize)); zassert_not_null(buf, "Failed to allocate request"); @@ -328,13 +333,14 @@ static void test_udc_ep_api(const struct device *dev, for (int i = 0; i < num_of_iterations; i++) { err = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); zassert_ok(err, "Failed to enable endpoint"); /* It needs a little reserve for memory management overhead. */ for (int n = 0; n < (CONFIG_UDC_BUF_COUNT - 2); n++) { buf = udc_ep_buf_alloc(dev, ed->bEndpointAddress, - ed->wMaxPacketSize); + sys_le16_to_cpu(ed->wMaxPacketSize)); zassert_not_null(buf, "Failed to allocate request (%d) for 0x%02x", n, ed->bEndpointAddress); @@ -363,7 +369,7 @@ static void test_udc_ep_mps(uint8_t type) .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x01, .bmAttributes = type, - .wMaxPacketSize = 0, + .wMaxPacketSize = sys_cpu_to_le16(0), .bInterval = 0, }; const struct device *dev; @@ -400,7 +406,7 @@ static void test_udc_ep_mps(uint8_t type) continue; } - ed.wMaxPacketSize = mps[i]; + ed.wMaxPacketSize = sys_cpu_to_le16(mps[i]); test_udc_ep_api(dev, &ed); ed.bEndpointAddress |= USB_EP_DIR_IN; @@ -441,7 +447,7 @@ static struct usb_ep_descriptor ed_ctrl_out = { .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = USB_CONTROL_EP_OUT, .bmAttributes = USB_EP_TYPE_CONTROL, - .wMaxPacketSize = 64, + .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0, }; @@ -450,7 +456,7 @@ static struct usb_ep_descriptor ed_ctrl_in = { .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = USB_CONTROL_EP_IN, .bmAttributes = USB_EP_TYPE_CONTROL, - .wMaxPacketSize = 64, + .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0, }; @@ -459,7 +465,7 @@ static struct usb_ep_descriptor ed_bulk_out = { .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x01, .bmAttributes = USB_EP_TYPE_BULK, - .wMaxPacketSize = 64, + .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0, }; @@ -468,7 +474,7 @@ static struct usb_ep_descriptor ed_bulk_in = { .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x81, .bmAttributes = USB_EP_TYPE_BULK, - .wMaxPacketSize = 64, + .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0, }; From 17fd41580544299bea35bbc1fc1ea3859b1e7713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 19 Jul 2024 13:23:09 +0200 Subject: [PATCH 27/39] [nrf fromtree] usb: device_next: hid: Simplify MPS less than 65 detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use COND_CODE_1() instead of UTIL_AND() to make it possible to only define the HID_MPS_LESS_65_ macros up to value 64. Signed-off-by: Tomasz Moń (cherry picked from commit e333739f3ae7c73af0cc2bb5a1ed34657f04a08b) Signed-off-by: Tomasz Moń --- .../usb/device_next/class/usbd_hid_macros.h | 964 +----------------- 1 file changed, 2 insertions(+), 962 deletions(-) diff --git a/subsys/usb/device_next/class/usbd_hid_macros.h b/subsys/usb/device_next/class/usbd_hid_macros.h index ec6c23fa2b8..952384a4888 100644 --- a/subsys/usb/device_next/class/usbd_hid_macros.h +++ b/subsys/usb/device_next/class/usbd_hid_macros.h @@ -84,966 +84,6 @@ #define HID_MPS_LESS_65_62 1 #define HID_MPS_LESS_65_63 1 #define HID_MPS_LESS_65_64 1 -#define HID_MPS_LESS_65_65 0 -#define HID_MPS_LESS_65_66 0 -#define HID_MPS_LESS_65_67 0 -#define HID_MPS_LESS_65_68 0 -#define HID_MPS_LESS_65_69 0 -#define HID_MPS_LESS_65_70 0 -#define HID_MPS_LESS_65_71 0 -#define HID_MPS_LESS_65_72 0 -#define HID_MPS_LESS_65_73 0 -#define HID_MPS_LESS_65_74 0 -#define HID_MPS_LESS_65_75 0 -#define HID_MPS_LESS_65_76 0 -#define HID_MPS_LESS_65_77 0 -#define HID_MPS_LESS_65_78 0 -#define HID_MPS_LESS_65_79 0 -#define HID_MPS_LESS_65_80 0 -#define HID_MPS_LESS_65_81 0 -#define HID_MPS_LESS_65_82 0 -#define HID_MPS_LESS_65_83 0 -#define HID_MPS_LESS_65_84 0 -#define HID_MPS_LESS_65_85 0 -#define HID_MPS_LESS_65_86 0 -#define HID_MPS_LESS_65_87 0 -#define HID_MPS_LESS_65_88 0 -#define HID_MPS_LESS_65_89 0 -#define HID_MPS_LESS_65_90 0 -#define HID_MPS_LESS_65_91 0 -#define HID_MPS_LESS_65_92 0 -#define HID_MPS_LESS_65_93 0 -#define HID_MPS_LESS_65_94 0 -#define HID_MPS_LESS_65_95 0 -#define HID_MPS_LESS_65_96 0 -#define HID_MPS_LESS_65_97 0 -#define HID_MPS_LESS_65_98 0 -#define HID_MPS_LESS_65_99 0 -#define HID_MPS_LESS_65_100 0 -#define HID_MPS_LESS_65_101 0 -#define HID_MPS_LESS_65_102 0 -#define HID_MPS_LESS_65_103 0 -#define HID_MPS_LESS_65_104 0 -#define HID_MPS_LESS_65_105 0 -#define HID_MPS_LESS_65_106 0 -#define HID_MPS_LESS_65_107 0 -#define HID_MPS_LESS_65_108 0 -#define HID_MPS_LESS_65_109 0 -#define HID_MPS_LESS_65_110 0 -#define HID_MPS_LESS_65_111 0 -#define HID_MPS_LESS_65_112 0 -#define HID_MPS_LESS_65_113 0 -#define HID_MPS_LESS_65_114 0 -#define HID_MPS_LESS_65_115 0 -#define HID_MPS_LESS_65_116 0 -#define HID_MPS_LESS_65_117 0 -#define HID_MPS_LESS_65_118 0 -#define HID_MPS_LESS_65_119 0 -#define HID_MPS_LESS_65_120 0 -#define HID_MPS_LESS_65_121 0 -#define HID_MPS_LESS_65_122 0 -#define HID_MPS_LESS_65_123 0 -#define HID_MPS_LESS_65_124 0 -#define HID_MPS_LESS_65_125 0 -#define HID_MPS_LESS_65_126 0 -#define HID_MPS_LESS_65_127 0 -#define HID_MPS_LESS_65_128 0 -#define HID_MPS_LESS_65_129 0 -#define HID_MPS_LESS_65_130 0 -#define HID_MPS_LESS_65_131 0 -#define HID_MPS_LESS_65_132 0 -#define HID_MPS_LESS_65_133 0 -#define HID_MPS_LESS_65_134 0 -#define HID_MPS_LESS_65_135 0 -#define HID_MPS_LESS_65_136 0 -#define HID_MPS_LESS_65_137 0 -#define HID_MPS_LESS_65_138 0 -#define HID_MPS_LESS_65_139 0 -#define HID_MPS_LESS_65_140 0 -#define HID_MPS_LESS_65_141 0 -#define HID_MPS_LESS_65_142 0 -#define HID_MPS_LESS_65_143 0 -#define HID_MPS_LESS_65_144 0 -#define HID_MPS_LESS_65_145 0 -#define HID_MPS_LESS_65_146 0 -#define HID_MPS_LESS_65_147 0 -#define HID_MPS_LESS_65_148 0 -#define HID_MPS_LESS_65_149 0 -#define HID_MPS_LESS_65_150 0 -#define HID_MPS_LESS_65_151 0 -#define HID_MPS_LESS_65_152 0 -#define HID_MPS_LESS_65_153 0 -#define HID_MPS_LESS_65_154 0 -#define HID_MPS_LESS_65_155 0 -#define HID_MPS_LESS_65_156 0 -#define HID_MPS_LESS_65_157 0 -#define HID_MPS_LESS_65_158 0 -#define HID_MPS_LESS_65_159 0 -#define HID_MPS_LESS_65_160 0 -#define HID_MPS_LESS_65_161 0 -#define HID_MPS_LESS_65_162 0 -#define HID_MPS_LESS_65_163 0 -#define HID_MPS_LESS_65_164 0 -#define HID_MPS_LESS_65_165 0 -#define HID_MPS_LESS_65_166 0 -#define HID_MPS_LESS_65_167 0 -#define HID_MPS_LESS_65_168 0 -#define HID_MPS_LESS_65_169 0 -#define HID_MPS_LESS_65_170 0 -#define HID_MPS_LESS_65_171 0 -#define HID_MPS_LESS_65_172 0 -#define HID_MPS_LESS_65_173 0 -#define HID_MPS_LESS_65_174 0 -#define HID_MPS_LESS_65_175 0 -#define HID_MPS_LESS_65_176 0 -#define HID_MPS_LESS_65_177 0 -#define HID_MPS_LESS_65_178 0 -#define HID_MPS_LESS_65_179 0 -#define HID_MPS_LESS_65_180 0 -#define HID_MPS_LESS_65_181 0 -#define HID_MPS_LESS_65_182 0 -#define HID_MPS_LESS_65_183 0 -#define HID_MPS_LESS_65_184 0 -#define HID_MPS_LESS_65_185 0 -#define HID_MPS_LESS_65_186 0 -#define HID_MPS_LESS_65_187 0 -#define HID_MPS_LESS_65_188 0 -#define HID_MPS_LESS_65_189 0 -#define HID_MPS_LESS_65_190 0 -#define HID_MPS_LESS_65_191 0 -#define HID_MPS_LESS_65_192 0 -#define HID_MPS_LESS_65_193 0 -#define HID_MPS_LESS_65_194 0 -#define HID_MPS_LESS_65_195 0 -#define HID_MPS_LESS_65_196 0 -#define HID_MPS_LESS_65_197 0 -#define HID_MPS_LESS_65_198 0 -#define HID_MPS_LESS_65_199 0 -#define HID_MPS_LESS_65_200 0 -#define HID_MPS_LESS_65_201 0 -#define HID_MPS_LESS_65_202 0 -#define HID_MPS_LESS_65_203 0 -#define HID_MPS_LESS_65_204 0 -#define HID_MPS_LESS_65_205 0 -#define HID_MPS_LESS_65_206 0 -#define HID_MPS_LESS_65_207 0 -#define HID_MPS_LESS_65_208 0 -#define HID_MPS_LESS_65_209 0 -#define HID_MPS_LESS_65_210 0 -#define HID_MPS_LESS_65_211 0 -#define HID_MPS_LESS_65_212 0 -#define HID_MPS_LESS_65_213 0 -#define HID_MPS_LESS_65_214 0 -#define HID_MPS_LESS_65_215 0 -#define HID_MPS_LESS_65_216 0 -#define HID_MPS_LESS_65_217 0 -#define HID_MPS_LESS_65_218 0 -#define HID_MPS_LESS_65_219 0 -#define HID_MPS_LESS_65_220 0 -#define HID_MPS_LESS_65_221 0 -#define HID_MPS_LESS_65_222 0 -#define HID_MPS_LESS_65_223 0 -#define HID_MPS_LESS_65_224 0 -#define HID_MPS_LESS_65_225 0 -#define HID_MPS_LESS_65_226 0 -#define HID_MPS_LESS_65_227 0 -#define HID_MPS_LESS_65_228 0 -#define HID_MPS_LESS_65_229 0 -#define HID_MPS_LESS_65_230 0 -#define HID_MPS_LESS_65_231 0 -#define HID_MPS_LESS_65_232 0 -#define HID_MPS_LESS_65_233 0 -#define HID_MPS_LESS_65_234 0 -#define HID_MPS_LESS_65_235 0 -#define HID_MPS_LESS_65_236 0 -#define HID_MPS_LESS_65_237 0 -#define HID_MPS_LESS_65_238 0 -#define HID_MPS_LESS_65_239 0 -#define HID_MPS_LESS_65_240 0 -#define HID_MPS_LESS_65_241 0 -#define HID_MPS_LESS_65_242 0 -#define HID_MPS_LESS_65_243 0 -#define HID_MPS_LESS_65_244 0 -#define HID_MPS_LESS_65_245 0 -#define HID_MPS_LESS_65_246 0 -#define HID_MPS_LESS_65_247 0 -#define HID_MPS_LESS_65_248 0 -#define HID_MPS_LESS_65_249 0 -#define HID_MPS_LESS_65_250 0 -#define HID_MPS_LESS_65_251 0 -#define HID_MPS_LESS_65_252 0 -#define HID_MPS_LESS_65_253 0 -#define HID_MPS_LESS_65_254 0 -#define HID_MPS_LESS_65_255 0 -#define HID_MPS_LESS_65_256 0 -#define HID_MPS_LESS_65_257 0 -#define HID_MPS_LESS_65_258 0 -#define HID_MPS_LESS_65_259 0 -#define HID_MPS_LESS_65_260 0 -#define HID_MPS_LESS_65_261 0 -#define HID_MPS_LESS_65_262 0 -#define HID_MPS_LESS_65_263 0 -#define HID_MPS_LESS_65_264 0 -#define HID_MPS_LESS_65_265 0 -#define HID_MPS_LESS_65_266 0 -#define HID_MPS_LESS_65_267 0 -#define HID_MPS_LESS_65_268 0 -#define HID_MPS_LESS_65_269 0 -#define HID_MPS_LESS_65_270 0 -#define HID_MPS_LESS_65_271 0 -#define HID_MPS_LESS_65_272 0 -#define HID_MPS_LESS_65_273 0 -#define HID_MPS_LESS_65_274 0 -#define HID_MPS_LESS_65_275 0 -#define HID_MPS_LESS_65_276 0 -#define HID_MPS_LESS_65_277 0 -#define HID_MPS_LESS_65_278 0 -#define HID_MPS_LESS_65_279 0 -#define HID_MPS_LESS_65_280 0 -#define HID_MPS_LESS_65_281 0 -#define HID_MPS_LESS_65_282 0 -#define HID_MPS_LESS_65_283 0 -#define HID_MPS_LESS_65_284 0 -#define HID_MPS_LESS_65_285 0 -#define HID_MPS_LESS_65_286 0 -#define HID_MPS_LESS_65_287 0 -#define HID_MPS_LESS_65_288 0 -#define HID_MPS_LESS_65_289 0 -#define HID_MPS_LESS_65_290 0 -#define HID_MPS_LESS_65_291 0 -#define HID_MPS_LESS_65_292 0 -#define HID_MPS_LESS_65_293 0 -#define HID_MPS_LESS_65_294 0 -#define HID_MPS_LESS_65_295 0 -#define HID_MPS_LESS_65_296 0 -#define HID_MPS_LESS_65_297 0 -#define HID_MPS_LESS_65_298 0 -#define HID_MPS_LESS_65_299 0 -#define HID_MPS_LESS_65_300 0 -#define HID_MPS_LESS_65_301 0 -#define HID_MPS_LESS_65_302 0 -#define HID_MPS_LESS_65_303 0 -#define HID_MPS_LESS_65_304 0 -#define HID_MPS_LESS_65_305 0 -#define HID_MPS_LESS_65_306 0 -#define HID_MPS_LESS_65_307 0 -#define HID_MPS_LESS_65_308 0 -#define HID_MPS_LESS_65_309 0 -#define HID_MPS_LESS_65_310 0 -#define HID_MPS_LESS_65_311 0 -#define HID_MPS_LESS_65_312 0 -#define HID_MPS_LESS_65_313 0 -#define HID_MPS_LESS_65_314 0 -#define HID_MPS_LESS_65_315 0 -#define HID_MPS_LESS_65_316 0 -#define HID_MPS_LESS_65_317 0 -#define HID_MPS_LESS_65_318 0 -#define HID_MPS_LESS_65_319 0 -#define HID_MPS_LESS_65_320 0 -#define HID_MPS_LESS_65_321 0 -#define HID_MPS_LESS_65_322 0 -#define HID_MPS_LESS_65_323 0 -#define HID_MPS_LESS_65_324 0 -#define HID_MPS_LESS_65_325 0 -#define HID_MPS_LESS_65_326 0 -#define HID_MPS_LESS_65_327 0 -#define HID_MPS_LESS_65_328 0 -#define HID_MPS_LESS_65_329 0 -#define HID_MPS_LESS_65_330 0 -#define HID_MPS_LESS_65_331 0 -#define HID_MPS_LESS_65_332 0 -#define HID_MPS_LESS_65_333 0 -#define HID_MPS_LESS_65_334 0 -#define HID_MPS_LESS_65_335 0 -#define HID_MPS_LESS_65_336 0 -#define HID_MPS_LESS_65_337 0 -#define HID_MPS_LESS_65_338 0 -#define HID_MPS_LESS_65_339 0 -#define HID_MPS_LESS_65_340 0 -#define HID_MPS_LESS_65_341 0 -#define HID_MPS_LESS_65_342 0 -#define HID_MPS_LESS_65_343 0 -#define HID_MPS_LESS_65_344 0 -#define HID_MPS_LESS_65_345 0 -#define HID_MPS_LESS_65_346 0 -#define HID_MPS_LESS_65_347 0 -#define HID_MPS_LESS_65_348 0 -#define HID_MPS_LESS_65_349 0 -#define HID_MPS_LESS_65_350 0 -#define HID_MPS_LESS_65_351 0 -#define HID_MPS_LESS_65_352 0 -#define HID_MPS_LESS_65_353 0 -#define HID_MPS_LESS_65_354 0 -#define HID_MPS_LESS_65_355 0 -#define HID_MPS_LESS_65_356 0 -#define HID_MPS_LESS_65_357 0 -#define HID_MPS_LESS_65_358 0 -#define HID_MPS_LESS_65_359 0 -#define HID_MPS_LESS_65_360 0 -#define HID_MPS_LESS_65_361 0 -#define HID_MPS_LESS_65_362 0 -#define HID_MPS_LESS_65_363 0 -#define HID_MPS_LESS_65_364 0 -#define HID_MPS_LESS_65_365 0 -#define HID_MPS_LESS_65_366 0 -#define HID_MPS_LESS_65_367 0 -#define HID_MPS_LESS_65_368 0 -#define HID_MPS_LESS_65_369 0 -#define HID_MPS_LESS_65_370 0 -#define HID_MPS_LESS_65_371 0 -#define HID_MPS_LESS_65_372 0 -#define HID_MPS_LESS_65_373 0 -#define HID_MPS_LESS_65_374 0 -#define HID_MPS_LESS_65_375 0 -#define HID_MPS_LESS_65_376 0 -#define HID_MPS_LESS_65_377 0 -#define HID_MPS_LESS_65_378 0 -#define HID_MPS_LESS_65_379 0 -#define HID_MPS_LESS_65_380 0 -#define HID_MPS_LESS_65_381 0 -#define HID_MPS_LESS_65_382 0 -#define HID_MPS_LESS_65_383 0 -#define HID_MPS_LESS_65_384 0 -#define HID_MPS_LESS_65_385 0 -#define HID_MPS_LESS_65_386 0 -#define HID_MPS_LESS_65_387 0 -#define HID_MPS_LESS_65_388 0 -#define HID_MPS_LESS_65_389 0 -#define HID_MPS_LESS_65_390 0 -#define HID_MPS_LESS_65_391 0 -#define HID_MPS_LESS_65_392 0 -#define HID_MPS_LESS_65_393 0 -#define HID_MPS_LESS_65_394 0 -#define HID_MPS_LESS_65_395 0 -#define HID_MPS_LESS_65_396 0 -#define HID_MPS_LESS_65_397 0 -#define HID_MPS_LESS_65_398 0 -#define HID_MPS_LESS_65_399 0 -#define HID_MPS_LESS_65_400 0 -#define HID_MPS_LESS_65_401 0 -#define HID_MPS_LESS_65_402 0 -#define HID_MPS_LESS_65_403 0 -#define HID_MPS_LESS_65_404 0 -#define HID_MPS_LESS_65_405 0 -#define HID_MPS_LESS_65_406 0 -#define HID_MPS_LESS_65_407 0 -#define HID_MPS_LESS_65_408 0 -#define HID_MPS_LESS_65_409 0 -#define HID_MPS_LESS_65_410 0 -#define HID_MPS_LESS_65_411 0 -#define HID_MPS_LESS_65_412 0 -#define HID_MPS_LESS_65_413 0 -#define HID_MPS_LESS_65_414 0 -#define HID_MPS_LESS_65_415 0 -#define HID_MPS_LESS_65_416 0 -#define HID_MPS_LESS_65_417 0 -#define HID_MPS_LESS_65_418 0 -#define HID_MPS_LESS_65_419 0 -#define HID_MPS_LESS_65_420 0 -#define HID_MPS_LESS_65_421 0 -#define HID_MPS_LESS_65_422 0 -#define HID_MPS_LESS_65_423 0 -#define HID_MPS_LESS_65_424 0 -#define HID_MPS_LESS_65_425 0 -#define HID_MPS_LESS_65_426 0 -#define HID_MPS_LESS_65_427 0 -#define HID_MPS_LESS_65_428 0 -#define HID_MPS_LESS_65_429 0 -#define HID_MPS_LESS_65_430 0 -#define HID_MPS_LESS_65_431 0 -#define HID_MPS_LESS_65_432 0 -#define HID_MPS_LESS_65_433 0 -#define HID_MPS_LESS_65_434 0 -#define HID_MPS_LESS_65_435 0 -#define HID_MPS_LESS_65_436 0 -#define HID_MPS_LESS_65_437 0 -#define HID_MPS_LESS_65_438 0 -#define HID_MPS_LESS_65_439 0 -#define HID_MPS_LESS_65_440 0 -#define HID_MPS_LESS_65_441 0 -#define HID_MPS_LESS_65_442 0 -#define HID_MPS_LESS_65_443 0 -#define HID_MPS_LESS_65_444 0 -#define HID_MPS_LESS_65_445 0 -#define HID_MPS_LESS_65_446 0 -#define HID_MPS_LESS_65_447 0 -#define HID_MPS_LESS_65_448 0 -#define HID_MPS_LESS_65_449 0 -#define HID_MPS_LESS_65_450 0 -#define HID_MPS_LESS_65_451 0 -#define HID_MPS_LESS_65_452 0 -#define HID_MPS_LESS_65_453 0 -#define HID_MPS_LESS_65_454 0 -#define HID_MPS_LESS_65_455 0 -#define HID_MPS_LESS_65_456 0 -#define HID_MPS_LESS_65_457 0 -#define HID_MPS_LESS_65_458 0 -#define HID_MPS_LESS_65_459 0 -#define HID_MPS_LESS_65_460 0 -#define HID_MPS_LESS_65_461 0 -#define HID_MPS_LESS_65_462 0 -#define HID_MPS_LESS_65_463 0 -#define HID_MPS_LESS_65_464 0 -#define HID_MPS_LESS_65_465 0 -#define HID_MPS_LESS_65_466 0 -#define HID_MPS_LESS_65_467 0 -#define HID_MPS_LESS_65_468 0 -#define HID_MPS_LESS_65_469 0 -#define HID_MPS_LESS_65_470 0 -#define HID_MPS_LESS_65_471 0 -#define HID_MPS_LESS_65_472 0 -#define HID_MPS_LESS_65_473 0 -#define HID_MPS_LESS_65_474 0 -#define HID_MPS_LESS_65_475 0 -#define HID_MPS_LESS_65_476 0 -#define HID_MPS_LESS_65_477 0 -#define HID_MPS_LESS_65_478 0 -#define HID_MPS_LESS_65_479 0 -#define HID_MPS_LESS_65_480 0 -#define HID_MPS_LESS_65_481 0 -#define HID_MPS_LESS_65_482 0 -#define HID_MPS_LESS_65_483 0 -#define HID_MPS_LESS_65_484 0 -#define HID_MPS_LESS_65_485 0 -#define HID_MPS_LESS_65_486 0 -#define HID_MPS_LESS_65_487 0 -#define HID_MPS_LESS_65_488 0 -#define HID_MPS_LESS_65_489 0 -#define HID_MPS_LESS_65_490 0 -#define HID_MPS_LESS_65_491 0 -#define HID_MPS_LESS_65_492 0 -#define HID_MPS_LESS_65_493 0 -#define HID_MPS_LESS_65_494 0 -#define HID_MPS_LESS_65_495 0 -#define HID_MPS_LESS_65_496 0 -#define HID_MPS_LESS_65_497 0 -#define HID_MPS_LESS_65_498 0 -#define HID_MPS_LESS_65_499 0 -#define HID_MPS_LESS_65_500 0 -#define HID_MPS_LESS_65_501 0 -#define HID_MPS_LESS_65_502 0 -#define HID_MPS_LESS_65_503 0 -#define HID_MPS_LESS_65_504 0 -#define HID_MPS_LESS_65_505 0 -#define HID_MPS_LESS_65_506 0 -#define HID_MPS_LESS_65_507 0 -#define HID_MPS_LESS_65_508 0 -#define HID_MPS_LESS_65_509 0 -#define HID_MPS_LESS_65_510 0 -#define HID_MPS_LESS_65_511 0 -#define HID_MPS_LESS_65_512 0 -#define HID_MPS_LESS_65_513 0 -#define HID_MPS_LESS_65_514 0 -#define HID_MPS_LESS_65_515 0 -#define HID_MPS_LESS_65_516 0 -#define HID_MPS_LESS_65_517 0 -#define HID_MPS_LESS_65_518 0 -#define HID_MPS_LESS_65_519 0 -#define HID_MPS_LESS_65_520 0 -#define HID_MPS_LESS_65_521 0 -#define HID_MPS_LESS_65_522 0 -#define HID_MPS_LESS_65_523 0 -#define HID_MPS_LESS_65_524 0 -#define HID_MPS_LESS_65_525 0 -#define HID_MPS_LESS_65_526 0 -#define HID_MPS_LESS_65_527 0 -#define HID_MPS_LESS_65_528 0 -#define HID_MPS_LESS_65_529 0 -#define HID_MPS_LESS_65_530 0 -#define HID_MPS_LESS_65_531 0 -#define HID_MPS_LESS_65_532 0 -#define HID_MPS_LESS_65_533 0 -#define HID_MPS_LESS_65_534 0 -#define HID_MPS_LESS_65_535 0 -#define HID_MPS_LESS_65_536 0 -#define HID_MPS_LESS_65_537 0 -#define HID_MPS_LESS_65_538 0 -#define HID_MPS_LESS_65_539 0 -#define HID_MPS_LESS_65_540 0 -#define HID_MPS_LESS_65_541 0 -#define HID_MPS_LESS_65_542 0 -#define HID_MPS_LESS_65_543 0 -#define HID_MPS_LESS_65_544 0 -#define HID_MPS_LESS_65_545 0 -#define HID_MPS_LESS_65_546 0 -#define HID_MPS_LESS_65_547 0 -#define HID_MPS_LESS_65_548 0 -#define HID_MPS_LESS_65_549 0 -#define HID_MPS_LESS_65_550 0 -#define HID_MPS_LESS_65_551 0 -#define HID_MPS_LESS_65_552 0 -#define HID_MPS_LESS_65_553 0 -#define HID_MPS_LESS_65_554 0 -#define HID_MPS_LESS_65_555 0 -#define HID_MPS_LESS_65_556 0 -#define HID_MPS_LESS_65_557 0 -#define HID_MPS_LESS_65_558 0 -#define HID_MPS_LESS_65_559 0 -#define HID_MPS_LESS_65_560 0 -#define HID_MPS_LESS_65_561 0 -#define HID_MPS_LESS_65_562 0 -#define HID_MPS_LESS_65_563 0 -#define HID_MPS_LESS_65_564 0 -#define HID_MPS_LESS_65_565 0 -#define HID_MPS_LESS_65_566 0 -#define HID_MPS_LESS_65_567 0 -#define HID_MPS_LESS_65_568 0 -#define HID_MPS_LESS_65_569 0 -#define HID_MPS_LESS_65_570 0 -#define HID_MPS_LESS_65_571 0 -#define HID_MPS_LESS_65_572 0 -#define HID_MPS_LESS_65_573 0 -#define HID_MPS_LESS_65_574 0 -#define HID_MPS_LESS_65_575 0 -#define HID_MPS_LESS_65_576 0 -#define HID_MPS_LESS_65_577 0 -#define HID_MPS_LESS_65_578 0 -#define HID_MPS_LESS_65_579 0 -#define HID_MPS_LESS_65_580 0 -#define HID_MPS_LESS_65_581 0 -#define HID_MPS_LESS_65_582 0 -#define HID_MPS_LESS_65_583 0 -#define HID_MPS_LESS_65_584 0 -#define HID_MPS_LESS_65_585 0 -#define HID_MPS_LESS_65_586 0 -#define HID_MPS_LESS_65_587 0 -#define HID_MPS_LESS_65_588 0 -#define HID_MPS_LESS_65_589 0 -#define HID_MPS_LESS_65_590 0 -#define HID_MPS_LESS_65_591 0 -#define HID_MPS_LESS_65_592 0 -#define HID_MPS_LESS_65_593 0 -#define HID_MPS_LESS_65_594 0 -#define HID_MPS_LESS_65_595 0 -#define HID_MPS_LESS_65_596 0 -#define HID_MPS_LESS_65_597 0 -#define HID_MPS_LESS_65_598 0 -#define HID_MPS_LESS_65_599 0 -#define HID_MPS_LESS_65_600 0 -#define HID_MPS_LESS_65_601 0 -#define HID_MPS_LESS_65_602 0 -#define HID_MPS_LESS_65_603 0 -#define HID_MPS_LESS_65_604 0 -#define HID_MPS_LESS_65_605 0 -#define HID_MPS_LESS_65_606 0 -#define HID_MPS_LESS_65_607 0 -#define HID_MPS_LESS_65_608 0 -#define HID_MPS_LESS_65_609 0 -#define HID_MPS_LESS_65_610 0 -#define HID_MPS_LESS_65_611 0 -#define HID_MPS_LESS_65_612 0 -#define HID_MPS_LESS_65_613 0 -#define HID_MPS_LESS_65_614 0 -#define HID_MPS_LESS_65_615 0 -#define HID_MPS_LESS_65_616 0 -#define HID_MPS_LESS_65_617 0 -#define HID_MPS_LESS_65_618 0 -#define HID_MPS_LESS_65_619 0 -#define HID_MPS_LESS_65_620 0 -#define HID_MPS_LESS_65_621 0 -#define HID_MPS_LESS_65_622 0 -#define HID_MPS_LESS_65_623 0 -#define HID_MPS_LESS_65_624 0 -#define HID_MPS_LESS_65_625 0 -#define HID_MPS_LESS_65_626 0 -#define HID_MPS_LESS_65_627 0 -#define HID_MPS_LESS_65_628 0 -#define HID_MPS_LESS_65_629 0 -#define HID_MPS_LESS_65_630 0 -#define HID_MPS_LESS_65_631 0 -#define HID_MPS_LESS_65_632 0 -#define HID_MPS_LESS_65_633 0 -#define HID_MPS_LESS_65_634 0 -#define HID_MPS_LESS_65_635 0 -#define HID_MPS_LESS_65_636 0 -#define HID_MPS_LESS_65_637 0 -#define HID_MPS_LESS_65_638 0 -#define HID_MPS_LESS_65_639 0 -#define HID_MPS_LESS_65_640 0 -#define HID_MPS_LESS_65_641 0 -#define HID_MPS_LESS_65_642 0 -#define HID_MPS_LESS_65_643 0 -#define HID_MPS_LESS_65_644 0 -#define HID_MPS_LESS_65_645 0 -#define HID_MPS_LESS_65_646 0 -#define HID_MPS_LESS_65_647 0 -#define HID_MPS_LESS_65_648 0 -#define HID_MPS_LESS_65_649 0 -#define HID_MPS_LESS_65_650 0 -#define HID_MPS_LESS_65_651 0 -#define HID_MPS_LESS_65_652 0 -#define HID_MPS_LESS_65_653 0 -#define HID_MPS_LESS_65_654 0 -#define HID_MPS_LESS_65_655 0 -#define HID_MPS_LESS_65_656 0 -#define HID_MPS_LESS_65_657 0 -#define HID_MPS_LESS_65_658 0 -#define HID_MPS_LESS_65_659 0 -#define HID_MPS_LESS_65_660 0 -#define HID_MPS_LESS_65_661 0 -#define HID_MPS_LESS_65_662 0 -#define HID_MPS_LESS_65_663 0 -#define HID_MPS_LESS_65_664 0 -#define HID_MPS_LESS_65_665 0 -#define HID_MPS_LESS_65_666 0 -#define HID_MPS_LESS_65_667 0 -#define HID_MPS_LESS_65_668 0 -#define HID_MPS_LESS_65_669 0 -#define HID_MPS_LESS_65_670 0 -#define HID_MPS_LESS_65_671 0 -#define HID_MPS_LESS_65_672 0 -#define HID_MPS_LESS_65_673 0 -#define HID_MPS_LESS_65_674 0 -#define HID_MPS_LESS_65_675 0 -#define HID_MPS_LESS_65_676 0 -#define HID_MPS_LESS_65_677 0 -#define HID_MPS_LESS_65_678 0 -#define HID_MPS_LESS_65_679 0 -#define HID_MPS_LESS_65_680 0 -#define HID_MPS_LESS_65_681 0 -#define HID_MPS_LESS_65_682 0 -#define HID_MPS_LESS_65_683 0 -#define HID_MPS_LESS_65_684 0 -#define HID_MPS_LESS_65_685 0 -#define HID_MPS_LESS_65_686 0 -#define HID_MPS_LESS_65_687 0 -#define HID_MPS_LESS_65_688 0 -#define HID_MPS_LESS_65_689 0 -#define HID_MPS_LESS_65_690 0 -#define HID_MPS_LESS_65_691 0 -#define HID_MPS_LESS_65_692 0 -#define HID_MPS_LESS_65_693 0 -#define HID_MPS_LESS_65_694 0 -#define HID_MPS_LESS_65_695 0 -#define HID_MPS_LESS_65_696 0 -#define HID_MPS_LESS_65_697 0 -#define HID_MPS_LESS_65_698 0 -#define HID_MPS_LESS_65_699 0 -#define HID_MPS_LESS_65_700 0 -#define HID_MPS_LESS_65_701 0 -#define HID_MPS_LESS_65_702 0 -#define HID_MPS_LESS_65_703 0 -#define HID_MPS_LESS_65_704 0 -#define HID_MPS_LESS_65_705 0 -#define HID_MPS_LESS_65_706 0 -#define HID_MPS_LESS_65_707 0 -#define HID_MPS_LESS_65_708 0 -#define HID_MPS_LESS_65_709 0 -#define HID_MPS_LESS_65_710 0 -#define HID_MPS_LESS_65_711 0 -#define HID_MPS_LESS_65_712 0 -#define HID_MPS_LESS_65_713 0 -#define HID_MPS_LESS_65_714 0 -#define HID_MPS_LESS_65_715 0 -#define HID_MPS_LESS_65_716 0 -#define HID_MPS_LESS_65_717 0 -#define HID_MPS_LESS_65_718 0 -#define HID_MPS_LESS_65_719 0 -#define HID_MPS_LESS_65_720 0 -#define HID_MPS_LESS_65_721 0 -#define HID_MPS_LESS_65_722 0 -#define HID_MPS_LESS_65_723 0 -#define HID_MPS_LESS_65_724 0 -#define HID_MPS_LESS_65_725 0 -#define HID_MPS_LESS_65_726 0 -#define HID_MPS_LESS_65_727 0 -#define HID_MPS_LESS_65_728 0 -#define HID_MPS_LESS_65_729 0 -#define HID_MPS_LESS_65_730 0 -#define HID_MPS_LESS_65_731 0 -#define HID_MPS_LESS_65_732 0 -#define HID_MPS_LESS_65_733 0 -#define HID_MPS_LESS_65_734 0 -#define HID_MPS_LESS_65_735 0 -#define HID_MPS_LESS_65_736 0 -#define HID_MPS_LESS_65_737 0 -#define HID_MPS_LESS_65_738 0 -#define HID_MPS_LESS_65_739 0 -#define HID_MPS_LESS_65_740 0 -#define HID_MPS_LESS_65_741 0 -#define HID_MPS_LESS_65_742 0 -#define HID_MPS_LESS_65_743 0 -#define HID_MPS_LESS_65_744 0 -#define HID_MPS_LESS_65_745 0 -#define HID_MPS_LESS_65_746 0 -#define HID_MPS_LESS_65_747 0 -#define HID_MPS_LESS_65_748 0 -#define HID_MPS_LESS_65_749 0 -#define HID_MPS_LESS_65_750 0 -#define HID_MPS_LESS_65_751 0 -#define HID_MPS_LESS_65_752 0 -#define HID_MPS_LESS_65_753 0 -#define HID_MPS_LESS_65_754 0 -#define HID_MPS_LESS_65_755 0 -#define HID_MPS_LESS_65_756 0 -#define HID_MPS_LESS_65_757 0 -#define HID_MPS_LESS_65_758 0 -#define HID_MPS_LESS_65_759 0 -#define HID_MPS_LESS_65_760 0 -#define HID_MPS_LESS_65_761 0 -#define HID_MPS_LESS_65_762 0 -#define HID_MPS_LESS_65_763 0 -#define HID_MPS_LESS_65_764 0 -#define HID_MPS_LESS_65_765 0 -#define HID_MPS_LESS_65_766 0 -#define HID_MPS_LESS_65_767 0 -#define HID_MPS_LESS_65_768 0 -#define HID_MPS_LESS_65_769 0 -#define HID_MPS_LESS_65_770 0 -#define HID_MPS_LESS_65_771 0 -#define HID_MPS_LESS_65_772 0 -#define HID_MPS_LESS_65_773 0 -#define HID_MPS_LESS_65_774 0 -#define HID_MPS_LESS_65_775 0 -#define HID_MPS_LESS_65_776 0 -#define HID_MPS_LESS_65_777 0 -#define HID_MPS_LESS_65_778 0 -#define HID_MPS_LESS_65_779 0 -#define HID_MPS_LESS_65_780 0 -#define HID_MPS_LESS_65_781 0 -#define HID_MPS_LESS_65_782 0 -#define HID_MPS_LESS_65_783 0 -#define HID_MPS_LESS_65_784 0 -#define HID_MPS_LESS_65_785 0 -#define HID_MPS_LESS_65_786 0 -#define HID_MPS_LESS_65_787 0 -#define HID_MPS_LESS_65_788 0 -#define HID_MPS_LESS_65_789 0 -#define HID_MPS_LESS_65_790 0 -#define HID_MPS_LESS_65_791 0 -#define HID_MPS_LESS_65_792 0 -#define HID_MPS_LESS_65_793 0 -#define HID_MPS_LESS_65_794 0 -#define HID_MPS_LESS_65_795 0 -#define HID_MPS_LESS_65_796 0 -#define HID_MPS_LESS_65_797 0 -#define HID_MPS_LESS_65_798 0 -#define HID_MPS_LESS_65_799 0 -#define HID_MPS_LESS_65_800 0 -#define HID_MPS_LESS_65_801 0 -#define HID_MPS_LESS_65_802 0 -#define HID_MPS_LESS_65_803 0 -#define HID_MPS_LESS_65_804 0 -#define HID_MPS_LESS_65_805 0 -#define HID_MPS_LESS_65_806 0 -#define HID_MPS_LESS_65_807 0 -#define HID_MPS_LESS_65_808 0 -#define HID_MPS_LESS_65_809 0 -#define HID_MPS_LESS_65_810 0 -#define HID_MPS_LESS_65_811 0 -#define HID_MPS_LESS_65_812 0 -#define HID_MPS_LESS_65_813 0 -#define HID_MPS_LESS_65_814 0 -#define HID_MPS_LESS_65_815 0 -#define HID_MPS_LESS_65_816 0 -#define HID_MPS_LESS_65_817 0 -#define HID_MPS_LESS_65_818 0 -#define HID_MPS_LESS_65_819 0 -#define HID_MPS_LESS_65_820 0 -#define HID_MPS_LESS_65_821 0 -#define HID_MPS_LESS_65_822 0 -#define HID_MPS_LESS_65_823 0 -#define HID_MPS_LESS_65_824 0 -#define HID_MPS_LESS_65_825 0 -#define HID_MPS_LESS_65_826 0 -#define HID_MPS_LESS_65_827 0 -#define HID_MPS_LESS_65_828 0 -#define HID_MPS_LESS_65_829 0 -#define HID_MPS_LESS_65_830 0 -#define HID_MPS_LESS_65_831 0 -#define HID_MPS_LESS_65_832 0 -#define HID_MPS_LESS_65_833 0 -#define HID_MPS_LESS_65_834 0 -#define HID_MPS_LESS_65_835 0 -#define HID_MPS_LESS_65_836 0 -#define HID_MPS_LESS_65_837 0 -#define HID_MPS_LESS_65_838 0 -#define HID_MPS_LESS_65_839 0 -#define HID_MPS_LESS_65_840 0 -#define HID_MPS_LESS_65_841 0 -#define HID_MPS_LESS_65_842 0 -#define HID_MPS_LESS_65_843 0 -#define HID_MPS_LESS_65_844 0 -#define HID_MPS_LESS_65_845 0 -#define HID_MPS_LESS_65_846 0 -#define HID_MPS_LESS_65_847 0 -#define HID_MPS_LESS_65_848 0 -#define HID_MPS_LESS_65_849 0 -#define HID_MPS_LESS_65_850 0 -#define HID_MPS_LESS_65_851 0 -#define HID_MPS_LESS_65_852 0 -#define HID_MPS_LESS_65_853 0 -#define HID_MPS_LESS_65_854 0 -#define HID_MPS_LESS_65_855 0 -#define HID_MPS_LESS_65_856 0 -#define HID_MPS_LESS_65_857 0 -#define HID_MPS_LESS_65_858 0 -#define HID_MPS_LESS_65_859 0 -#define HID_MPS_LESS_65_860 0 -#define HID_MPS_LESS_65_861 0 -#define HID_MPS_LESS_65_862 0 -#define HID_MPS_LESS_65_863 0 -#define HID_MPS_LESS_65_864 0 -#define HID_MPS_LESS_65_865 0 -#define HID_MPS_LESS_65_866 0 -#define HID_MPS_LESS_65_867 0 -#define HID_MPS_LESS_65_868 0 -#define HID_MPS_LESS_65_869 0 -#define HID_MPS_LESS_65_870 0 -#define HID_MPS_LESS_65_871 0 -#define HID_MPS_LESS_65_872 0 -#define HID_MPS_LESS_65_873 0 -#define HID_MPS_LESS_65_874 0 -#define HID_MPS_LESS_65_875 0 -#define HID_MPS_LESS_65_876 0 -#define HID_MPS_LESS_65_877 0 -#define HID_MPS_LESS_65_878 0 -#define HID_MPS_LESS_65_879 0 -#define HID_MPS_LESS_65_880 0 -#define HID_MPS_LESS_65_881 0 -#define HID_MPS_LESS_65_882 0 -#define HID_MPS_LESS_65_883 0 -#define HID_MPS_LESS_65_884 0 -#define HID_MPS_LESS_65_885 0 -#define HID_MPS_LESS_65_886 0 -#define HID_MPS_LESS_65_887 0 -#define HID_MPS_LESS_65_888 0 -#define HID_MPS_LESS_65_889 0 -#define HID_MPS_LESS_65_890 0 -#define HID_MPS_LESS_65_891 0 -#define HID_MPS_LESS_65_892 0 -#define HID_MPS_LESS_65_893 0 -#define HID_MPS_LESS_65_894 0 -#define HID_MPS_LESS_65_895 0 -#define HID_MPS_LESS_65_896 0 -#define HID_MPS_LESS_65_897 0 -#define HID_MPS_LESS_65_898 0 -#define HID_MPS_LESS_65_899 0 -#define HID_MPS_LESS_65_900 0 -#define HID_MPS_LESS_65_901 0 -#define HID_MPS_LESS_65_902 0 -#define HID_MPS_LESS_65_903 0 -#define HID_MPS_LESS_65_904 0 -#define HID_MPS_LESS_65_905 0 -#define HID_MPS_LESS_65_906 0 -#define HID_MPS_LESS_65_907 0 -#define HID_MPS_LESS_65_908 0 -#define HID_MPS_LESS_65_909 0 -#define HID_MPS_LESS_65_910 0 -#define HID_MPS_LESS_65_911 0 -#define HID_MPS_LESS_65_912 0 -#define HID_MPS_LESS_65_913 0 -#define HID_MPS_LESS_65_914 0 -#define HID_MPS_LESS_65_915 0 -#define HID_MPS_LESS_65_916 0 -#define HID_MPS_LESS_65_917 0 -#define HID_MPS_LESS_65_918 0 -#define HID_MPS_LESS_65_919 0 -#define HID_MPS_LESS_65_920 0 -#define HID_MPS_LESS_65_921 0 -#define HID_MPS_LESS_65_922 0 -#define HID_MPS_LESS_65_923 0 -#define HID_MPS_LESS_65_924 0 -#define HID_MPS_LESS_65_925 0 -#define HID_MPS_LESS_65_926 0 -#define HID_MPS_LESS_65_927 0 -#define HID_MPS_LESS_65_928 0 -#define HID_MPS_LESS_65_929 0 -#define HID_MPS_LESS_65_930 0 -#define HID_MPS_LESS_65_931 0 -#define HID_MPS_LESS_65_932 0 -#define HID_MPS_LESS_65_933 0 -#define HID_MPS_LESS_65_934 0 -#define HID_MPS_LESS_65_935 0 -#define HID_MPS_LESS_65_936 0 -#define HID_MPS_LESS_65_937 0 -#define HID_MPS_LESS_65_938 0 -#define HID_MPS_LESS_65_939 0 -#define HID_MPS_LESS_65_940 0 -#define HID_MPS_LESS_65_941 0 -#define HID_MPS_LESS_65_942 0 -#define HID_MPS_LESS_65_943 0 -#define HID_MPS_LESS_65_944 0 -#define HID_MPS_LESS_65_945 0 -#define HID_MPS_LESS_65_946 0 -#define HID_MPS_LESS_65_947 0 -#define HID_MPS_LESS_65_948 0 -#define HID_MPS_LESS_65_949 0 -#define HID_MPS_LESS_65_950 0 -#define HID_MPS_LESS_65_951 0 -#define HID_MPS_LESS_65_952 0 -#define HID_MPS_LESS_65_953 0 -#define HID_MPS_LESS_65_954 0 -#define HID_MPS_LESS_65_955 0 -#define HID_MPS_LESS_65_956 0 -#define HID_MPS_LESS_65_957 0 -#define HID_MPS_LESS_65_958 0 -#define HID_MPS_LESS_65_959 0 -#define HID_MPS_LESS_65_960 0 -#define HID_MPS_LESS_65_961 0 -#define HID_MPS_LESS_65_962 0 -#define HID_MPS_LESS_65_963 0 -#define HID_MPS_LESS_65_964 0 -#define HID_MPS_LESS_65_965 0 -#define HID_MPS_LESS_65_966 0 -#define HID_MPS_LESS_65_967 0 -#define HID_MPS_LESS_65_968 0 -#define HID_MPS_LESS_65_969 0 -#define HID_MPS_LESS_65_970 0 -#define HID_MPS_LESS_65_971 0 -#define HID_MPS_LESS_65_972 0 -#define HID_MPS_LESS_65_973 0 -#define HID_MPS_LESS_65_974 0 -#define HID_MPS_LESS_65_975 0 -#define HID_MPS_LESS_65_976 0 -#define HID_MPS_LESS_65_977 0 -#define HID_MPS_LESS_65_978 0 -#define HID_MPS_LESS_65_979 0 -#define HID_MPS_LESS_65_980 0 -#define HID_MPS_LESS_65_981 0 -#define HID_MPS_LESS_65_982 0 -#define HID_MPS_LESS_65_983 0 -#define HID_MPS_LESS_65_984 0 -#define HID_MPS_LESS_65_985 0 -#define HID_MPS_LESS_65_986 0 -#define HID_MPS_LESS_65_987 0 -#define HID_MPS_LESS_65_988 0 -#define HID_MPS_LESS_65_989 0 -#define HID_MPS_LESS_65_990 0 -#define HID_MPS_LESS_65_991 0 -#define HID_MPS_LESS_65_992 0 -#define HID_MPS_LESS_65_993 0 -#define HID_MPS_LESS_65_994 0 -#define HID_MPS_LESS_65_995 0 -#define HID_MPS_LESS_65_996 0 -#define HID_MPS_LESS_65_997 0 -#define HID_MPS_LESS_65_998 0 -#define HID_MPS_LESS_65_999 0 -#define HID_MPS_LESS_65_1000 0 -#define HID_MPS_LESS_65_1001 0 -#define HID_MPS_LESS_65_1002 0 -#define HID_MPS_LESS_65_1003 0 -#define HID_MPS_LESS_65_1004 0 -#define HID_MPS_LESS_65_1005 0 -#define HID_MPS_LESS_65_1006 0 -#define HID_MPS_LESS_65_1007 0 -#define HID_MPS_LESS_65_1008 0 -#define HID_MPS_LESS_65_1009 0 -#define HID_MPS_LESS_65_1010 0 -#define HID_MPS_LESS_65_1011 0 -#define HID_MPS_LESS_65_1012 0 -#define HID_MPS_LESS_65_1013 0 -#define HID_MPS_LESS_65_1014 0 -#define HID_MPS_LESS_65_1015 0 -#define HID_MPS_LESS_65_1016 0 -#define HID_MPS_LESS_65_1017 0 -#define HID_MPS_LESS_65_1018 0 -#define HID_MPS_LESS_65_1019 0 -#define HID_MPS_LESS_65_1020 0 -#define HID_MPS_LESS_65_1021 0 -#define HID_MPS_LESS_65_1022 0 -#define HID_MPS_LESS_65_1023 0 -#define HID_MPS_LESS_65_1024 0 #define HID_MPS_LESS_65(x) UTIL_PRIMITIVE_CAT(HID_MPS_LESS_65_, x) @@ -1052,8 +92,8 @@ * configure an alternate interface. */ #define HID_ALL_MPS_LESS_65(n) \ - UTIL_AND(HID_MPS_LESS_65(DT_INST_PROP(n, out_report_size)), \ - HID_MPS_LESS_65(DT_INST_PROP(n, in_report_size))) + COND_CODE_1(HID_MPS_LESS_65(DT_INST_PROP_OR(n, out_report_size, 0)), \ + (HID_MPS_LESS_65(DT_INST_PROP(n, in_report_size))), (0)) /* Get IN endpoint polling rate based on the desired speed. */ #define HID_IN_EP_INTERVAL(n, hs) \ From efc269879c9cdccd2abca594a7a2ce3ada2b4f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 19 Jul 2024 08:33:08 +0200 Subject: [PATCH 28/39] [nrf fromtree] usb: device_next: Make stack High-Bandwidth aware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add macros for converting between Max Packet Size and total payload length. Allow drivers specify whether endpoint supports high-bandwidth interrupt and high-bandwidth isochronous transfers. Signed-off-by: Tomasz Moń (cherry picked from commit b7664f27c8f4d7dd88f35a23379c37bee6748037) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_common.c | 10 +++++--- include/zephyr/drivers/usb/udc.h | 2 ++ include/zephyr/usb/usb_ch9.h | 23 +++++++++++++++++++ subsys/usb/device_next/class/usbd_hid.c | 2 ++ .../usb/device_next/class/usbd_hid_macros.h | 10 ++++++-- tests/drivers/udc/src/main.c | 4 ++-- 6 files changed, 44 insertions(+), 7 deletions(-) diff --git a/drivers/usb/udc/udc_common.c b/drivers/usb/udc/udc_common.c index 009fb085a5d..6ef33cc1d30 100644 --- a/drivers/usb/udc/udc_common.c +++ b/drivers/usb/udc/udc_common.c @@ -262,7 +262,7 @@ static bool ep_check_config(const struct device *dev, return false; } - if (mps > cfg->caps.mps) { + if (USB_MPS_EP_SIZE(mps) > USB_MPS_EP_SIZE(cfg->caps.mps)) { return false; } @@ -273,12 +273,16 @@ static bool ep_check_config(const struct device *dev, } break; case USB_EP_TYPE_INTERRUPT: - if (!cfg->caps.interrupt) { + if (!cfg->caps.interrupt || + (USB_MPS_ADDITIONAL_TRANSACTIONS(mps) && + !cfg->caps.high_bandwidth)) { return false; } break; case USB_EP_TYPE_ISO: - if (!cfg->caps.iso) { + if (!cfg->caps.iso || + (USB_MPS_ADDITIONAL_TRANSACTIONS(mps) && + !cfg->caps.high_bandwidth)) { return false; } break; diff --git a/include/zephyr/drivers/usb/udc.h b/include/zephyr/drivers/usb/udc.h index 7d7c8ee07c5..a757a74d964 100644 --- a/include/zephyr/drivers/usb/udc.h +++ b/include/zephyr/drivers/usb/udc.h @@ -76,6 +76,8 @@ struct udc_ep_caps { uint32_t bulk : 1; /** ISO transfer capable endpoint */ uint32_t iso : 1; + /** High-Bandwidth (interrupt or iso) capable endpoint */ + uint32_t high_bandwidth : 1; /** IN transfer capable endpoint */ uint32_t in : 1; /** OUT transfer capable endpoint */ diff --git a/include/zephyr/usb/usb_ch9.h b/include/zephyr/usb/usb_ch9.h index 8fe33f46b71..b486466cb9c 100644 --- a/include/zephyr/usb/usb_ch9.h +++ b/include/zephyr/usb/usb_ch9.h @@ -348,6 +348,29 @@ struct usb_association_descriptor { /** Calculate high speed isochronous endpoint bInterval from a value in microseconds */ #define USB_HS_ISO_EP_INTERVAL(us) CLAMP((ilog2((us) / 125U) + 1U), 1U, 16U) +/** Get endpoint size field from Max Packet Size value */ +#define USB_MPS_EP_SIZE(mps) ((mps) & BIT_MASK(11)) + +/** Get number of additional transactions per microframe from Max Packet Size value */ +#define USB_MPS_ADDITIONAL_TRANSACTIONS(mps) (((mps) & 0x1800) >> 11) + +/** Calculate total payload length from Max Packet Size value */ +#define USB_MPS_TO_TPL(mps) \ + ((1 + USB_MPS_ADDITIONAL_TRANSACTIONS(mps)) * USB_MPS_EP_SIZE(mps)) + +/** Calculate Max Packet Size value from total payload length */ +#define USB_TPL_TO_MPS(tpl) \ + (((tpl) > 2048) ? ((2 << 11) | ((tpl) / 3)) : \ + ((tpl) > 1024) ? ((1 << 11) | ((tpl) / 2)) : \ + (tpl)) + +/** Determine whether total payload length value is valid according to USB 2.0 */ +#define USB_TPL_IS_VALID(tpl) \ + (((tpl) > 3072) ? false : \ + ((tpl) > 2048) ? ((tpl) % 3 == 0) : \ + ((tpl) > 1024) ? ((tpl) % 2 == 0) : \ + ((tpl) >= 0)) + #ifdef __cplusplus } #endif diff --git a/subsys/usb/device_next/class/usbd_hid.c b/subsys/usb/device_next/class/usbd_hid.c index d32715f6ed4..068a3ff170b 100644 --- a/subsys/usb/device_next/class/usbd_hid.c +++ b/subsys/usb/device_next/class/usbd_hid.c @@ -726,6 +726,8 @@ static const struct hid_device_driver_api hid_device_api = { (USBD_HID_INTERFACE_ALTERNATE_DEFINE(n))) #define USBD_HID_INSTANCE_DEFINE(n) \ + HID_VERIFY_REPORT_SIZES(n); \ + \ NET_BUF_POOL_DEFINE(hid_buf_pool_in_##n, \ CONFIG_USBD_HID_IN_BUF_COUNT, 0, \ sizeof(struct udc_buf_info), NULL); \ diff --git a/subsys/usb/device_next/class/usbd_hid_macros.h b/subsys/usb/device_next/class/usbd_hid_macros.h index 952384a4888..6a16557bf42 100644 --- a/subsys/usb/device_next/class/usbd_hid_macros.h +++ b/subsys/usb/device_next/class/usbd_hid_macros.h @@ -153,7 +153,7 @@ */ #define HID_OUT_EP_MPS(n, alt) \ COND_CODE_1(alt, \ - (sys_cpu_to_le16(DT_INST_PROP(n, out_report_size))), \ + (sys_cpu_to_le16(USB_TPL_TO_MPS(DT_INST_PROP(n, out_report_size)))), \ (sys_cpu_to_le16(MIN(DT_INST_PROP(n, out_report_size), 64U)))) /* @@ -162,7 +162,7 @@ */ #define HID_IN_EP_MPS(n, alt) \ COND_CODE_1(alt, \ - (sys_cpu_to_le16(DT_INST_PROP(n, in_report_size))), \ + (sys_cpu_to_le16(USB_TPL_TO_MPS(DT_INST_PROP(n, in_report_size)))), \ (sys_cpu_to_le16(MIN(DT_INST_PROP(n, in_report_size), 64U)))) #define HID_OUT_EP_DEFINE(n, hs, alt) \ @@ -206,4 +206,10 @@ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, out_report_size), \ (&hid_buf_pool_out_##n), (NULL)) +#define HID_VERIFY_REPORT_SIZES(n) \ + BUILD_ASSERT(USB_TPL_IS_VALID(DT_INST_PROP_OR(n, out_report_size, 0)), \ + "out-report-size must be valid Total Packet Length"); \ + BUILD_ASSERT(USB_TPL_IS_VALID(DT_INST_PROP_OR(n, in_report_size, 0)), \ + "in-report-size must be valid Total Packet Length"); + #endif /* ZEPHYR_USB_DEVICE_CLASS_HID_MACROS_H_ */ diff --git a/tests/drivers/udc/src/main.c b/tests/drivers/udc/src/main.c index 86565aaad9b..6ca5d8a2548 100644 --- a/tests/drivers/udc/src/main.c +++ b/tests/drivers/udc/src/main.c @@ -193,7 +193,7 @@ static struct net_buf *test_udc_ep_buf_alloc(const struct device *dev, struct net_buf *buf; buf = udc_ep_buf_alloc(dev, ed->bEndpointAddress, - sys_le16_to_cpu(ed->wMaxPacketSize)); + USB_MPS_TO_TPL(sys_le16_to_cpu(ed->wMaxPacketSize))); zassert_not_null(buf, "Failed to allocate request"); @@ -340,7 +340,7 @@ static void test_udc_ep_api(const struct device *dev, /* It needs a little reserve for memory management overhead. */ for (int n = 0; n < (CONFIG_UDC_BUF_COUNT - 2); n++) { buf = udc_ep_buf_alloc(dev, ed->bEndpointAddress, - sys_le16_to_cpu(ed->wMaxPacketSize)); + USB_MPS_TO_TPL(sys_le16_to_cpu(ed->wMaxPacketSize))); zassert_not_null(buf, "Failed to allocate request (%d) for 0x%02x", n, ed->bEndpointAddress); From 3a0934832d32bd411453bb5fe55570511c5c4b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 19 Jul 2024 09:15:23 +0200 Subject: [PATCH 29/39] [nrf fromtree] drivers: udc_dwc2: Allow larger TxFIFO writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DMA is capable of packetizing transfers on its own and the driver doesn't need to check available TxFIFO size when scheduling DMA transfer. Split packets on DWORD boundary when writing more than one packet to TxFIFO in completer mode. Signed-off-by: Tomasz Moń (cherry picked from commit 07ea4aa15566852b6d6d961b45ac59f07195db9d) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 79 +++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index eefba16ecda..54d7968cd62 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -288,23 +288,29 @@ static int dwc2_tx_fifo_write(const struct device *dev, unsigned int key; uint32_t len; - spcavail = dwc2_ftx_avail(dev, ep_idx); - /* Round down to multiple of endpoint MPS */ - spcavail -= spcavail % cfg->mps; - /* - * Here, the available space should be equal to the FIFO space - * assigned/configured for that endpoint because we do not schedule another - * transfer until the previous one has not finished. For simplicity, - * we only check that the available space is not less than the endpoint - * MPS. - */ - if (spcavail < cfg->mps) { - LOG_ERR("ep 0x%02x FIFO space is too low, %u (%u)", - cfg->addr, spcavail, dwc2_ftx_avail(dev, ep_idx)); - return -EAGAIN; - } + if (priv->bufferdma) { + /* DMA automatically handles packet split */ + len = buf->len; + } else { + uint32_t spcavail = dwc2_ftx_avail(dev, ep_idx); + + /* Round down to multiple of endpoint MPS */ + spcavail -= spcavail % cfg->mps; + + /* Here, the available space should be equal to the FIFO space + * assigned/configured for that endpoint because we do not + * schedule another transfer until the previous one has not + * finished. For simplicity, we only check that the available + * space is not less than the endpoint MPS. + */ + if (spcavail < cfg->mps) { + LOG_ERR("ep 0x%02x FIFO space is too low, %u (%u)", + cfg->addr, spcavail, dwc2_ftx_avail(dev, ep_idx)); + return -EAGAIN; + } - len = MIN(buf->len, spcavail); + len = MIN(buf->len, spcavail); + } if (len != 0U) { max_pktcnt = dwc2_get_iept_pktctn(dev, ep_idx); @@ -332,8 +338,8 @@ static int dwc2_tx_fifo_write(const struct device *dev, pktcnt = 1U; } - LOG_DBG("Prepare ep 0x%02x xfer len %u pktcnt %u spcavail %u", - cfg->addr, len, pktcnt, spcavail); + LOG_DBG("Prepare ep 0x%02x xfer len %u pktcnt %u", + cfg->addr, len, pktcnt); priv->tx_len[ep_idx] = len; /* Lock and write to endpoint FIFO */ @@ -363,22 +369,31 @@ static int dwc2_tx_fifo_write(const struct device *dev, sys_write32(USB_DWC2_DIEPINT_INEPNAKEFF, diepint_reg); if (!priv->bufferdma) { - /* FIFO access is always in 32-bit words */ - - for (uint32_t i = 0UL; i < len; i += d) { - uint32_t val = buf->data[i]; - - if (i + 1 < len) { - val |= ((uint32_t)buf->data[i + 1UL]) << 8; - } - if (i + 2 < len) { - val |= ((uint32_t)buf->data[i + 2UL]) << 16; - } - if (i + 3 < len) { - val |= ((uint32_t)buf->data[i + 3UL]) << 24; + const uint8_t *src = buf->data; + uint32_t pktlen; + + while (pktcnt > 0) { + uint32_t pktlen = MIN(len, cfg->mps); + + for (uint32_t i = 0UL; i < pktlen; i += d) { + uint32_t val = src[i]; + + if (i + 1 < pktlen) { + val |= ((uint32_t)src[i + 1UL]) << 8; + } + if (i + 2 < pktlen) { + val |= ((uint32_t)src[i + 2UL]) << 16; + } + if (i + 3 < pktlen) { + val |= ((uint32_t)src[i + 3UL]) << 24; + } + + sys_write32(val, UDC_DWC2_EP_FIFO(base, ep_idx)); } - sys_write32(val, UDC_DWC2_EP_FIFO(base, ep_idx)); + pktcnt--; + src += pktlen; + len -= pktlen; } } From 10c2d4b738def564c65e23f3960d7bc837a213e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 19 Jul 2024 09:57:21 +0200 Subject: [PATCH 30/39] [nrf fromtree] drivers: udc_dwc2: Support High-Bandwidth endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make driver aware of High-Bandwidth endpoints both in Completer and Buffer DMA mode. In Completer mode TxFIFO must be able to hold all packets for microframe, while in Buffer DMA mode space enough for two packets is sufficient. Signed-off-by: Tomasz Moń (cherry picked from commit 07bd625196bc606fe4a01de069ce577b0afbdae3) Signed-off-by: Tomasz Moń --- drivers/usb/common/usb_dwc2_hw.h | 4 + drivers/usb/udc/udc_dwc2.c | 156 ++++++++++++++++++++++--------- 2 files changed, 117 insertions(+), 43 deletions(-) diff --git a/drivers/usb/common/usb_dwc2_hw.h b/drivers/usb/common/usb_dwc2_hw.h index 88d32a9137b..5203cb0e21f 100644 --- a/drivers/usb/common/usb_dwc2_hw.h +++ b/drivers/usb/common/usb_dwc2_hw.h @@ -755,13 +755,17 @@ USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_xfersize, DEPTSIZ0_XFERSIZE) * IN at offsets 0x0910 + (0x20 * n), n = 1 .. x, * OUT at offsets 0x0B10 + (0x20 * n), n = 1 .. x */ +#define USB_DWC2_DEPTSIZN_MC_POS 29UL +#define USB_DWC2_DEPTSIZN_MC_MASK (0x3UL << USB_DWC2_DEPTSIZN_MC_POS) #define USB_DWC2_DEPTSIZN_PKTCNT_POS 19UL #define USB_DWC2_DEPTSIZN_PKTCNT_MASK (0x3FFUL << USB_DWC2_DEPTSIZN_PKTCNT_POS) #define USB_DWC2_DEPTSIZN_XFERSIZE_POS 0UL #define USB_DWC2_DEPTSIZN_XFERSIZE_MASK 0x7FFFFUL +USB_DWC2_GET_FIELD_DEFINE(deptsizn_mc, DEPTSIZN_MC) USB_DWC2_GET_FIELD_DEFINE(deptsizn_pktcnt, DEPTSIZN_PKTCNT) USB_DWC2_GET_FIELD_DEFINE(deptsizn_xfersize, DEPTSIZN_XFERSIZE) +USB_DWC2_SET_FIELD_DEFINE(deptsizn_mc, DEPTSIZN_MC) USB_DWC2_SET_FIELD_DEFINE(deptsizn_pktcnt, DEPTSIZN_PKTCNT) USB_DWC2_SET_FIELD_DEFINE(deptsizn_xfersize, DEPTSIZN_XFERSIZE) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 54d7968cd62..466611e895f 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -50,7 +50,14 @@ K_MSGQ_DEFINE(drv_msgq, sizeof(struct dwc2_drv_event), * of 512 bytes. The value must be adjusted according to the number of OUT * endpoints. */ -#define UDC_DWC2_GRXFSIZ_DEFAULT (15U + 512U/4U) +#define UDC_DWC2_GRXFSIZ_FS_DEFAULT (15U + 512U/4U) +/* Default Rx FIFO size in 32-bit words calculated to support High-Speed with: + * * 1 control endpoint in Completer/Buffer DMA mode: 13 locations + * * Global OUT NAK: 1 location + * * Space for 3 * 1024 packets: ((1024/4) + 1) * 3 = 774 locations + * Driver adds 2 locations for each OUT endpoint to this value. + */ +#define UDC_DWC2_GRXFSIZ_HS_DEFAULT (13 + 1 + 774) /* TX FIFO0 depth in 32-bit words (used by control IN endpoint) */ #define UDC_DWC2_FIFO0_DEPTH 16U @@ -251,6 +258,18 @@ static void dwc2_set_epint(const struct device *dev, } } +static bool dwc2_ep_is_periodic(struct udc_ep_config *const cfg) +{ + switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_INTERRUPT: + __fallthrough; + case USB_EP_TYPE_ISO: + return true; + default: + return false; + } +} + static bool dwc2_dma_buffer_ok_to_use(const struct device *dev, void *buf, uint32_t xfersize, uint16_t mps) { @@ -261,8 +280,8 @@ static bool dwc2_dma_buffer_ok_to_use(const struct device *dev, void *buf, return false; } - /* If Max Packet Size is not */ - if (unlikely(mps % 4) && (xfersize > mps)) { + /* We can only do 1 packet if Max Packet Size is not multiple of 4 */ + if (unlikely(mps % 4) && (xfersize > USB_MPS_EP_SIZE(mps))) { LOG_ERR("Padding not supported"); return false; } @@ -283,33 +302,41 @@ static int dwc2_tx_fifo_write(const struct device *dev, mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl; mem_addr_t diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint; - uint32_t max_xfersize, max_pktcnt, pktcnt, spcavail; + uint32_t max_xfersize, max_pktcnt, pktcnt; + const uint32_t addnl = USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps); const size_t d = sizeof(uint32_t); unsigned int key; uint32_t len; + const bool is_periodic = dwc2_ep_is_periodic(cfg); if (priv->bufferdma) { /* DMA automatically handles packet split */ len = buf->len; } else { uint32_t spcavail = dwc2_ftx_avail(dev, ep_idx); + uint32_t spcperpkt = ROUND_UP(USB_MPS_EP_SIZE(cfg->mps), 4); + uint32_t max_pkts, max_transfer; + + /* Maximum number of packets that can fit in TxFIFO */ + max_pkts = spcavail / spcperpkt; - /* Round down to multiple of endpoint MPS */ - spcavail -= spcavail % cfg->mps; + /* We can transfer up to max_pkts MPS packets and a short one */ + max_transfer = (max_pkts * USB_MPS_EP_SIZE(cfg->mps)) + + (spcavail % spcperpkt); - /* Here, the available space should be equal to the FIFO space - * assigned/configured for that endpoint because we do not - * schedule another transfer until the previous one has not - * finished. For simplicity, we only check that the available - * space is not less than the endpoint MPS. + /* If there is enough space for the transfer, there's no need + * to check any additional conditions. If the transfer is larger + * than TxFIFO then TxFIFO must be able to hold at least one + * packet (for periodic transfers at least the number of packets + * per microframe). */ - if (spcavail < cfg->mps) { + if ((buf->len > max_transfer) && ((1 + addnl) > max_pkts)) { LOG_ERR("ep 0x%02x FIFO space is too low, %u (%u)", - cfg->addr, spcavail, dwc2_ftx_avail(dev, ep_idx)); + cfg->addr, spcavail, buf->len); return -EAGAIN; } - len = MIN(buf->len, spcavail); + len = MIN(buf->len, max_transfer); } if (len != 0U) { @@ -321,32 +348,34 @@ static int dwc2_tx_fifo_write(const struct device *dev, * Avoid short packets if the transfer size cannot be * handled in one set. */ - len = ROUND_DOWN(max_xfersize, cfg->mps); + len = ROUND_DOWN(max_xfersize, USB_MPS_TO_TPL(cfg->mps)); } /* * Determine the number of packets for the current transfer; * if the pktcnt is too large, truncate the actual transfer length. */ - pktcnt = DIV_ROUND_UP(len, cfg->mps); + pktcnt = DIV_ROUND_UP(len, USB_MPS_EP_SIZE(cfg->mps)); if (pktcnt > max_pktcnt) { - pktcnt = max_pktcnt; - len = pktcnt * cfg->mps; + pktcnt = ROUND_DOWN(max_pktcnt, (1 + addnl)); + len = pktcnt * USB_MPS_EP_SIZE(cfg->mps); } } else { /* ZLP */ pktcnt = 1U; } - LOG_DBG("Prepare ep 0x%02x xfer len %u pktcnt %u", - cfg->addr, len, pktcnt); + LOG_DBG("Prepare ep 0x%02x xfer len %u pktcnt %u addnl %u", + cfg->addr, len, pktcnt, addnl); priv->tx_len[ep_idx] = len; /* Lock and write to endpoint FIFO */ key = irq_lock(); /* Set number of packets and transfer size */ - sys_write32((pktcnt << USB_DWC2_DEPTSIZN_PKTCNT_POS) | len, dieptsiz_reg); + sys_write32((is_periodic ? usb_dwc2_set_deptsizn_mc(1 + addnl) : 0) | + usb_dwc2_set_deptsizn_pktcnt(pktcnt) | + usb_dwc2_set_deptsizn_xfersize(len), dieptsiz_reg); if (priv->bufferdma) { if (!dwc2_dma_buffer_ok_to_use(dev, buf->data, len, cfg->mps)) { @@ -370,10 +399,9 @@ static int dwc2_tx_fifo_write(const struct device *dev, if (!priv->bufferdma) { const uint8_t *src = buf->data; - uint32_t pktlen; while (pktcnt > 0) { - uint32_t pktlen = MIN(len, cfg->mps); + uint32_t pktlen = MIN(len, USB_MPS_EP_SIZE(cfg->mps)); for (uint32_t i = 0UL; i < pktlen; i += d) { uint32_t val = src[i]; @@ -445,14 +473,17 @@ static uint32_t dwc2_rx_xfer_size(struct udc_dwc2_data *const priv, /* Do as many packets in a single DMA as possible */ if (size > priv->max_xfersize) { - size = ROUND_DOWN(priv->max_xfersize, cfg->mps); + size = ROUND_DOWN(priv->max_xfersize, + USB_MPS_TO_TPL(cfg->mps)); } } else { /* Completer mode can always program Max Packet Size, RxFLvl * interrupt will drop excessive data if necessary (i.e. buffer - * is too short). + * is too short). The value returned must be Max Packet Size + * multiple in order for dwc2_handle_rxflvl() to correctly + * detect when RX has to be prepared during large transfers. */ - size = cfg->mps; + size = USB_MPS_TO_TPL(cfg->mps); } return size; @@ -467,12 +498,15 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); mem_addr_t doeptsiz_reg = (mem_addr_t)&base->out_ep[ep_idx].doeptsiz; mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx); + uint32_t pktcnt; uint32_t doeptsiz; uint32_t xfersize; xfersize = dwc2_rx_xfer_size(priv, cfg, buf); - doeptsiz = xfersize | usb_dwc2_set_deptsizn_pktcnt(DIV_ROUND_UP(xfersize, cfg->mps)); + pktcnt = DIV_ROUND_UP(xfersize, USB_MPS_EP_SIZE(cfg->mps)); + doeptsiz = usb_dwc2_set_deptsizn_pktcnt(pktcnt) | + usb_dwc2_set_deptsizn_xfersize(xfersize); if (cfg->addr == USB_CONTROL_EP_OUT) { /* Use 1 to allow 8 byte long buffers for SETUP data */ doeptsiz |= (1 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS); @@ -605,7 +639,7 @@ static inline int dwc2_handle_evt_dout(const struct device *dev, buf = udc_buf_get(dev, cfg->addr); if (buf == NULL) { - LOG_ERR("No buffer queued for control ep"); + LOG_ERR("No buffer queued for ep 0x%02x", cfg->addr); return -ENODATA; } @@ -853,8 +887,15 @@ static inline void dwc2_handle_rxflvl(const struct device *dev) break; } - if (net_buf_tailroom(buf) && evt.bcnt == ep_cfg->mps) { - dwc2_prep_rx(dev, buf, ep_cfg, 0); + if (((evt.bcnt % USB_MPS_EP_SIZE(ep_cfg->mps)) == 0) && + net_buf_tailroom(buf)) { + uint32_t doeptsiz; + + /* Prepare next read only when transfer finished */ + doeptsiz = sys_read32((mem_addr_t)&base->out_ep[evt.ep].doeptsiz); + if (usb_dwc2_get_deptsizn_xfersize(doeptsiz) == 0) { + dwc2_prep_rx(dev, buf, ep_cfg, 0); + } } else { k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); } @@ -969,7 +1010,8 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, net_buf_add(buf, evt.bcnt); - if (((evt.bcnt % ep_cfg->mps) == 0) && net_buf_tailroom(buf)) { + if (((evt.bcnt % USB_MPS_EP_SIZE(ep_cfg->mps)) == 0) && + net_buf_tailroom(buf)) { dwc2_prep_rx(dev, buf, ep_cfg, 0); } else { k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); @@ -1149,6 +1191,8 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, { struct udc_dwc2_data *const priv = udc_get_private(dev); uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + const uint32_t addnl = USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps); + uint32_t reqdep; uint32_t txfaddr; uint32_t txfdep; uint32_t tmp; @@ -1156,6 +1200,14 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, /* Keep everything but FIFO number */ tmp = *diepctl & ~USB_DWC2_DEPCTL_TXFNUM_MASK; + reqdep = DIV_ROUND_UP(USB_MPS_EP_SIZE(cfg->mps), 4U); + if (priv->bufferdma) { + /* In DMA mode, TxFIFO capable of holding 2 packets is enough */ + reqdep *= MIN(2, (1 + addnl)); + } else { + reqdep *= (1 + addnl); + } + if (priv->dynfifosizing) { if (priv->txf_set & ~BIT_MASK(ep_idx)) { dwc2_unset_unused_fifo(dev); @@ -1176,7 +1228,7 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, } /* Make sure to not set TxFIFO greater than hardware allows */ - txfdep = DIV_ROUND_UP(cfg->mps, 4U); + txfdep = reqdep; if (txfdep > priv->max_txfifo_depth[ep_idx]) { return -ENOMEM; } @@ -1187,7 +1239,7 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, txfdep = dwc2_get_txfdep(dev, ep_idx - 1); txfaddr = dwc2_get_txfaddr(dev, ep_idx - 1); - if (cfg->mps < txfdep * 4U) { + if (reqdep > txfdep) { return -ENOMEM; } @@ -1279,11 +1331,7 @@ static int udc_dwc2_ep_activate(const struct device *dev, dxepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl; } - if (cfg->mps > usb_dwc2_get_depctl_mps(UINT16_MAX)) { - return -EINVAL; - } - - if (priv->bufferdma && (cfg->mps % 4)) { + if (priv->bufferdma && (USB_MPS_EP_SIZE(cfg->mps) % 4)) { /* TODO: In Buffer DMA mode, DMA will insert padding bytes in * between packets if endpoint Max Packet Size is not multiple * of 4 (DWORD) and single transfer spans across multiple @@ -1303,7 +1351,7 @@ static int udc_dwc2_ep_activate(const struct device *dev, dxepctl = sys_read32(dxepctl_reg); /* Set max packet size */ dxepctl &= ~USB_DWC2_DEPCTL_MPS_MASK; - dxepctl |= cfg->mps << USB_DWC2_DEPCTL_MPS_POS; + dxepctl |= usb_dwc2_set_depctl_mps(USB_MPS_EP_SIZE(cfg->mps)); /* Set endpoint type */ dxepctl &= ~USB_DWC2_DEPCTL_EPTYPE_MASK; @@ -1327,7 +1375,7 @@ static int udc_dwc2_ep_activate(const struct device *dev, return -EINVAL; } - if (USB_EP_DIR_IS_IN(cfg->addr) && cfg->mps != 0U) { + if (USB_EP_DIR_IS_IN(cfg->addr) && USB_MPS_EP_SIZE(cfg->mps) != 0U) { int ret = dwc2_set_dedicated_fifo(dev, cfg, &dxepctl); if (ret) { @@ -1530,7 +1578,8 @@ static int udc_dwc2_ep_deactivate(const struct device *dev, cfg->addr, ep_idx, dxepctl); } - if (USB_EP_DIR_IS_IN(cfg->addr) && cfg->mps != 0U && ep_idx != 0U) { + if (USB_EP_DIR_IS_IN(cfg->addr) && USB_MPS_EP_SIZE(cfg->mps) != 0U && + ep_idx != 0U) { dwc2_unset_dedicated_fifo(dev, cfg, &dxepctl); } @@ -1757,6 +1806,7 @@ static int udc_dwc2_init_controller(const struct device *dev) uint32_t ghwcfg4; uint32_t val; int ret; + bool hs_phy; ret = dwc2_core_soft_reset(dev); if (ret) { @@ -1857,12 +1907,14 @@ static int udc_dwc2_init_controller(const struct device *dev) USB_DWC2_GUSBCFG_ULPI_UTMI_SEL_ULPI; dcfg |= USB_DWC2_DCFG_DEVSPD_USBHS20 << USB_DWC2_DCFG_DEVSPD_POS; + hs_phy = true; break; case USB_DWC2_GHWCFG2_HSPHYTYPE_UTMIPLUS: gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB20 | USB_DWC2_GUSBCFG_ULPI_UTMI_SEL_UTMI; dcfg |= USB_DWC2_DCFG_DEVSPD_USBHS20 << USB_DWC2_DCFG_DEVSPD_POS; + hs_phy = true; break; case USB_DWC2_GHWCFG2_HSPHYTYPE_NO_HS: __fallthrough; @@ -1874,6 +1926,7 @@ static int udc_dwc2_init_controller(const struct device *dev) dcfg |= USB_DWC2_DCFG_DEVSPD_USBFS1148 << USB_DWC2_DCFG_DEVSPD_POS; + hs_phy = false; } if (usb_dwc2_get_ghwcfg4_phydatawidth(ghwcfg4)) { @@ -1912,13 +1965,28 @@ static int udc_dwc2_init_controller(const struct device *dev) if (priv->dynfifosizing) { uint32_t gnptxfsiz; + uint32_t default_depth; + + /* TODO: For proper runtime FIFO sizing UDC driver would have to + * have prior knowledge of the USB configurations. Only with the + * prior knowledge, the driver will be able to fairly distribute + * available resources. For the time being just use different + * defaults based on maximum configured PHY speed, but this has + * to be revised if e.g. thresholding support would be necessary + * on some target. + */ + if (hs_phy) { + default_depth = UDC_DWC2_GRXFSIZ_HS_DEFAULT; + } else { + default_depth = UDC_DWC2_GRXFSIZ_FS_DEFAULT; + } + default_depth += priv->outeps * 2U; /* Driver does not dynamically resize RxFIFO so there is no need * to store reset value. Read the reset value and make sure that * the programmed value is not greater than what driver sets. */ - priv->rxfifo_depth = MIN(priv->rxfifo_depth, - UDC_DWC2_GRXFSIZ_DEFAULT + priv->outeps * 2U); + priv->rxfifo_depth = MIN(priv->rxfifo_depth, default_depth); sys_write32(usb_dwc2_set_grxfsiz(priv->rxfifo_depth), grxfsiz_reg); /* Set TxFIFO 0 depth */ @@ -2098,6 +2166,7 @@ static int dwc2_driver_preinit(const struct device *dev) config->ep_cfg_out[n].caps.bulk = 1; config->ep_cfg_out[n].caps.interrupt = 1; config->ep_cfg_out[n].caps.iso = 1; + config->ep_cfg_out[n].caps.high_bandwidth = data->caps.hs; config->ep_cfg_out[n].caps.mps = mps; } @@ -2133,6 +2202,7 @@ static int dwc2_driver_preinit(const struct device *dev) config->ep_cfg_in[n].caps.bulk = 1; config->ep_cfg_in[n].caps.interrupt = 1; config->ep_cfg_in[n].caps.iso = 1; + config->ep_cfg_in[n].caps.high_bandwidth = data->caps.hs; config->ep_cfg_in[n].caps.mps = mps; } From 3051f2adc747d2f3db7b069cf3bff9b63f05383d Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 15 Aug 2024 14:26:27 +0200 Subject: [PATCH 31/39] [nrf fromtree] drivers: udc: do not use the MPS value directly for the endpoint size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead, use the helper to get the size field from the MPS value. MPS value may contain information about additional transaction per microframe (bits 12..11). Signed-off-by: Johann Fischer (cherry picked from commit 8617e468493761c02fa4e4d70735a3064b278a6c) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 28 +++++++++++++--------------- drivers/usb/udc/udc_it82xx2.c | 6 +++--- drivers/usb/udc/udc_kinetis.c | 11 ++++++----- drivers/usb/udc/udc_mcux_ehci.c | 2 +- drivers/usb/udc/udc_mcux_ip3511.c | 2 +- drivers/usb/udc/udc_nrf.c | 2 +- drivers/usb/udc/udc_stm32.c | 8 ++++---- drivers/usb/udc/udc_virtual.c | 6 +++--- include/zephyr/drivers/usb/udc.h | 12 ++++++++++++ 9 files changed, 44 insertions(+), 33 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 466611e895f..61274b2b934 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -314,14 +314,14 @@ static int dwc2_tx_fifo_write(const struct device *dev, len = buf->len; } else { uint32_t spcavail = dwc2_ftx_avail(dev, ep_idx); - uint32_t spcperpkt = ROUND_UP(USB_MPS_EP_SIZE(cfg->mps), 4); + uint32_t spcperpkt = ROUND_UP(udc_mps_ep_size(cfg), 4); uint32_t max_pkts, max_transfer; /* Maximum number of packets that can fit in TxFIFO */ max_pkts = spcavail / spcperpkt; /* We can transfer up to max_pkts MPS packets and a short one */ - max_transfer = (max_pkts * USB_MPS_EP_SIZE(cfg->mps)) + + max_transfer = (max_pkts * udc_mps_ep_size(cfg)) + (spcavail % spcperpkt); /* If there is enough space for the transfer, there's no need @@ -355,10 +355,10 @@ static int dwc2_tx_fifo_write(const struct device *dev, * Determine the number of packets for the current transfer; * if the pktcnt is too large, truncate the actual transfer length. */ - pktcnt = DIV_ROUND_UP(len, USB_MPS_EP_SIZE(cfg->mps)); + pktcnt = DIV_ROUND_UP(len, udc_mps_ep_size(cfg)); if (pktcnt > max_pktcnt) { pktcnt = ROUND_DOWN(max_pktcnt, (1 + addnl)); - len = pktcnt * USB_MPS_EP_SIZE(cfg->mps); + len = pktcnt * udc_mps_ep_size(cfg); } } else { /* ZLP */ @@ -401,7 +401,7 @@ static int dwc2_tx_fifo_write(const struct device *dev, const uint8_t *src = buf->data; while (pktcnt > 0) { - uint32_t pktlen = MIN(len, USB_MPS_EP_SIZE(cfg->mps)); + uint32_t pktlen = MIN(len, udc_mps_ep_size(cfg)); for (uint32_t i = 0UL; i < pktlen; i += d) { uint32_t val = src[i]; @@ -504,7 +504,7 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, xfersize = dwc2_rx_xfer_size(priv, cfg, buf); - pktcnt = DIV_ROUND_UP(xfersize, USB_MPS_EP_SIZE(cfg->mps)); + pktcnt = DIV_ROUND_UP(xfersize, udc_mps_ep_size(cfg)); doeptsiz = usb_dwc2_set_deptsizn_pktcnt(pktcnt) | usb_dwc2_set_deptsizn_xfersize(xfersize); if (cfg->addr == USB_CONTROL_EP_OUT) { @@ -887,8 +887,7 @@ static inline void dwc2_handle_rxflvl(const struct device *dev) break; } - if (((evt.bcnt % USB_MPS_EP_SIZE(ep_cfg->mps)) == 0) && - net_buf_tailroom(buf)) { + if ((evt.bcnt % udc_mps_ep_size(ep_cfg)) == 0 && net_buf_tailroom(buf)) { uint32_t doeptsiz; /* Prepare next read only when transfer finished */ @@ -1010,8 +1009,7 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, net_buf_add(buf, evt.bcnt); - if (((evt.bcnt % USB_MPS_EP_SIZE(ep_cfg->mps)) == 0) && - net_buf_tailroom(buf)) { + if ((evt.bcnt % udc_mps_ep_size(ep_cfg)) == 0 && net_buf_tailroom(buf)) { dwc2_prep_rx(dev, buf, ep_cfg, 0); } else { k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); @@ -1200,7 +1198,7 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, /* Keep everything but FIFO number */ tmp = *diepctl & ~USB_DWC2_DEPCTL_TXFNUM_MASK; - reqdep = DIV_ROUND_UP(USB_MPS_EP_SIZE(cfg->mps), 4U); + reqdep = DIV_ROUND_UP(udc_mps_ep_size(cfg), 4U); if (priv->bufferdma) { /* In DMA mode, TxFIFO capable of holding 2 packets is enough */ reqdep *= MIN(2, (1 + addnl)); @@ -1331,7 +1329,7 @@ static int udc_dwc2_ep_activate(const struct device *dev, dxepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl; } - if (priv->bufferdma && (USB_MPS_EP_SIZE(cfg->mps) % 4)) { + if (priv->bufferdma && (udc_mps_ep_size(cfg) % 4)) { /* TODO: In Buffer DMA mode, DMA will insert padding bytes in * between packets if endpoint Max Packet Size is not multiple * of 4 (DWORD) and single transfer spans across multiple @@ -1351,7 +1349,7 @@ static int udc_dwc2_ep_activate(const struct device *dev, dxepctl = sys_read32(dxepctl_reg); /* Set max packet size */ dxepctl &= ~USB_DWC2_DEPCTL_MPS_MASK; - dxepctl |= usb_dwc2_set_depctl_mps(USB_MPS_EP_SIZE(cfg->mps)); + dxepctl |= usb_dwc2_set_depctl_mps(udc_mps_ep_size(cfg)); /* Set endpoint type */ dxepctl &= ~USB_DWC2_DEPCTL_EPTYPE_MASK; @@ -1375,7 +1373,7 @@ static int udc_dwc2_ep_activate(const struct device *dev, return -EINVAL; } - if (USB_EP_DIR_IS_IN(cfg->addr) && USB_MPS_EP_SIZE(cfg->mps) != 0U) { + if (USB_EP_DIR_IS_IN(cfg->addr) && udc_mps_ep_size(cfg) != 0U) { int ret = dwc2_set_dedicated_fifo(dev, cfg, &dxepctl); if (ret) { @@ -1578,7 +1576,7 @@ static int udc_dwc2_ep_deactivate(const struct device *dev, cfg->addr, ep_idx, dxepctl); } - if (USB_EP_DIR_IS_IN(cfg->addr) && USB_MPS_EP_SIZE(cfg->mps) != 0U && + if (USB_EP_DIR_IS_IN(cfg->addr) && udc_mps_ep_size(cfg) != 0U && ep_idx != 0U) { dwc2_unset_dedicated_fifo(dev, cfg, &dxepctl); } diff --git a/drivers/usb/udc/udc_it82xx2.c b/drivers/usb/udc/udc_it82xx2.c index 70209f11e17..d14ce36db34 100644 --- a/drivers/usb/udc/udc_it82xx2.c +++ b/drivers/usb/udc/udc_it82xx2.c @@ -705,7 +705,7 @@ static int it82xx2_xfer_in_data(const struct device *dev, uint8_t ep, struct net it82xx2_usb_fifo_ctrl(dev, ep); } - len = MIN(buf->len, ep_cfg->mps); + len = MIN(buf->len, udc_mps_ep_size(ep_cfg)); for (size_t i = 0; i < len; i++) { ff_regs[fifo_idx].ep_tx_fifo_data = buf->data[i]; @@ -874,7 +874,7 @@ static inline int work_handler_in(const struct device *dev, uint8_t ep) } ep_cfg = udc_get_ep_cfg(dev, ep); - net_buf_pull(buf, MIN(buf->len, ep_cfg->mps)); + net_buf_pull(buf, MIN(buf->len, udc_mps_ep_size(ep_cfg))); it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_TOGGLE, true); @@ -1016,7 +1016,7 @@ static inline int work_handler_out(const struct device *dev, uint8_t ep) } ep_cfg = udc_get_ep_cfg(dev, ep); - if (len > ep_cfg->mps) { + if (len > udc_mps_ep_size(ep_cfg)) { LOG_ERR("Failed to handle this packet due to the packet size"); return -ENOBUFS; } diff --git a/drivers/usb/udc/udc_kinetis.c b/drivers/usb/udc/udc_kinetis.c index 38dd1db2d04..08934115b0c 100644 --- a/drivers/usb/udc/udc_kinetis.c +++ b/drivers/usb/udc/udc_kinetis.c @@ -195,10 +195,10 @@ static int usbfsotg_xfer_continue(const struct device *dev, } if (USB_EP_DIR_IS_OUT(cfg->addr)) { - len = MIN(net_buf_tailroom(buf), cfg->mps); + len = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); data_ptr = net_buf_tail(buf); } else { - len = MIN(buf->len, cfg->mps); + len = MIN(buf->len, udc_mps_ep_size(cfg)); data_ptr = buf->data; } @@ -243,7 +243,7 @@ static inline int usbfsotg_ctrl_feed_start(const struct device *dev, } bd = usbfsotg_get_ebd(dev, cfg, false); - length = MIN(net_buf_tailroom(buf), cfg->mps); + length = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); priv->out_buf[cfg->stat.odd] = buf; priv->busy[cfg->stat.odd] = true; @@ -267,7 +267,7 @@ static inline int usbfsotg_ctrl_feed_start_next(const struct device *dev, } bd = usbfsotg_get_ebd(dev, cfg, true); - length = MIN(net_buf_tailroom(buf), cfg->mps); + length = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); priv->out_buf[!cfg->stat.odd] = buf; priv->busy[!cfg->stat.odd] = true; @@ -592,7 +592,8 @@ static ALWAYS_INLINE void isr_handle_xfer_done(const struct device *dev, } net_buf_add(buf, len); - if (net_buf_tailroom(buf) >= ep_cfg->mps && len == ep_cfg->mps) { + if (net_buf_tailroom(buf) >= udc_mps_ep_size(ep_cfg) && + len == udc_mps_ep_size(ep_cfg)) { if (ep == USB_CONTROL_EP_OUT) { usbfsotg_ctrl_feed_start(dev, buf); } else { diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index abfb35cf142..3cb0b299905 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -571,7 +571,7 @@ static int udc_mcux_ep_enable(const struct device *dev, ep_init.zlt = 0U; ep_init.interval = cfg->interval; ep_init.endpointAddress = cfg->addr; - ep_init.maxPacketSize = cfg->mps; + ep_init.maxPacketSize = udc_mps_ep_size(cfg); switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { case USB_EP_TYPE_CONTROL: diff --git a/drivers/usb/udc/udc_mcux_ip3511.c b/drivers/usb/udc/udc_mcux_ip3511.c index 87978f0ff08..0ff464054f1 100644 --- a/drivers/usb/udc/udc_mcux_ip3511.c +++ b/drivers/usb/udc/udc_mcux_ip3511.c @@ -571,7 +571,7 @@ static int udc_mcux_ep_enable(const struct device *dev, ep_init.zlt = 0U; ep_init.interval = cfg->interval; ep_init.endpointAddress = cfg->addr; - ep_init.maxPacketSize = cfg->mps; + ep_init.maxPacketSize = udc_mps_ep_size(cfg); switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { case USB_EP_TYPE_CONTROL: diff --git a/drivers/usb/udc/udc_nrf.c b/drivers/usb/udc/udc_nrf.c index f02dfcdc308..2f195611537 100644 --- a/drivers/usb/udc/udc_nrf.c +++ b/drivers/usb/udc/udc_nrf.c @@ -552,7 +552,7 @@ static int udc_nrf_ep_enable(const struct device *dev, uint16_t mps; __ASSERT_NO_MSG(cfg); - mps = (cfg->mps == 0) ? cfg->caps.mps : cfg->mps; + mps = (udc_mps_ep_size(cfg) == 0) ? cfg->caps.mps : udc_mps_ep_size(cfg); nrf_usbd_common_ep_max_packet_size_set(cfg->addr, mps); nrf_usbd_common_ep_enable(cfg->addr); if (!NRF_USBD_EPISO_CHECK(cfg->addr)) { diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index e91367224dc..c0d0e8847c9 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -400,7 +400,7 @@ static int udc_stm32_ep_mem_config(const struct device *dev, const struct udc_stm32_config *cfg = dev->config; uint32_t size; - size = MIN(ep->mps, cfg->ep_mps); + size = MIN(udc_mps_ep_size(ep), cfg->ep_mps); if (!enable) { priv->occupied_mem -= size; @@ -464,7 +464,7 @@ static int udc_stm32_ep_mem_config(const struct device *dev, return 0; } - words = MIN(ep->mps, cfg->ep_mps) / 4; + words = MIN(udc_mps_ep_size(ep), cfg->ep_mps) / 4; words = (words <= 64) ? words * 2 : words; if (!enable) { @@ -634,8 +634,8 @@ static int udc_stm32_ep_enable(const struct device *dev, return ret; } - status = HAL_PCD_EP_Open(&priv->pcd, ep_cfg->addr, ep_cfg->mps, - ep_type); + status = HAL_PCD_EP_Open(&priv->pcd, ep_cfg->addr, + udc_mps_ep_size(ep_cfg), ep_type); if (status != HAL_OK) { LOG_ERR("HAL_PCD_EP_Open failed(0x%02x), %d", ep_cfg->addr, (int)status); diff --git a/drivers/usb/udc/udc_virtual.c b/drivers/usb/udc/udc_virtual.c index d1ba034a2df..cc21649ad67 100644 --- a/drivers/usb/udc/udc_virtual.c +++ b/drivers/usb/udc/udc_virtual.c @@ -183,7 +183,7 @@ static int vrt_handle_out(const struct device *dev, LOG_DBG("Handle data OUT, %zu | %zu", pkt->length, net_buf_tailroom(buf)); - if (net_buf_tailroom(buf) == 0 || pkt->length < ep_cfg->mps) { + if (net_buf_tailroom(buf) == 0 || pkt->length < udc_mps_ep_size(ep_cfg)) { buf = udc_buf_get(dev, ep); if (ep == USB_CONTROL_EP_OUT) { @@ -246,13 +246,13 @@ static int vrt_handle_in(const struct device *dev, } LOG_DBG("Handle data IN, %zu | %u | %u", - pkt->length, buf->len, ep_cfg->mps); + pkt->length, buf->len, udc_mps_ep_size(ep_cfg)); min_len = MIN(pkt->length, buf->len); memcpy(pkt->data, buf->data, min_len); net_buf_pull(buf, min_len); pkt->length = min_len; - if (buf->len == 0 || pkt->length < ep_cfg->mps) { + if (buf->len == 0 || pkt->length < udc_mps_ep_size(ep_cfg)) { if (udc_ep_buf_has_zlp(buf)) { udc_ep_buf_clear_zlp(buf); goto continue_in; diff --git a/include/zephyr/drivers/usb/udc.h b/include/zephyr/drivers/usb/udc.h index a757a74d964..b0a136fac62 100644 --- a/include/zephyr/drivers/usb/udc.h +++ b/include/zephyr/drivers/usb/udc.h @@ -704,6 +704,18 @@ static inline struct udc_buf_info *udc_get_buf_info(const struct net_buf *const return (struct udc_buf_info *)net_buf_user_data(buf); } +/** + * @brief Get endpoint size from UDC endpoint configuration + * + * @param[in] cfg Pointer to UDC endpoint configuration + * + * @return Endpoint size + */ +static inline uint16_t udc_mps_ep_size(const struct udc_ep_config *const cfg) +{ + return USB_MPS_EP_SIZE(cfg->mps); +} + /** * @} */ From c0048ba356c1cb34734693862b8bece726fc46e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Wed, 31 Jul 2024 10:38:11 +0200 Subject: [PATCH 32/39] [nrf fromtree] drivers: udc_dwc2: Add isochronous register bit defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add all register bit defines necessary for isochronous transfers. Clean up the endpoint transfer size register defines clearly separating IN and OUT registers because they do use different bit fields. Add alternate bit names for bits that do have different meaning based on configured endpoint transfer type. Signed-off-by: Tomasz Moń (cherry picked from commit 9f98ee854c49473ff137856f7d9866941571369c) Signed-off-by: Tomasz Moń --- drivers/usb/common/usb_dwc2_hw.h | 88 ++++++++++++++++++++++---------- drivers/usb/udc/udc_dwc2.c | 14 ++--- 2 files changed, 69 insertions(+), 33 deletions(-) diff --git a/drivers/usb/common/usb_dwc2_hw.h b/drivers/usb/common/usb_dwc2_hw.h index 5203cb0e21f..9cd172bdd11 100644 --- a/drivers/usb/common/usb_dwc2_hw.h +++ b/drivers/usb/common/usb_dwc2_hw.h @@ -242,6 +242,7 @@ USB_DWC2_SET_FIELD_DEFINE(grstctl_txfnum, GRSTCTL_TXFNUM) #define USB_DWC2_GINTSTS_FETSUSP BIT(USB_DWC2_GINTSTS_FETSUSP_POS) #define USB_DWC2_GINTSTS_INCOMPIP_POS 21UL #define USB_DWC2_GINTSTS_INCOMPIP BIT(USB_DWC2_GINTSTS_INCOMPIP_POS) +#define USB_DWC2_GINTSTS_INCOMPISOOUT USB_DWC2_GINTSTS_INCOMPIP #define USB_DWC2_GINTSTS_INCOMPISOIN_POS 20UL #define USB_DWC2_GINTSTS_INCOMPISOIN BIT(USB_DWC2_GINTSTS_INCOMPISOIN_POS) #define USB_DWC2_GINTSTS_OEPINT_POS 19UL @@ -565,13 +566,23 @@ USB_DWC2_SET_FIELD_DEFINE(dctl_tstctl, DCTL_TSTCTL) /* Device status register */ #define USB_DWC2_DSTS 0x0808UL +#define USB_DWC2_DSTS_DEVLNSTS_POS 22UL +#define USB_DWC2_DSTS_DEVLNSTS_MASK (0x3UL << USB_DWC2_DSTS_DEVLNSTS_POS) +#define USB_DWC2_DSTS_SOFFN_POS 8UL +#define USB_DWC2_DSTS_SOFFN_MASK (0x3FFFUL << USB_DWC2_DSTS_SOFFN_POS) +#define USB_DWC2_DSTS_ERRTICERR_POS 3UL +#define USB_DWC2_DSTS_ERRTICERR BIT(USB_DWC2_DSTS_ERRTICERR_POS) #define USB_DWC2_DSTS_ENUMSPD_POS 1UL #define USB_DWC2_DSTS_ENUMSPD_MASK (0x3UL << USB_DWC2_DSTS_ENUMSPD_POS) #define USB_DWC2_DSTS_ENUMSPD_HS3060 0 #define USB_DWC2_DSTS_ENUMSPD_FS3060 1 #define USB_DWC2_DSTS_ENUMSPD_LS6 2 #define USB_DWC2_DSTS_ENUMSPD_FS48 3 +#define USB_DWC2_DSTS_SUSPSTS_POS 0UL +#define USB_DWC2_DSTS_SUSPSTS BIT(USB_DWC2_DSTS_SUSPSTS_POS) +USB_DWC2_GET_FIELD_DEFINE(dsts_devlnsts, DSTS_DEVLNSTS) +USB_DWC2_GET_FIELD_DEFINE(dsts_soffn, DSTS_SOFFN) USB_DWC2_GET_FIELD_DEFINE(dsts_enumspd, DSTS_ENUMSPD) /* Device all endpoints interrupt registers */ @@ -622,8 +633,10 @@ USB_DWC2_SET_FIELD_DEFINE(dthrctl_txthrlen, DTHRCTL_TXTHRLEN) #define USB_DWC2_DEPCTL_EPDIS BIT(USB_DWC2_DEPCTL_EPDIS_POS) #define USB_DWC2_DEPCTL_SETD1PID_POS 29UL #define USB_DWC2_DEPCTL_SETD1PID BIT(USB_DWC2_DEPCTL_SETD1PID_POS) +#define USB_DWC2_DEPCTL_SETODDFR USB_DWC2_DEPCTL_SETD1PID #define USB_DWC2_DEPCTL_SETD0PID_POS 28UL #define USB_DWC2_DEPCTL_SETD0PID BIT(USB_DWC2_DEPCTL_SETD0PID_POS) +#define USB_DWC2_DEPCTL_SETEVENFR USB_DWC2_DEPCTL_SETD0PID #define USB_DWC2_DEPCTL_SNAK_POS 27UL #define USB_DWC2_DEPCTL_SNAK BIT(USB_DWC2_DEPCTL_SNAK_POS) #define USB_DWC2_DEPCTL_CNAK_POS 26UL @@ -730,44 +743,67 @@ USB_DWC2_SET_FIELD_DEFINE(depctl_mps, DEPCTL_MPS) #define USB_DWC2_DOEPINT_XFERCOMPL_POS 0UL #define USB_DWC2_DOEPINT_XFERCOMPL BIT(USB_DWC2_DOEPINT_XFERCOMPL_POS) -/* - * Device IN/OUT control endpoint transfer size register - */ +/* Device IN control endpoint transfer size register */ #define USB_DWC2_DIEPTSIZ0 0x0910UL +#define USB_DWC2_DIEPTSIZ0_PKTCNT_POS 19UL +#define USB_DWC2_DIEPTSIZ0_PKTCNT_MASK (0x3UL << USB_DWC2_DIEPTSIZ0_PKTCNT_POS) +#define USB_DWC2_DIEPTSIZ0_XFERSIZE_POS 0UL +#define USB_DWC2_DIEPTSIZ0_XFERSIZE_MASK 0x7FUL + +USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_pktcnt, DIEPTSIZ0_PKTCNT) +USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_xfersize, DIEPTSIZ0_XFERSIZE) + +/* Device OUT control endpoint transfer size register */ #define USB_DWC2_DOEPTSIZ0 0x0B10UL #define USB_DWC2_DOEPTSIZ0_SUPCNT_POS 29UL #define USB_DWC2_DOEPTSIZ0_SUPCNT_MASK (0x3UL << USB_DWC2_DOEPTSIZ0_SUPCNT_POS) #define USB_DWC2_DOEPTSIZ0_PKTCNT_POS 19UL #define USB_DWC2_DOEPTSIZ0_PKTCNT_MASK (0x1UL << USB_DWC2_DOEPTSIZ0_PKTCNT_POS) -#define USB_DWC2_DIEPTSIZ0_PKTCNT_POS 19UL -#define USB_DWC2_DIEPTSIZ0_PKTCNT_MASK (0x3UL << USB_DWC2_DIEPTSIZ0_PKTCNT_POS) -#define USB_DWC2_DEPTSIZ0_XFERSIZE_POS 0UL -#define USB_DWC2_DEPTSIZ0_XFERSIZE_MASK 0x7FUL +#define USB_DWC2_DOEPTSIZ0_XFERSIZE_POS 0UL +#define USB_DWC2_DOEPTSIZ0_XFERSIZE_MASK 0x7FUL USB_DWC2_GET_FIELD_DEFINE(doeptsiz0_supcnt, DOEPTSIZ0_SUPCNT) USB_DWC2_GET_FIELD_DEFINE(doeptsiz0_pktcnt, DOEPTSIZ0_PKTCNT) -USB_DWC2_GET_FIELD_DEFINE(doeptsiz0_xfersize, DEPTSIZ0_XFERSIZE) -USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_pktcnt, DIEPTSIZ0_PKTCNT) -USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_xfersize, DEPTSIZ0_XFERSIZE) +USB_DWC2_GET_FIELD_DEFINE(doeptsiz0_xfersize, DOEPTSIZ0_XFERSIZE) /* - * Device IN/OUT endpoint transfer size register - * IN at offsets 0x0910 + (0x20 * n), n = 1 .. x, - * OUT at offsets 0x0B10 + (0x20 * n), n = 1 .. x + * Device IN endpoint transfer size register + * at offsets 0x0910 + (0x20 * n), n = 1 .. x + */ +#define USB_DWC2_DIEPTSIZN_MC_POS 29UL +#define USB_DWC2_DIEPTSIZN_MC_MASK (0x3UL << USB_DWC2_DIEPTSIZN_MC_POS) +#define USB_DWC2_DIEPTSIZN_PKTCNT_POS 19UL +#define USB_DWC2_DIEPTSIZN_PKTCNT_MASK (0x3FFUL << USB_DWC2_DIEPTSIZN_PKTCNT_POS) +#define USB_DWC2_DIEPTSIZN_XFERSIZE_POS 0UL +#define USB_DWC2_DIEPTSIZN_XFERSIZE_MASK 0x7FFFFUL + +USB_DWC2_GET_FIELD_DEFINE(dieptsizn_mc, DIEPTSIZN_MC) +USB_DWC2_GET_FIELD_DEFINE(dieptsizn_pktcnt, DIEPTSIZN_PKTCNT) +USB_DWC2_GET_FIELD_DEFINE(dieptsizn_xfersize, DIEPTSIZN_XFERSIZE) +USB_DWC2_SET_FIELD_DEFINE(dieptsizn_mc, DIEPTSIZN_MC) +USB_DWC2_SET_FIELD_DEFINE(dieptsizn_pktcnt, DIEPTSIZN_PKTCNT) +USB_DWC2_SET_FIELD_DEFINE(dieptsizn_xfersize, DIEPTSIZN_XFERSIZE) + +/* + * Device OUT endpoint transfer size register + * at offsets 0x0B10 + (0x20 * n), n = 1 .. x */ -#define USB_DWC2_DEPTSIZN_MC_POS 29UL -#define USB_DWC2_DEPTSIZN_MC_MASK (0x3UL << USB_DWC2_DEPTSIZN_MC_POS) -#define USB_DWC2_DEPTSIZN_PKTCNT_POS 19UL -#define USB_DWC2_DEPTSIZN_PKTCNT_MASK (0x3FFUL << USB_DWC2_DEPTSIZN_PKTCNT_POS) -#define USB_DWC2_DEPTSIZN_XFERSIZE_POS 0UL -#define USB_DWC2_DEPTSIZN_XFERSIZE_MASK 0x7FFFFUL - -USB_DWC2_GET_FIELD_DEFINE(deptsizn_mc, DEPTSIZN_MC) -USB_DWC2_GET_FIELD_DEFINE(deptsizn_pktcnt, DEPTSIZN_PKTCNT) -USB_DWC2_GET_FIELD_DEFINE(deptsizn_xfersize, DEPTSIZN_XFERSIZE) -USB_DWC2_SET_FIELD_DEFINE(deptsizn_mc, DEPTSIZN_MC) -USB_DWC2_SET_FIELD_DEFINE(deptsizn_pktcnt, DEPTSIZN_PKTCNT) -USB_DWC2_SET_FIELD_DEFINE(deptsizn_xfersize, DEPTSIZN_XFERSIZE) +#define USB_DWC2_DOEPTSIZN_RXDPID_POS 29UL +#define USB_DWC2_DOEPTSIZN_RXDPID_MASK (0x3UL << USB_DWC2_DOEPTSIZN_RXDPID_POS) +#define USB_DWC2_DOEPTSIZN_RXDPID_MDATA 3 +#define USB_DWC2_DOEPTSIZN_RXDPID_DATA1 2 +#define USB_DWC2_DOEPTSIZN_RXDPID_DATA2 1 +#define USB_DWC2_DOEPTSIZN_RXDPID_DATA0 0 +#define USB_DWC2_DOEPTSIZN_PKTCNT_POS 19UL +#define USB_DWC2_DOEPTSIZN_PKTCNT_MASK (0x3FFUL << USB_DWC2_DOEPTSIZN_PKTCNT_POS) +#define USB_DWC2_DOEPTSIZN_XFERSIZE_POS 0UL +#define USB_DWC2_DOEPTSIZN_XFERSIZE_MASK 0x7FFFFUL + +USB_DWC2_GET_FIELD_DEFINE(doeptsizn_rxdpid, DOEPTSIZN_RXDPID) +USB_DWC2_GET_FIELD_DEFINE(doeptsizn_pktcnt, DOEPTSIZN_PKTCNT) +USB_DWC2_GET_FIELD_DEFINE(doeptsizn_xfersize, DOEPTSIZN_XFERSIZE) +USB_DWC2_SET_FIELD_DEFINE(doeptsizn_pktcnt, DOEPTSIZN_PKTCNT) +USB_DWC2_SET_FIELD_DEFINE(doeptsizn_xfersize, DOEPTSIZN_XFERSIZE) /* * Device IN/OUT endpoint transfer size register diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 61274b2b934..207c714f950 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -373,9 +373,9 @@ static int dwc2_tx_fifo_write(const struct device *dev, key = irq_lock(); /* Set number of packets and transfer size */ - sys_write32((is_periodic ? usb_dwc2_set_deptsizn_mc(1 + addnl) : 0) | - usb_dwc2_set_deptsizn_pktcnt(pktcnt) | - usb_dwc2_set_deptsizn_xfersize(len), dieptsiz_reg); + sys_write32((is_periodic ? usb_dwc2_set_dieptsizn_mc(1 + addnl) : 0) | + usb_dwc2_set_dieptsizn_pktcnt(pktcnt) | + usb_dwc2_set_dieptsizn_xfersize(len), dieptsiz_reg); if (priv->bufferdma) { if (!dwc2_dma_buffer_ok_to_use(dev, buf->data, len, cfg->mps)) { @@ -505,8 +505,8 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, xfersize = dwc2_rx_xfer_size(priv, cfg, buf); pktcnt = DIV_ROUND_UP(xfersize, udc_mps_ep_size(cfg)); - doeptsiz = usb_dwc2_set_deptsizn_pktcnt(pktcnt) | - usb_dwc2_set_deptsizn_xfersize(xfersize); + doeptsiz = usb_dwc2_set_doeptsizn_pktcnt(pktcnt) | + usb_dwc2_set_doeptsizn_xfersize(xfersize); if (cfg->addr == USB_CONTROL_EP_OUT) { /* Use 1 to allow 8 byte long buffers for SETUP data */ doeptsiz |= (1 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS); @@ -892,7 +892,7 @@ static inline void dwc2_handle_rxflvl(const struct device *dev) /* Prepare next read only when transfer finished */ doeptsiz = sys_read32((mem_addr_t)&base->out_ep[evt.ep].doeptsiz); - if (usb_dwc2_get_deptsizn_xfersize(doeptsiz) == 0) { + if (usb_dwc2_get_doeptsizn_xfersize(doeptsiz) == 0) { dwc2_prep_rx(dev, buf, ep_cfg, 0); } } else { @@ -1001,7 +1001,7 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, * every byte stored. */ evt.bcnt = dwc2_rx_xfer_size(priv, ep_cfg, buf) - - usb_dwc2_get_deptsizn_xfersize(doeptsiz); + usb_dwc2_get_doeptsizn_xfersize(doeptsiz); if (priv->bufferdma) { sys_cache_data_invd_range(buf->data, evt.bcnt); From 67b51837bf93cd21ed984aa834409efe90241a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 19 Aug 2024 08:31:29 +0200 Subject: [PATCH 33/39] [nrf fromtree] drivers: udc_dwc2: Pass fifo number to tx fifo flush MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function dwc2_set_dedicated_fifo() calls dwc2_flush_tx_fifo() before the diepctl register is written. The register will be only written after the dwc2_set_dedicated_fifo() finishes. Solve the problem by passing fifo number directly instead of endpoint address to dwc2_flush_tx_fifo(). Signed-off-by: Tomasz Moń (cherry picked from commit 31ee5d9787fd2e874b1fce8f2f8c8640578db64b) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 207c714f950..58b4d067d27 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -184,16 +184,12 @@ static void dwc2_flush_rx_fifo(const struct device *dev) } } -static void dwc2_flush_tx_fifo(const struct device *dev, const uint8_t idx) +static void dwc2_flush_tx_fifo(const struct device *dev, const uint8_t fnum) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); mem_addr_t grstctl_reg = (mem_addr_t)&base->grstctl; - /* TODO: use dwc2_get_dxepctl_reg() */ - mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[idx].diepctl; uint32_t grstctl; - uint32_t fnum; - fnum = usb_dwc2_get_depctl_txfnum(sys_read32(diepctl_reg)); grstctl = usb_dwc2_set_grstctl_txfnum(fnum) | USB_DWC2_GRSTCTL_TXFFLSH; sys_write32(grstctl, grstctl_reg); @@ -1544,7 +1540,7 @@ static void udc_dwc2_ep_disable(const struct device *dev, * were transferred to the host. */ - dwc2_flush_tx_fifo(dev, ep_idx); + dwc2_flush_tx_fifo(dev, usb_dwc2_get_depctl_txfnum(dxepctl)); } udc_ep_set_busy(dev, cfg->addr, false); From b6be85045e4db2f9113669223269f6f16fbd4366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 26 Jul 2024 12:42:09 +0200 Subject: [PATCH 34/39] [nrf fromtree] drivers: udc_dwc2: Unify OUT transfers in Completer and DMA mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RxFLvl interrupt should only handle the actual data movement from RxFIFO to the buffer. OUT transfers are completed in XferCompl handler both in DMA and Completer mode. This reduces code size by avoiding having separate code paths based on operating mode. Signed-off-by: Tomasz Moń (cherry picked from commit de28a828c5cad4b246a5451d89fdbe11db61b7c5) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 82 ++++++++++++-------------------------- 1 file changed, 25 insertions(+), 57 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 58b4d067d27..0ee3eef1a3f 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -38,7 +38,6 @@ enum dwc2_drv_event_type { struct dwc2_drv_event { const struct device *dev; enum dwc2_drv_event_type type; - uint32_t bcnt; uint8_t ep; }; @@ -74,6 +73,7 @@ struct udc_dwc2_data { uint32_t max_xfersize; uint32_t max_pktcnt; uint32_t tx_len[16]; + uint32_t rx_siz[16]; uint16_t dfifodepth; uint16_t rxfifo_depth; uint16_t max_txfifo_depth[16]; @@ -464,22 +464,11 @@ static uint32_t dwc2_rx_xfer_size(struct udc_dwc2_data *const priv, { uint32_t size; - if (priv->bufferdma) { - size = net_buf_tailroom(buf); + size = net_buf_tailroom(buf); - /* Do as many packets in a single DMA as possible */ - if (size > priv->max_xfersize) { - size = ROUND_DOWN(priv->max_xfersize, - USB_MPS_TO_TPL(cfg->mps)); - } - } else { - /* Completer mode can always program Max Packet Size, RxFLvl - * interrupt will drop excessive data if necessary (i.e. buffer - * is too short). The value returned must be Max Packet Size - * multiple in order for dwc2_handle_rxflvl() to correctly - * detect when RX has to be prepared during large transfers. - */ - size = USB_MPS_TO_TPL(cfg->mps); + /* Do as many packets in a single transfer as possible */ + if (size > priv->max_xfersize) { + size = ROUND_DOWN(priv->max_xfersize, USB_MPS_TO_TPL(cfg->mps)); } return size; @@ -508,6 +497,7 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, doeptsiz |= (1 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS); } + priv->rx_siz[ep_idx] = doeptsiz; sys_write32(doeptsiz, doeptsiz_reg); if (priv->bufferdma) { @@ -785,10 +775,9 @@ static void dwc2_on_bus_reset(const struct device *dev) } } - doepmsk = USB_DWC2_DOEPINT_SETUP; + doepmsk = USB_DWC2_DOEPINT_SETUP | USB_DWC2_DOEPINT_XFERCOMPL; if (priv->bufferdma) { - doepmsk |= USB_DWC2_DOEPINT_XFERCOMPL | - USB_DWC2_DOEPINT_STSPHSERCVD; + doepmsk |= USB_DWC2_DOEPINT_STSPHSERCVD; } sys_write32(doepmsk, (mem_addr_t)&base->doepmsk); @@ -852,49 +841,30 @@ static inline void dwc2_handle_rxflvl(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); struct udc_ep_config *ep_cfg; - struct dwc2_drv_event evt; struct net_buf *buf; uint32_t grxstsp; uint32_t pktsts; + uint32_t bcnt; + uint8_t ep; grxstsp = sys_read32((mem_addr_t)&base->grxstsp); - evt.ep = usb_dwc2_get_grxstsp_epnum(grxstsp); - evt.bcnt = usb_dwc2_get_grxstsp_bcnt(grxstsp); + ep = usb_dwc2_get_grxstsp_epnum(grxstsp); + bcnt = usb_dwc2_get_grxstsp_bcnt(grxstsp); pktsts = usb_dwc2_get_grxstsp_pktsts(grxstsp); - LOG_DBG("ep 0x%02x: pktsts %u, bcnt %u", evt.ep, pktsts, evt.bcnt); + LOG_DBG("ep 0x%02x: pktsts %u, bcnt %u", ep, pktsts, bcnt); switch (pktsts) { case USB_DWC2_GRXSTSR_PKTSTS_SETUP: - dwc2_read_fifo_setup(dev, evt.ep, evt.bcnt); + dwc2_read_fifo_setup(dev, ep, bcnt); break; case USB_DWC2_GRXSTSR_PKTSTS_OUT_DATA: - evt.type = DWC2_DRV_EVT_DOUT; - ep_cfg = udc_get_ep_cfg(dev, evt.ep); + ep_cfg = udc_get_ep_cfg(dev, ep); buf = udc_buf_peek(dev, ep_cfg->addr); /* RxFIFO data must be retrieved even when buf is NULL */ - dwc2_read_fifo(dev, evt.ep, buf, evt.bcnt); - - if (buf == NULL) { - LOG_ERR("No buffer for ep 0x%02x", ep_cfg->addr); - udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); - break; - } - - if ((evt.bcnt % udc_mps_ep_size(ep_cfg)) == 0 && net_buf_tailroom(buf)) { - uint32_t doeptsiz; - - /* Prepare next read only when transfer finished */ - doeptsiz = sys_read32((mem_addr_t)&base->out_ep[evt.ep].doeptsiz); - if (usb_dwc2_get_doeptsizn_xfersize(doeptsiz) == 0) { - dwc2_prep_rx(dev, buf, ep_cfg, 0); - } - } else { - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); - } - + dwc2_read_fifo(dev, ep, buf, bcnt); break; case USB_DWC2_GRXSTSR_PKTSTS_OUT_DATA_DONE: LOG_DBG("RX pktsts DONE"); @@ -977,6 +947,7 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, struct udc_dwc2_data *const priv = udc_get_private(dev); struct usb_dwc2_reg *const base = dwc2_get_base(dev); struct dwc2_drv_event evt; + uint32_t bcnt; struct net_buf *buf; uint32_t doeptsiz; @@ -991,21 +962,19 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, evt.type = DWC2_DRV_EVT_DOUT; evt.ep = ep_cfg->addr; - /* Assume udc buffer and endpoint config is the same as it was when - * transfer was scheduled in dwc2_prep_rx(). The original transfer size - * value is necessary here because controller decreases the value for - * every byte stored. + + /* The original transfer size value is necessary here because controller + * decreases the value for every byte stored. */ - evt.bcnt = dwc2_rx_xfer_size(priv, ep_cfg, buf) - - usb_dwc2_get_doeptsizn_xfersize(doeptsiz); + bcnt = usb_dwc2_get_doeptsizn_xfersize(priv->rx_siz[ep_idx]) - + usb_dwc2_get_doeptsizn_xfersize(doeptsiz); if (priv->bufferdma) { - sys_cache_data_invd_range(buf->data, evt.bcnt); + sys_cache_data_invd_range(buf->data, bcnt); + net_buf_add(buf, bcnt); } - net_buf_add(buf, evt.bcnt); - - if ((evt.bcnt % udc_mps_ep_size(ep_cfg)) == 0 && net_buf_tailroom(buf)) { + if ((bcnt % udc_mps_ep_size(ep_cfg)) == 0 && net_buf_tailroom(buf)) { dwc2_prep_rx(dev, buf, ep_cfg, 0); } else { k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); @@ -1063,7 +1032,6 @@ static inline void dwc2_handle_oepint(const struct device *dev) struct dwc2_drv_event evt = { .type = DWC2_DRV_EVT_SETUP, .ep = USB_CONTROL_EP_OUT, - .bcnt = 8, }; k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); From 272bdf25b711d229bbb1e8fe0e103132d0f8ff38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 26 Jul 2024 11:16:29 +0200 Subject: [PATCH 35/39] [nrf fromtree] drivers: udc_dwc2: Support isochronous endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store SOF frame number and use the information to set the Even/Odd microframe bit when enabling isochronous endpoints. Reject received isochronous data if number of packets received does not align with received PID. Signed-off-by: Tomasz Moń (cherry picked from commit c3daf6f2203a971fbfa01beca2bc46eaf87996f8) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 260 ++++++++++++++++++++++++++++++++----- 1 file changed, 225 insertions(+), 35 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 0ee3eef1a3f..9dcae103e1e 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -77,6 +77,7 @@ struct udc_dwc2_data { uint16_t dfifodepth; uint16_t rxfifo_depth; uint16_t max_txfifo_depth[16]; + uint16_t sof_num; unsigned int dynfifosizing : 1; unsigned int bufferdma : 1; /* Number of endpoints including control endpoint */ @@ -88,6 +89,9 @@ struct udc_dwc2_data { uint8_t setup[8]; }; +static void udc_dwc2_ep_disable(const struct device *dev, + struct udc_ep_config *const cfg, bool stall); + #if defined(CONFIG_PINCTRL) #include @@ -266,6 +270,11 @@ static bool dwc2_ep_is_periodic(struct udc_ep_config *const cfg) } } +static bool dwc2_ep_is_iso(struct udc_ep_config *const cfg) +{ + return (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) == USB_EP_TYPE_ISO; +} + static bool dwc2_dma_buffer_ok_to_use(const struct device *dev, void *buf, uint32_t xfersize, uint16_t mps) { @@ -298,17 +307,28 @@ static int dwc2_tx_fifo_write(const struct device *dev, mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl; mem_addr_t diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint; + uint32_t diepctl; uint32_t max_xfersize, max_pktcnt, pktcnt; const uint32_t addnl = USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps); const size_t d = sizeof(uint32_t); unsigned int key; uint32_t len; const bool is_periodic = dwc2_ep_is_periodic(cfg); + const bool is_iso = dwc2_ep_is_iso(cfg); - if (priv->bufferdma) { - /* DMA automatically handles packet split */ - len = buf->len; + if (is_iso) { + /* Isochronous transfers can only be programmed one + * (micro-)frame at a time. + */ + len = MIN(buf->len, USB_MPS_TO_TPL(cfg->mps)); } else { + /* DMA automatically handles packet split. In completer mode, + * the value is sanitized below. + */ + len = buf->len; + } + + if (!priv->bufferdma) { uint32_t spcavail = dwc2_ftx_avail(dev, ep_idx); uint32_t spcperpkt = ROUND_UP(udc_mps_ep_size(cfg), 4); uint32_t max_pkts, max_transfer; @@ -326,13 +346,13 @@ static int dwc2_tx_fifo_write(const struct device *dev, * packet (for periodic transfers at least the number of packets * per microframe). */ - if ((buf->len > max_transfer) && ((1 + addnl) > max_pkts)) { + if ((len > max_transfer) && ((1 + addnl) > max_pkts)) { LOG_ERR("ep 0x%02x FIFO space is too low, %u (%u)", - cfg->addr, spcavail, buf->len); + cfg->addr, spcavail, len); return -EAGAIN; } - len = MIN(buf->len, max_transfer); + len = MIN(len, max_transfer); } if (len != 0U) { @@ -388,8 +408,19 @@ static int dwc2_tx_fifo_write(const struct device *dev, sys_cache_data_flush_range(buf->data, len); } + diepctl = sys_read32(diepctl_reg); + if (is_iso) { + if (priv->sof_num & 1) { + diepctl |= USB_DWC2_DEPCTL_SETODDFR; + } else { + diepctl |= USB_DWC2_DEPCTL_SETEVENFR; + } + } + /* Clear NAK and set endpoint enable */ - sys_set_bits(diepctl_reg, USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK); + diepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK; + sys_write32(diepctl, diepctl_reg); + /* Clear IN Endpoint NAK Effective interrupt in case it was set */ sys_write32(USB_DWC2_DIEPINT_INEPNAKEFF, diepint_reg); @@ -458,25 +489,9 @@ static inline int dwc2_read_fifo(const struct device *dev, const uint8_t ep, return 0; } -static uint32_t dwc2_rx_xfer_size(struct udc_dwc2_data *const priv, - struct udc_ep_config *const cfg, - struct net_buf *buf) -{ - uint32_t size; - - size = net_buf_tailroom(buf); - - /* Do as many packets in a single transfer as possible */ - if (size > priv->max_xfersize) { - size = ROUND_DOWN(priv->max_xfersize, USB_MPS_TO_TPL(cfg->mps)); - } - - return size; -} - /* Can be called from ISR and we call it only when there is a buffer in the queue */ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, - struct udc_ep_config *const cfg, const bool ncnak) + struct udc_ep_config *const cfg) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); struct udc_dwc2_data *const priv = udc_get_private(dev); @@ -485,9 +500,38 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx); uint32_t pktcnt; uint32_t doeptsiz; + uint32_t doepctl; uint32_t xfersize; - xfersize = dwc2_rx_xfer_size(priv, cfg, buf); + /* Clear NAK and set endpoint enable */ + doepctl = sys_read32(doepctl_reg); + doepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK; + + if (dwc2_ep_is_iso(cfg)) { + xfersize = USB_MPS_TO_TPL(cfg->mps); + pktcnt = 1 + USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps); + + if (xfersize > net_buf_tailroom(buf)) { + LOG_ERR("ISO RX buffer too small"); + return; + } + + /* Set the Even/Odd (micro-)frame appropriately */ + if (priv->sof_num & 1) { + doepctl |= USB_DWC2_DEPCTL_SETEVENFR; + } else { + doepctl |= USB_DWC2_DEPCTL_SETODDFR; + } + } else { + xfersize = net_buf_tailroom(buf); + + /* Do as many packets in a single transfer as possible */ + if (xfersize > priv->max_xfersize) { + xfersize = ROUND_DOWN(priv->max_xfersize, USB_MPS_TO_TPL(cfg->mps)); + } + + pktcnt = DIV_ROUND_UP(xfersize, USB_MPS_EP_SIZE(cfg->mps)); + } pktcnt = DIV_ROUND_UP(xfersize, udc_mps_ep_size(cfg)); doeptsiz = usb_dwc2_set_doeptsizn_pktcnt(pktcnt) | @@ -514,11 +558,7 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, sys_cache_data_invd_range(buf->data, xfersize); } - if (ncnak) { - sys_set_bits(doepctl_reg, USB_DWC2_DEPCTL_EPENA); - } else { - sys_set_bits(doepctl_reg, USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK); - } + sys_write32(doepctl, doepctl_reg); LOG_INF("Prepare RX 0x%02x doeptsiz 0x%x", cfg->addr, doeptsiz); } @@ -534,7 +574,7 @@ static void dwc2_handle_xfer_next(const struct device *dev, } if (USB_EP_DIR_IS_OUT(cfg->addr)) { - dwc2_prep_rx(dev, buf, cfg, 0); + dwc2_prep_rx(dev, buf, cfg); } else { if (dwc2_tx_fifo_write(dev, cfg, buf)) { LOG_ERR("Failed to start write to TX FIFO, ep 0x%02x", @@ -556,7 +596,7 @@ static int dwc2_ctrl_feed_dout(const struct device *dev, const size_t length) } udc_buf_put(ep_cfg, buf); - dwc2_prep_rx(dev, buf, ep_cfg, 0); + dwc2_prep_rx(dev, buf, ep_cfg); LOG_DBG("feed buf %p", buf); return 0; @@ -950,6 +990,7 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, uint32_t bcnt; struct net_buf *buf; uint32_t doeptsiz; + const bool is_iso = dwc2_ep_is_iso(ep_cfg); doeptsiz = sys_read32((mem_addr_t)&base->out_ep[ep_idx].doeptsiz); @@ -969,13 +1010,46 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, bcnt = usb_dwc2_get_doeptsizn_xfersize(priv->rx_siz[ep_idx]) - usb_dwc2_get_doeptsizn_xfersize(doeptsiz); - if (priv->bufferdma) { + if (is_iso) { + uint32_t pkts; + bool valid; + + pkts = usb_dwc2_get_doeptsizn_pktcnt(priv->rx_siz[ep_idx]) - + usb_dwc2_get_doeptsizn_pktcnt(doeptsiz); + switch (usb_dwc2_get_doeptsizn_rxdpid(doeptsiz)) { + case USB_DWC2_DOEPTSIZN_RXDPID_DATA0: + valid = (pkts == 1); + break; + case USB_DWC2_DOEPTSIZN_RXDPID_DATA1: + valid = (pkts == 2); + break; + case USB_DWC2_DOEPTSIZN_RXDPID_DATA2: + valid = (pkts == 3); + break; + case USB_DWC2_DOEPTSIZN_RXDPID_MDATA: + default: + valid = false; + break; + } + + if (!valid) { + if (!priv->bufferdma) { + /* RxFlvl added data to net buf, rollback */ + net_buf_remove_mem(buf, bcnt); + } + /* Data is not valid, discard it */ + bcnt = 0; + } + } + + if (priv->bufferdma && bcnt) { sys_cache_data_invd_range(buf->data, bcnt); net_buf_add(buf, bcnt); } - if ((bcnt % udc_mps_ep_size(ep_cfg)) == 0 && net_buf_tailroom(buf)) { - dwc2_prep_rx(dev, buf, ep_cfg, 0); + if (!is_iso && (bcnt % udc_mps_ep_size(ep_cfg)) == 0 && + net_buf_tailroom(buf)) { + dwc2_prep_rx(dev, buf, ep_cfg); } else { k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); } @@ -1056,10 +1130,112 @@ static inline void dwc2_handle_oepint(const struct device *dev) sys_write32(USB_DWC2_GINTSTS_OEPINT, (mem_addr_t)&base->gintsts); } +/* In DWC2 otg context incomplete isochronous IN transfer means that the host + * did not issue IN token to at least one isochronous endpoint and software has + * find on which endpoints the data is no longer valid and discard it. + */ +static void dwc2_handle_incompisoin(const struct device *dev) +{ + const struct udc_dwc2_config *const config = dev->config; + struct usb_dwc2_reg *const base = config->base; + struct udc_dwc2_data *const priv = udc_get_private(dev); + mem_addr_t gintsts_reg = (mem_addr_t)&base->gintsts; + const uint32_t mask = + USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPTYPE_MASK | + USB_DWC2_DEPCTL_USBACTEP; + const uint32_t val = + USB_DWC2_DEPCTL_EPENA | + usb_dwc2_set_depctl_eptype(USB_DWC2_DEPCTL_EPTYPE_ISO) | + USB_DWC2_DEPCTL_USBACTEP; + + for (uint8_t i = 1U; i < priv->numdeveps; i++) { + uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i); + + if (epdir == USB_DWC2_GHWCFG1_EPDIR_IN || + epdir == USB_DWC2_GHWCFG1_EPDIR_BDIR) { + mem_addr_t diepctl_reg = dwc2_get_dxepctl_reg(dev, i | USB_EP_DIR_IN); + uint32_t diepctl; + + diepctl = sys_read32(diepctl_reg); + + /* Check if endpoint didn't receive ISO OUT data */ + if ((diepctl & mask) == val) { + struct udc_ep_config *cfg; + struct net_buf *buf; + + cfg = udc_get_ep_cfg(dev, i | USB_EP_DIR_IN); + __ASSERT_NO_MSG(cfg && cfg->stat.enabled && + dwc2_ep_is_iso(cfg)); + + udc_dwc2_ep_disable(dev, cfg, false); + + buf = udc_buf_get(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, 0); + } + } + } + } + + sys_write32(USB_DWC2_GINTSTS_INCOMPISOIN, gintsts_reg); +} + +/* In DWC2 otg context incomplete isochronous OUT transfer means that the host + * did not issue OUT token to at least one isochronous endpoint and software has + * to find on which endpoint it didn't receive any data and let the stack know. + */ +static void dwc2_handle_incompisoout(const struct device *dev) +{ + const struct udc_dwc2_config *const config = dev->config; + struct usb_dwc2_reg *const base = config->base; + struct udc_dwc2_data *const priv = udc_get_private(dev); + mem_addr_t gintsts_reg = (mem_addr_t)&base->gintsts; + const uint32_t mask = + USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPTYPE_MASK | + USB_DWC2_DEPCTL_DPID | USB_DWC2_DEPCTL_USBACTEP; + const uint32_t val = + USB_DWC2_DEPCTL_EPENA | + usb_dwc2_set_depctl_eptype(USB_DWC2_DEPCTL_EPTYPE_ISO) | + ((priv->sof_num & 1) ? USB_DWC2_DEPCTL_DPID : 0) | + USB_DWC2_DEPCTL_USBACTEP; + + for (uint8_t i = 1U; i < priv->numdeveps; i++) { + uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i); + + if (epdir == USB_DWC2_GHWCFG1_EPDIR_OUT || + epdir == USB_DWC2_GHWCFG1_EPDIR_BDIR) { + mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, i); + uint32_t doepctl; + + doepctl = sys_read32(doepctl_reg); + + /* Check if endpoint didn't receive ISO OUT data */ + if ((doepctl & mask) == val) { + struct udc_ep_config *cfg; + struct net_buf *buf; + + cfg = udc_get_ep_cfg(dev, i); + __ASSERT_NO_MSG(cfg && cfg->stat.enabled && + dwc2_ep_is_iso(cfg)); + + udc_dwc2_ep_disable(dev, cfg, false); + + buf = udc_buf_get(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, 0); + } + } + } + } + + sys_write32(USB_DWC2_GINTSTS_INCOMPISOOUT, gintsts_reg); +} + static void udc_dwc2_isr_handler(const struct device *dev) { const struct udc_dwc2_config *const config = dev->config; struct usb_dwc2_reg *const base = config->base; + struct udc_dwc2_data *const priv = udc_get_private(dev); mem_addr_t gintsts_reg = (mem_addr_t)&base->gintsts; uint32_t int_status; uint32_t gintmsk; @@ -1072,8 +1248,13 @@ static void udc_dwc2_isr_handler(const struct device *dev) LOG_DBG("GINTSTS 0x%x", int_status); if (int_status & USB_DWC2_GINTSTS_SOF) { + uint32_t dsts; + /* Clear USB SOF interrupt. */ sys_write32(USB_DWC2_GINTSTS_SOF, gintsts_reg); + + dsts = sys_read32((mem_addr_t)&base->dsts); + priv->sof_num = usb_dwc2_get_dsts_soffn(dsts); udc_submit_event(dev, UDC_EVT_SOF, 0); } @@ -1119,6 +1300,14 @@ static void udc_dwc2_isr_handler(const struct device *dev) /* Handle OUT Endpoints interrupt */ dwc2_handle_oepint(dev); } + + if (int_status & USB_DWC2_GINTSTS_INCOMPISOIN) { + dwc2_handle_incompisoin(dev); + } + + if (int_status & USB_DWC2_GINTSTS_INCOMPISOOUT) { + dwc2_handle_incompisoout(dev); + } } (void)dwc2_quirk_irq_clear(dev); @@ -1981,6 +2170,7 @@ static int udc_dwc2_init_controller(const struct device *dev) sys_write32(USB_DWC2_GINTSTS_OEPINT | USB_DWC2_GINTSTS_IEPINT | USB_DWC2_GINTSTS_ENUMDONE | USB_DWC2_GINTSTS_USBRST | USB_DWC2_GINTSTS_WKUPINT | USB_DWC2_GINTSTS_USBSUSP | + USB_DWC2_GINTSTS_INCOMPISOOUT | USB_DWC2_GINTSTS_INCOMPISOIN | USB_DWC2_GINTSTS_SOF, (mem_addr_t)&base->gintmsk); From de904dfe940357f7c2793a18966522c7bafe4479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 19 Aug 2024 10:34:04 +0200 Subject: [PATCH 36/39] [nrf fromtree] drivers: udc_dwc2: Do not enable inactive endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fail TxFIFO write if the endpoint is not activated, because there is essentially no fifo assigned. Writing to not activated endpoint is possible, but it does tend to corrupt fifo number 0 which is used for control transfers. USB stack should not really be attempting to write to endpoints that have not been activated (ep_enable called). Signed-off-by: Tomasz Moń (cherry picked from commit 0b242dea4244d2474259699f97da9472fb4e3145) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 9dcae103e1e..b778d90b987 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -409,6 +409,15 @@ static int dwc2_tx_fifo_write(const struct device *dev, } diepctl = sys_read32(diepctl_reg); + if (!(diepctl & USB_DWC2_DEPCTL_USBACTEP)) { + /* Do not attempt to write data on inactive endpoint, because + * no fifo is assigned to inactive endpoint and therefore it is + * possible that the write will corrupt other endpoint fifo. + */ + irq_unlock(key); + return -ENOENT; + } + if (is_iso) { if (priv->sof_num & 1) { diepctl |= USB_DWC2_DEPCTL_SETODDFR; From 583ce6e94a7d3d0ef3fab1bca0d7a93fe5f02fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 19 Aug 2024 10:41:24 +0200 Subject: [PATCH 37/39] [nrf fromtree] drivers: udc_dwc2: Allow not-multiple-of-4 mps in DMA mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the early max packet size check on endpoint activation, because there are valid use cases where the max packet size is not multiple of 4 and the stack won't perform multi-transaction transfers. Example use case is UAC2 explicit feedback endpoint that has Max Packet Size equal 3 when device is operating at Full-Speed. Signed-off-by: Tomasz Moń (cherry picked from commit debc7446655da6387ecb39264c9614cd8e755083) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index b778d90b987..d977acec030 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -1491,23 +1491,6 @@ static int udc_dwc2_ep_activate(const struct device *dev, dxepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl; } - if (priv->bufferdma && (udc_mps_ep_size(cfg) % 4)) { - /* TODO: In Buffer DMA mode, DMA will insert padding bytes in - * between packets if endpoint Max Packet Size is not multiple - * of 4 (DWORD) and single transfer spans across multiple - * packets. - * - * In order to support such Max Packet Sizes, the driver would - * have to remove the padding in between the packets. Besides - * just driver shuffling the data, the buffers would have to be - * large enough to temporarily hold the paddings. - * - * For the time being just error out early. - */ - LOG_ERR("Driver requires MPS to be multiple of 4"); - return -EINVAL; - } - dxepctl = sys_read32(dxepctl_reg); /* Set max packet size */ dxepctl &= ~USB_DWC2_DEPCTL_MPS_MASK; From d7881c13c13d68e2705876ef827afa9e6b944854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 19 Aug 2024 10:47:54 +0200 Subject: [PATCH 38/39] [nrf fromtree] usb: device_next: Respect granularity in UDC buf pool define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round up buffer size used in UDC_BUF_POOL_DEFINE() to respect the required buffer granularity. The issue was observed with UAC2 explicit feedback data, but the problem applies to any UDC_BUF_POOL_DEFINE() use. In order for every buffer returned by UDC_BUF_POOL_DEFINE() to be both aligned and to have required granurality, it is required to allocate the buffers for ROUND_UP(size, LCM(UDC_BUF_GRANULARITY, UDC_BUF_ALIGN)). Because we do not have Least Common Multiple nor Greatest Common Divisor compile time macros, assume that granularity is multiple of alignment. Validate the assumption with a build time assert. When we get a target where this assumption fails we would have to come up with a solution to compute LCM and/or GCD at compile time. Signed-off-by: Tomasz Moń (cherry picked from commit 0f41950564b5a8e2bdf1649aff01fa30fcbdc417) Signed-off-by: Tomasz Moń --- include/zephyr/drivers/usb/udc_buf.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/zephyr/drivers/usb/udc_buf.h b/include/zephyr/drivers/usb/udc_buf.h index aae4cf28fba..69b85da94db 100644 --- a/include/zephyr/drivers/usb/udc_buf.h +++ b/include/zephyr/drivers/usb/udc_buf.h @@ -132,15 +132,17 @@ extern const struct net_buf_data_cb net_buf_dma_cb; */ #define UDC_BUF_POOL_DEFINE(pname, count, size, ud_size, fdestroy) \ _NET_BUF_ARRAY_DEFINE(pname, count, ud_size); \ + BUILD_ASSERT((UDC_BUF_GRANULARITY) % (UDC_BUF_ALIGN) == 0, \ + "Code assumes granurality is multiple of alignment"); \ static uint8_t __nocache __aligned(UDC_BUF_ALIGN) \ - net_buf_data_##pname[count][size]; \ + net_buf_data_##pname[count][ROUND_UP(size, UDC_BUF_GRANULARITY)];\ static const struct net_buf_pool_fixed net_buf_fixed_##pname = { \ .data_pool = (uint8_t *)net_buf_data_##pname, \ }; \ static const struct net_buf_data_alloc net_buf_fixed_alloc_##pname = { \ .cb = &net_buf_fixed_cb, \ .alloc_data = (void *)&net_buf_fixed_##pname, \ - .max_alloc_size = size, \ + .max_alloc_size = ROUND_UP(size, UDC_BUF_GRANULARITY), \ }; \ static STRUCT_SECTION_ITERABLE(net_buf_pool, pname) = \ NET_BUF_POOL_INITIALIZER(pname, &net_buf_fixed_alloc_##pname, \ From ad923b14ec73b896c24de8b1ee2818d3a743b114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Wed, 11 Sep 2024 12:53:25 +0200 Subject: [PATCH 39/39] [nrf fromtree] drivers: usb: udc_dwc2: Restrict TxFIFO to SPRAM size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not allocate TxFIFO in a way that could corrupt Endpoint Information Controller data (stored at DFIFO Depth address) or access locations outside the SPRAM. Signed-off-by: Tomasz Moń (cherry picked from commit 6582529d1f3646229af53876edd8b580737cffa8) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index d977acec030..1a780ac920f 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -1393,6 +1393,11 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, return -ENOMEM; } + /* Do not allocate TxFIFO outside the SPRAM */ + if (txfaddr + txfdep > priv->dfifodepth) { + return -ENOMEM; + } + /* Set FIFO depth (32-bit words) and address */ dwc2_set_txf(dev, ep_idx - 1, txfdep, txfaddr); } else {