From e0328298d9212b3189ccda0d25691406c0b15d5d Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Fri, 1 Aug 2025 19:23:35 +0200 Subject: [PATCH 1/2] drivers: usb: stm32: Enable High Speed interface The High Speed mode is not available once driver do not report it is capable when applicable. This fixes the selection and allow to build the High Speed when device supports. In addition, this increases the maximum endpoint size to allow isochronous transfers. Signed-off-by: BUDKE Gerson Fernando --- drivers/usb/udc/Kconfig.stm32 | 1 + drivers/usb/udc/udc_stm32.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/udc/Kconfig.stm32 b/drivers/usb/udc/Kconfig.stm32 index 2d7684469a9fc..056f23187d507 100644 --- a/drivers/usb/udc/Kconfig.stm32 +++ b/drivers/usb/udc/Kconfig.stm32 @@ -9,6 +9,7 @@ config UDC_STM32 select USE_STM32_LL_USB select USE_STM32_HAL_PCD select USE_STM32_HAL_PCD_EX + select UDC_DRIVER_HAS_HIGH_SPEED_SUPPORT if DT_HAS_ST_STM32_OTGHS_ENABLED select PINCTRL default y help diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index 53b9654ed14c1..5ac91b2d600dd 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -1223,7 +1223,7 @@ static int udc_stm32_driver_init0(const struct device *dev) ep_cfg_out[i].caps.bulk = 1; ep_cfg_out[i].caps.interrupt = 1; ep_cfg_out[i].caps.iso = 1; - ep_cfg_out[i].caps.mps = cfg->ep_mps; + ep_cfg_out[i].caps.mps = (cfg->speed_idx == 2) ? 1024 : 1023; } ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; @@ -1243,7 +1243,7 @@ static int udc_stm32_driver_init0(const struct device *dev) ep_cfg_in[i].caps.bulk = 1; ep_cfg_in[i].caps.interrupt = 1; ep_cfg_in[i].caps.iso = 1; - ep_cfg_in[i].caps.mps = 1023; + ep_cfg_in[i].caps.mps = (cfg->speed_idx == 2) ? 1024 : 1023; } ep_cfg_in[i].addr = USB_EP_DIR_IN | i; From 68b57ea8dca4cf3c0b6f62683d2610f841a3769f Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Fri, 1 Aug 2025 18:50:08 +0200 Subject: [PATCH 2/2] drivers: usb: stm32: Fix data out on control ep The Linux Compliance tests that are available in the tools/usb folder can be used to validate a usb stack implementation. When ran those tests in the current stm32 boards using the testusb sample next it is possible to see that tests 14 and 21 are failing. Those tests check corner cases when data is expected to be transfered in the control endpoint. This add the missing piece to allow all tests pass when using the devices in Full Speed. When enabling High Speed the test 13 starts to failing. The test 13 is about set/clear the halt endpoint. Fixes #93924 Signed-off-by: BUDKE Gerson Fernando --- drivers/usb/udc/udc_stm32.c | 73 +++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index 5ac91b2d600dd..19b801356c3e5 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -322,15 +322,15 @@ static void handle_msg_data_out(struct udc_stm32_data *priv, uint8_t epnum, uint LOG_DBG("DataOut ep 0x%02x", ep); epcfg = udc_get_ep_cfg(dev, ep); - udc_ep_set_busy(epcfg, false); - buf = udc_buf_get(epcfg); if (unlikely(buf == NULL)) { + udc_ep_set_busy(epcfg, false); LOG_ERR("ep 0x%02x queue is empty", ep); return; } net_buf_add(buf, rx_count); + udc_ep_set_busy(epcfg, false); if (ep == USB_CONTROL_EP_OUT) { if (udc_ctrl_stage_is_status_out(dev)) { @@ -353,6 +353,40 @@ static void handle_msg_data_out(struct udc_stm32_data *priv, uint8_t epnum, uint } } +static void handle_msg_data_ep0_out(struct udc_stm32_data *priv, uint8_t epnum, uint16_t rx_count) +{ + const struct device *dev = priv->dev; + struct udc_ep_config *epcfg; + uint8_t ep = epnum | USB_EP_DIR_OUT; + struct net_buf *buf; + uint8_t *data; + uint32_t len; + + epcfg = udc_get_ep_cfg(dev, ep); + buf = udc_buf_peek(epcfg); + if (unlikely(buf == NULL)) { + udc_ep_set_busy(epcfg, false); + return; + } + + len = net_buf_tailroom(buf); + + /* Check if all data was transferred and then go back to normal flow + * otherwise continue receiving data + */ + LOG_DBG("xfer size: %d, free: %d, size: %d", rx_count, len, buf->size); + if (len == rx_count || rx_count < epcfg->mps) { + /* busy will be release in handle_msg_data_out() */ + handle_msg_data_out(priv, epnum, rx_count); + return; + } + + net_buf_add(buf, rx_count); + data = net_buf_tail(buf); + + HAL_PCD_EP_Receive(&priv->pcd, epcfg->addr, data, buf->size); +} + static void handle_msg_data_in(struct udc_stm32_data *priv, uint8_t epnum) { const struct device *dev = priv->dev; @@ -363,13 +397,14 @@ static void handle_msg_data_in(struct udc_stm32_data *priv, uint8_t epnum) LOG_DBG("DataIn ep 0x%02x", ep); epcfg = udc_get_ep_cfg(dev, ep); - udc_ep_set_busy(epcfg, false); - buf = udc_buf_peek(epcfg); if (unlikely(buf == NULL)) { + udc_ep_set_busy(epcfg, false); return; } + udc_ep_set_busy(epcfg, false); + if (ep == USB_CONTROL_EP_IN && buf->len) { const struct udc_stm32_config *cfg = dev->config; uint32_t len = MIN(cfg->ep0_mps, buf->len); @@ -420,6 +455,21 @@ static void handle_msg_data_in(struct udc_stm32_data *priv, uint8_t epnum) } } +static void drop_control_transfers(const struct device *dev) +{ + struct net_buf *buf; + + buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); + if (buf != NULL) { + net_buf_unref(buf); + } + + buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN)); + if (buf != NULL) { + net_buf_unref(buf); + } +} + static void handle_msg_setup(struct udc_stm32_data *priv) { struct usb_setup_packet *setup = (void *)priv->pcd.Setup; @@ -427,15 +477,16 @@ static void handle_msg_setup(struct udc_stm32_data *priv) struct net_buf *buf; int err; + drop_control_transfers(dev); + 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; } + net_buf_add_mem(buf, setup, sizeof(struct usb_setup_packet)); udc_ep_buf_set_setup(buf); - memcpy(buf->data, setup, 8); - net_buf_add(buf, 8); udc_ctrl_update_stage(dev, buf); @@ -450,13 +501,16 @@ static void handle_msg_setup(struct udc_stm32_data *priv) if (udc_ctrl_stage_is_data_out(dev)) { /* Allocate and feed buffer for data OUT stage */ + LOG_DBG("s:%p|feed for -out-", (void *)buf); err = usbd_ctrl_feed_dout(dev, udc_data_stage_length(buf)); if (err == -ENOMEM) { udc_submit_ep_event(dev, buf, err); } } else if (udc_ctrl_stage_is_data_in(dev)) { + LOG_DBG("s:%p|feed for -in-status", (void *)buf); udc_ctrl_submit_s_in_status(dev); } else { + LOG_DBG("s:%p|no data", (void *)buf); udc_ctrl_submit_s_status(dev); } } @@ -477,7 +531,12 @@ static void udc_stm32_thread_handler(void *arg1, void *arg2, void *arg3) handle_msg_data_in(priv, msg.ep); break; case UDC_STM32_MSG_DATA_OUT: - handle_msg_data_out(priv, msg.ep, msg.rx_count); + /* workaround to xfer data stage on ep0 */ + if (msg.ep == USB_CONTROL_EP_OUT) { + handle_msg_data_ep0_out(priv, msg.ep, msg.rx_count); + } else { + handle_msg_data_out(priv, msg.ep, msg.rx_count); + } break; } }