From 9ac878d0b65cdf42d45c1698011b7e7e134ad16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 11 Mar 2025 12:05:59 +0100 Subject: [PATCH 1/2] [nrf fromlist] usb: device_next: Fix SETUP leak on timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Control transfers can be abruptly terminated by host. The most common use case when new SETUP token is received before the previous transfer ends, is when the host timeouts on previous control transfer. Fix the SETUP data leak by releasing stored setup buffer if new transfer is started out of sequence. Upstream PR #: 86925 Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_common.c | 9 +++++++++ subsys/usb/device_next/usbd_ch9.c | 2 ++ 2 files changed, 11 insertions(+) diff --git a/drivers/usb/udc/udc_common.c b/drivers/usb/udc/udc_common.c index bef3f657382..b5bae12192f 100644 --- a/drivers/usb/udc/udc_common.c +++ b/drivers/usb/udc/udc_common.c @@ -745,6 +745,7 @@ int udc_enable(const struct device *dev) } data->stage = CTRL_PIPE_STAGE_SETUP; + data->setup = NULL; ret = api->enable(dev); if (ret == 0) { @@ -1011,6 +1012,14 @@ void udc_ctrl_update_stage(const struct device *dev, if (bi->setup && bi->ep == USB_CONTROL_EP_OUT) { uint16_t length = udc_data_stage_length(buf); + if (data->setup) { + /* Host started new control transfer before the previous + * one finished. This was most likely due to a timeout. + * Release old setup buffer as it is no longer needed. + */ + net_buf_unref(data->setup); + } + data->setup = buf; if (data->stage != CTRL_PIPE_STAGE_SETUP) { diff --git a/subsys/usb/device_next/usbd_ch9.c b/subsys/usb/device_next/usbd_ch9.c index ed7cf14f68d..6d8338f58fb 100644 --- a/subsys/usb/device_next/usbd_ch9.c +++ b/subsys/usb/device_next/usbd_ch9.c @@ -1102,6 +1102,7 @@ int usbd_handle_ctrl_xfer(struct usbd_context *const uds_ctx, buf, bi->ep, buf->len, bi->setup, bi->data, bi->status); if (bi->setup && bi->ep == USB_CONTROL_EP_OUT) { + struct udc_data *data = uds_ctx->dev->data; struct net_buf *next_buf; if (ctrl_xfer_get_setup(uds_ctx, buf)) { @@ -1112,6 +1113,7 @@ int usbd_handle_ctrl_xfer(struct usbd_context *const uds_ctx, /* Remove setup packet buffer from the chain */ next_buf = net_buf_frag_del(NULL, buf); + data->setup = NULL; if (next_buf == NULL) { LOG_ERR("Buffer for data|status is missing"); goto ctrl_xfer_stall; From 119fcc0bb0b42c93214fe36cb571f9c13b56a344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 15 Apr 2025 10:52:08 +0200 Subject: [PATCH 2/2] [nrf fromlist] drivers: udc_dwc2: Disable control IN endpoint on timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DWC2 core sets DIEPCTL0 SNAK when SETUP packet is received. The CNAK bit results in device sending NAK in response to IN token sent to EP0, but it does not modify the TxFIFO in any way. The stale data in TxFIFO can then lead to "FIFO space is too low" error. Solve the issue by disabling and flushing IN endpoint 0 if previous control transfer did not finish. Upstream PR #: 88642 Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index d1fc555d839..d0f77ae0207 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -139,6 +139,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 @@ -801,6 +804,7 @@ static int dwc2_handle_evt_setup(const struct device *dev) buf = udc_buf_get_all(dev, USB_CONTROL_EP_IN); if (buf) { + udc_dwc2_ep_disable(dev, udc_get_ep_cfg(dev, USB_CONTROL_EP_IN), false); net_buf_unref(buf); } @@ -1493,7 +1497,8 @@ static void udc_dwc2_ep_disable(const struct device *dev, dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr); dxepctl = sys_read32(dxepctl_reg); - if (!is_iso && (dxepctl & USB_DWC2_DEPCTL_NAKSTS)) { + if (!is_iso && (dxepctl & USB_DWC2_DEPCTL_NAKSTS) && + !(dxepctl & USB_DWC2_DEPCTL_EPENA)) { /* Endpoint already sends forced NAKs. STALL if necessary. */ if (stall) { dxepctl |= USB_DWC2_DEPCTL_STALL;