From 810c7cac63c68fc9cd83b15d3aafa1e5bc1ae9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 11 Jul 2025 08:26:52 +0200 Subject: [PATCH 1/8] [nrf fromtree] samples: usb: uac2: implicit: Stop processing micophone data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop processing microphone data on error or when streaming ends. This avoids I2S read timeouts due to audio data not being available while streaming. Signed-off-by: Tomasz Moń (cherry picked from commit 943fc6fd88c89c08a47a71b6139426574f38ac20) --- samples/subsys/usb/uac2_implicit_feedback/src/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/subsys/usb/uac2_implicit_feedback/src/main.c b/samples/subsys/usb/uac2_implicit_feedback/src/main.c index b7720fe0331..12abdf4fe68 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/src/main.c +++ b/samples/subsys/usb/uac2_implicit_feedback/src/main.c @@ -93,6 +93,7 @@ static void uac2_terminal_update_cb(const struct device *dev, uint8_t terminal, !ctx->microphone_enabled) { i2s_trigger(ctx->i2s_dev, I2S_DIR_BOTH, I2S_TRIGGER_DROP); ctx->i2s_started = false; + ctx->rx_started = false; ctx->i2s_counter = 0; ctx->plus_ones = ctx->minus_ones = 0; if (ctx->pending_mic_samples) { @@ -172,6 +173,7 @@ static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal, ret = i2s_write(ctx->i2s_dev, buf, size); if (ret < 0) { ctx->i2s_started = false; + ctx->rx_started = false; ctx->i2s_counter = 0; ctx->plus_ones = ctx->minus_ones = 0; if (ctx->pending_mic_samples) { From f0bc3e1037fa2db45126c1bdd17a223d64979f6e Mon Sep 17 00:00:00 2001 From: Victor Brzeski Date: Thu, 5 Jun 2025 15:51:12 -0700 Subject: [PATCH 2/8] [nrf fromtree] usb: device_next: hid: Fix endpoint address getters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit resolves a bug where the USB Endpoint provided by the HID driver was incorrect when in High Speed mode. Signed-off-by: Victor Brzeski (cherry picked from commit c0e1268d9f185cb844346c47466f9985eec88e9a) Signed-off-by: Tomasz Moń --- subsys/usb/device_next/class/usbd_hid.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/subsys/usb/device_next/class/usbd_hid.c b/subsys/usb/device_next/class/usbd_hid.c index d5476a88ed6..d978aea0d27 100644 --- a/subsys/usb/device_next/class/usbd_hid.c +++ b/subsys/usb/device_next/class/usbd_hid.c @@ -84,19 +84,29 @@ struct hid_device_data { static inline uint8_t hid_get_in_ep(struct usbd_class_data *const c_data) { + struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); const struct device *dev = usbd_class_get_private(c_data); const struct hid_device_config *dcfg = dev->config; struct usbd_hid_descriptor *desc = dcfg->desc; + if (USBD_SUPPORTS_HIGH_SPEED && usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { + return desc->hs_in_ep.bEndpointAddress; + } + return desc->in_ep.bEndpointAddress; } static inline uint8_t hid_get_out_ep(struct usbd_class_data *const c_data) { + struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); const struct device *dev = usbd_class_get_private(c_data); const struct hid_device_config *dcfg = dev->config; struct usbd_hid_descriptor *desc = dcfg->desc; + if (USBD_SUPPORTS_HIGH_SPEED && usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { + return desc->hs_out_ep.bEndpointAddress; + } + return desc->out_ep.bEndpointAddress; } From f7a344c8450caf73f6cd88f3a8b0f241661c8b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Wed, 23 Jul 2025 08:18:34 +0200 Subject: [PATCH 3/8] [nrf fromtree] usb: device_next: uac2: Remove unnecessary plus signs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the macros to have only one plus operator per term. This is purely stylistic change, no functional changes. Signed-off-by: Tomasz Moń (cherry picked from commit 673f32428c1374979ba12116ef6d94a8a038527d) --- subsys/usb/device_next/class/usbd_uac2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subsys/usb/device_next/class/usbd_uac2.c b/subsys/usb/device_next/class/usbd_uac2.c index efa3fd6cdf3..0b08673abd2 100644 --- a/subsys/usb/device_next/class/usbd_uac2.c +++ b/subsys/usb/device_next/class/usbd_uac2.c @@ -26,9 +26,9 @@ LOG_MODULE_REGISTER(usbd_uac2, CONFIG_USBD_UAC2_LOG_LEVEL); #define COUNT_UAC2_AS_ENDPOINT_BUFFERS(node) \ IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_audio_streaming), ( \ - + AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node) + \ - + AS_IS_USB_ISO_IN(node) /* ISO IN double buffering */ + \ - AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node))) + + AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node) \ + + AS_IS_USB_ISO_IN(node) /* ISO IN double buffering */ \ + + AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node))) #define COUNT_UAC2_EP_BUFFERS(i) \ + DT_PROP(DT_DRV_INST(i), interrupt_endpoint) \ DT_INST_FOREACH_CHILD(i, COUNT_UAC2_AS_ENDPOINT_BUFFERS) From da3d5e02eab5d1d5e22dd60b7507be0bad7c1d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 11 Jul 2025 14:49:04 +0200 Subject: [PATCH 4/8] [nrf fromtree] usb: device_next: uac2: Double buffering on data OUT endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable double buffering on data OUT endpoints to allow USB stack to enqueue next transfer as soon as possible. This is especially useful when operating at High-Speed where there is significantly less time between subsequent SOF packets. Signed-off-by: Tomasz Moń (cherry picked from commit 2f343bf572a1f05b8bf73e9a0d8a55b9cf0a8d30) --- subsys/usb/device_next/class/usbd_uac2.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/subsys/usb/device_next/class/usbd_uac2.c b/subsys/usb/device_next/class/usbd_uac2.c index 0b08673abd2..00a8fa5b826 100644 --- a/subsys/usb/device_next/class/usbd_uac2.c +++ b/subsys/usb/device_next/class/usbd_uac2.c @@ -28,6 +28,7 @@ LOG_MODULE_REGISTER(usbd_uac2, CONFIG_USBD_UAC2_LOG_LEVEL); IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_audio_streaming), ( \ + AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node) \ + AS_IS_USB_ISO_IN(node) /* ISO IN double buffering */ \ + + AS_IS_USB_ISO_OUT(node) /* ISO OUT double buffering */ \ + AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node))) #define COUNT_UAC2_EP_BUFFERS(i) \ + DT_PROP(DT_DRV_INST(i), interrupt_endpoint) \ @@ -348,6 +349,7 @@ static void schedule_iso_out_read(struct usbd_class_data *const c_data, const struct uac2_cfg *cfg = dev->config; struct uac2_ctx *ctx = dev->data; struct net_buf *buf; + atomic_t *queued_bits = &ctx->as_queued; void *data_buf; int as_idx = terminal_to_as_interface(dev, terminal); int ret; @@ -364,16 +366,19 @@ static void schedule_iso_out_read(struct usbd_class_data *const c_data, return; } - if (atomic_test_and_set_bit(&ctx->as_queued, as_idx)) { - /* Transfer already queued - do not requeue */ - return; + if (atomic_test_and_set_bit(queued_bits, as_idx)) { + queued_bits = &ctx->as_double; + if (atomic_test_and_set_bit(queued_bits, as_idx)) { + /* Transfer already double queued - nothing to do */ + return; + } } /* Prepare transfer to read audio OUT data from host */ data_buf = ctx->ops->get_recv_buf(dev, terminal, mps, ctx->user_data); if (!data_buf) { LOG_ERR("No data buffer for terminal %d", terminal); - atomic_clear_bit(&ctx->as_queued, as_idx); + atomic_clear_bit(queued_bits, as_idx); return; } @@ -386,7 +391,7 @@ static void schedule_iso_out_read(struct usbd_class_data *const c_data, */ ctx->ops->data_recv_cb(dev, terminal, data_buf, 0, ctx->user_data); - atomic_clear_bit(&ctx->as_queued, as_idx); + atomic_clear_bit(queued_bits, as_idx); return; } @@ -394,7 +399,7 @@ static void schedule_iso_out_read(struct usbd_class_data *const c_data, if (ret) { LOG_ERR("Failed to enqueue net_buf for 0x%02x", ep); net_buf_unref(buf); - atomic_clear_bit(&ctx->as_queued, as_idx); + atomic_clear_bit(queued_bits, as_idx); } } @@ -814,6 +819,10 @@ static int uac2_request(struct usbd_class_data *const c_data, struct net_buf *bu if (USB_EP_DIR_IS_OUT(ep)) { ctx->ops->data_recv_cb(dev, terminal, buf->__buf, buf->len, ctx->user_data); + if (buf->frags) { + ctx->ops->data_recv_cb(dev, terminal, buf->frags->__buf, + buf->frags->len, ctx->user_data); + } } else if (!is_feedback) { ctx->ops->buf_release_cb(dev, terminal, buf->__buf, ctx->user_data); if (buf->frags) { From d9ad13fccbbb477b300e4fdcbe7c9f341e5e9642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 11 Jul 2025 15:00:12 +0200 Subject: [PATCH 5/8] [nrf fromtree] usb: device_next: uac2: Do not leak buffer on failed enqueue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Release receive buffer on failed endpoint enqueue. Signed-off-by: Tomasz Moń (cherry picked from commit f9ac3181ff02f738c142d8c1c5be0f403cdd3c86) --- subsys/usb/device_next/class/usbd_uac2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subsys/usb/device_next/class/usbd_uac2.c b/subsys/usb/device_next/class/usbd_uac2.c index 00a8fa5b826..945970c18c5 100644 --- a/subsys/usb/device_next/class/usbd_uac2.c +++ b/subsys/usb/device_next/class/usbd_uac2.c @@ -399,6 +399,8 @@ static void schedule_iso_out_read(struct usbd_class_data *const c_data, if (ret) { LOG_ERR("Failed to enqueue net_buf for 0x%02x", ep); net_buf_unref(buf); + ctx->ops->data_recv_cb(dev, terminal, + data_buf, 0, ctx->user_data); atomic_clear_bit(queued_bits, as_idx); } } From da00cea1b5e6eb1d599fb55697c322c8ddcb22d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 15 Jul 2025 11:26:56 +0200 Subject: [PATCH 6/8] [nrf fromtree] usb: device_next: uac2: Double buffering on feedback endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable double buffering on isochronous feedback endpoint to avoid sending ZLP instead of feedback information. Signed-off-by: Tomasz Moń (cherry picked from commit 223d23a34d667f390a694b1b9ccccf6171c6cf88) --- subsys/usb/device_next/class/usbd_uac2.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/subsys/usb/device_next/class/usbd_uac2.c b/subsys/usb/device_next/class/usbd_uac2.c index 945970c18c5..3a53b5c5d21 100644 --- a/subsys/usb/device_next/class/usbd_uac2.c +++ b/subsys/usb/device_next/class/usbd_uac2.c @@ -29,7 +29,7 @@ LOG_MODULE_REGISTER(usbd_uac2, CONFIG_USBD_UAC2_LOG_LEVEL); + AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node) \ + AS_IS_USB_ISO_IN(node) /* ISO IN double buffering */ \ + AS_IS_USB_ISO_OUT(node) /* ISO OUT double buffering */ \ - + AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node))) + + 2 * AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node))) #define COUNT_UAC2_EP_BUFFERS(i) \ + DT_PROP(DT_DRV_INST(i), interrupt_endpoint) \ DT_INST_FOREACH_CHILD(i, COUNT_UAC2_AS_ENDPOINT_BUFFERS) @@ -88,6 +88,7 @@ struct uac2_ctx { atomic_t as_queued; atomic_t as_double; uint32_t fb_queued; + uint32_t fb_double; }; /* UAC2 device constant data */ @@ -441,7 +442,11 @@ static void write_explicit_feedback(struct usbd_class_data *const c_data, LOG_ERR("Failed to enqueue net_buf for 0x%02x", ep); net_buf_unref(buf); } else { - ctx->fb_queued |= BIT(as_idx); + if (ctx->fb_queued & BIT(as_idx)) { + ctx->fb_double |= BIT(as_idx); + } else { + ctx->fb_queued |= BIT(as_idx); + } } } @@ -813,7 +818,17 @@ static int uac2_request(struct usbd_class_data *const c_data, struct net_buf *bu terminal = cfg->as_terminals[as_idx]; if (is_feedback) { - ctx->fb_queued &= ~BIT(as_idx); + bool clear_double = buf->frags; + + if (ctx->fb_queued & BIT(as_idx)) { + ctx->fb_queued &= ~BIT(as_idx); + } else { + clear_double = true; + } + + if (clear_double) { + ctx->fb_double &= ~BIT(as_idx); + } } else if (!atomic_test_and_clear_bit(&ctx->as_queued, as_idx) || buf->frags) { atomic_clear_bit(&ctx->as_double, as_idx); } @@ -880,7 +895,7 @@ static void uac2_sof(struct usbd_class_data *const c_data) * for now to allow faster recovery (i.e. reduce workload to be * done during this frame). */ - if (ctx->fb_queued & BIT(as_idx)) { + if (ctx->fb_queued & ctx->fb_double & BIT(as_idx)) { continue; } From e42bb3b568bcdad8cb5d4151cdd19cacc303f3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 15 Jul 2025 11:04:13 +0200 Subject: [PATCH 7/8] [nrf fromtree] drivers: udc_dwc2: Rearm isochronous OUT endpoints during incompisoout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rearm isochronous endpoints when handling incomplete iso out interrupt to make it possible to rearm the endpoint in time (before SOF), especially when operating at High-Speed. Signed-off-by: Tomasz Moń (cherry picked from commit 6eb2fa8eddd1a238878efb8f2dbeaf9d00d1c250) --- drivers/usb/udc/udc_dwc2.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index fca8f607ec7..599dfcd8b5d 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -121,6 +121,7 @@ struct udc_dwc2_data { /* Isochronous endpoint enabled (IN on bits 0-15, OUT on bits 16-31) */ uint32_t iso_enabled; uint16_t iso_in_rearm; + uint16_t iso_out_rearm; uint16_t ep_out_disable; uint16_t ep_out_stall; uint16_t txf_set; @@ -2776,6 +2777,7 @@ static inline void dwc2_handle_oepint(const struct device *dev) while (epint) { uint8_t n = find_lsb_set(epint) - 1; mem_addr_t doepint_reg = (mem_addr_t)&base->out_ep[n].doepint; + mem_addr_t doepctl_reg = (mem_addr_t)&base->out_ep[n].doepctl; uint32_t doepint; uint32_t status; @@ -2828,7 +2830,20 @@ static inline void dwc2_handle_oepint(const struct device *dev) } if (status & USB_DWC2_DOEPINT_EPDISBLD) { + uint32_t doepctl = sys_read32(doepctl_reg); + k_event_post(&priv->ep_disabled, BIT(16 + n)); + + if ((usb_dwc2_get_depctl_eptype(doepctl) == USB_DWC2_DEPCTL_EPTYPE_ISO) && + (priv->iso_out_rearm & BIT(n))) { + struct udc_ep_config *cfg; + + /* Try to queue next packet before SOF */ + cfg = udc_get_ep_cfg(dev, n | USB_EP_DIR_OUT); + dwc2_handle_xfer_next(dev, cfg); + + priv->iso_out_rearm &= ~BIT(n); + } } epint &= ~BIT(n); @@ -2913,6 +2928,7 @@ static void dwc2_handle_incompisoout(const struct device *dev) ((priv->sof_num & 1) ? USB_DWC2_DEPCTL_DPID : 0) | USB_DWC2_DEPCTL_USBACTEP; uint32_t eps = (priv->iso_enabled & 0xFFFF0000UL) >> 16; + uint16_t rearm = 0; while (eps) { uint8_t i = find_lsb_set(eps) - 1; @@ -2935,12 +2951,17 @@ static void dwc2_handle_incompisoout(const struct device *dev) buf = udc_buf_get(cfg); if (buf) { udc_submit_ep_event(dev, buf, 0); + + rearm |= BIT(i); } } eps &= ~BIT(i); } + /* Mark endpoints to re-arm in EPDISBLD handler */ + priv->iso_out_rearm = rearm; + sys_write32(USB_DWC2_GINTSTS_INCOMPISOOUT, gintsts_reg); } From 4dd326951cd6a523f8073de8da4b8e8a7a83f73d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Thu, 17 Jul 2025 10:20:34 +0200 Subject: [PATCH 8/8] [nrf fromtree] drivers: udc_dwc2: Fix incomplete iso handling race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Incomplete iso IN/OUT is just informative and its occurrence does not prevent the endpoint from actually transmitting/receiving data. Such "late" isochronous transfers, which are perfectly fine according to USB specification, were observed on Windows host with nRF54H20 running explicit feedback sample operating at High-Speed. The incorrect handling manifested itself with "ISO RX buffer too small" error message. The faulty scenario was: * incompISOIN handler does not find any matching endpoint * incompISOOUT handler disables endpoint, discards buffer and sets rearm flag * next DWC2 interrupt handler iteration after reading GINTSTS * XferCompl interrupt on iso IN endpoint * XferCompl interrupt on iso OUT endpoint - transfer was actually happening to the buffer discarded in incompISOOUT handler - XferCompl handler modified the next buffer * GOUTNakEff interrupt, iso OUT endpoint EPDIS bit is set * EPDisbld interrupt, rearm flag set - the buffer modified by XferCompl is used and fails because it is not large enough Modify the sequence so it accounts for host actions and the above faulty scenario no longer causes any problems. Signed-off-by: Tomasz Moń (cherry picked from commit 8d1f7b3bef5fcbb63b8dab99f85320f02835fe63) --- drivers/usb/udc/udc_dwc2.c | 98 ++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 24 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 599dfcd8b5d..4ef5dc1825c 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -141,6 +141,7 @@ struct udc_dwc2_data { unsigned int enumdone : 1; unsigned int enumspd : 2; unsigned int pending_dout_feed : 1; + unsigned int ignore_ep0_nakeff : 1; enum dwc2_suspend_type suspend_type; /* Number of endpoints including control endpoint */ uint8_t numdeveps; @@ -485,7 +486,6 @@ static int dwc2_tx_fifo_write(const struct device *dev, mem_addr_t dieptsiz_reg = (mem_addr_t)&base->in_ep[ep_idx].dieptsiz; /* TODO: use dwc2_get_dxepctl_reg() */ 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; @@ -617,9 +617,6 @@ static int dwc2_tx_fifo_write(const struct device *dev, } sys_write32(diepctl, diepctl_reg); - /* Clear IN Endpoint NAK Effective interrupt in case it was set */ - sys_write32(USB_DWC2_DIEPINT_INEPNAKEFF, diepint_reg); - if (dwc2_in_completer_mode(dev)) { const uint8_t *src = buf->data; @@ -811,7 +808,20 @@ static void dwc2_handle_xfer_next(const struct device *dev, if (USB_EP_DIR_IS_OUT(cfg->addr)) { dwc2_prep_rx(dev, buf, cfg); } else { - int err = dwc2_tx_fifo_write(dev, cfg, buf); + int err; + + if (cfg->addr == USB_CONTROL_EP_IN && + udc_ctrl_stage_is_status_in(dev)) { + /* It was observed that EPENA results in INEPNAKEFF + * interrupt which leads to endpoint disable. It is not + * clear how to prevent this without violating sequence + * described in Programming Guide, so just set a flag + * for interrupt handler to ignore it. + */ + priv->ignore_ep0_nakeff = 1; + } + + err = dwc2_tx_fifo_write(dev, cfg, buf); if (cfg->addr == USB_CONTROL_EP_IN) { /* Feed a buffer for the next setup packet after arming @@ -862,6 +872,7 @@ static int dwc2_handle_evt_setup(const struct device *dev) * transfer beforehand. In Buffer DMA the SETUP can be copied to any EP0 * OUT buffer. If there is any buffer queued, it is obsolete now. */ + priv->ignore_ep0_nakeff = 0; udc_dwc2_ep_disable(dev, cfg_in, false, true); atomic_and(&priv->xfer_finished, ~(BIT(0) | BIT(16))); @@ -2658,7 +2669,21 @@ static inline void dwc2_handle_iepint(const struct device *dev) } if (status & USB_DWC2_DIEPINT_INEPNAKEFF) { - sys_set_bits(diepctl_reg, USB_DWC2_DEPCTL_EPDIS); + uint32_t diepctl = sys_read32(diepctl_reg); + + if (!(diepctl & USB_DWC2_DEPCTL_NAKSTS)) { + /* Ignore stale NAK effective interrupt */ + } else if (n == 0 && priv->ignore_ep0_nakeff) { + /* Status stage enabled endpoint. NAK will be + * cleared in STSPHSERCVD interrupt. + */ + } else if (diepctl & USB_DWC2_DEPCTL_EPENA) { + diepctl &= ~USB_DWC2_DEPCTL_EPENA; + diepctl |= USB_DWC2_DEPCTL_EPDIS; + sys_write32(diepctl, diepctl_reg); + } else if (priv->iso_in_rearm & (BIT(n))) { + priv->iso_in_rearm &= ~BIT(n); + } } if (status & USB_DWC2_DIEPINT_EPDISBLD) { @@ -2676,6 +2701,13 @@ static inline void dwc2_handle_iepint(const struct device *dev) if ((usb_dwc2_get_depctl_eptype(diepctl) == USB_DWC2_DEPCTL_EPTYPE_ISO) && (priv->iso_in_rearm & BIT(n))) { struct udc_ep_config *cfg = udc_get_ep_cfg(dev, n | USB_EP_DIR_IN); + struct net_buf *buf; + + /* Data is no longer relevant, discard it */ + buf = udc_buf_get(cfg); + if (buf) { + udc_submit_ep_event(dev, buf, 0); + } /* Try to queue next packet before SOF */ dwc2_handle_xfer_next(dev, cfg); @@ -2837,9 +2869,17 @@ static inline void dwc2_handle_oepint(const struct device *dev) if ((usb_dwc2_get_depctl_eptype(doepctl) == USB_DWC2_DEPCTL_EPTYPE_ISO) && (priv->iso_out_rearm & BIT(n))) { struct udc_ep_config *cfg; + struct net_buf *buf; - /* Try to queue next packet before SOF */ cfg = udc_get_ep_cfg(dev, n | USB_EP_DIR_OUT); + + /* Discard disabled transfer buffer */ + buf = udc_buf_get(cfg); + if (buf) { + udc_submit_ep_event(dev, buf, 0); + } + + /* Try to queue next packet before SOF */ dwc2_handle_xfer_next(dev, cfg); priv->iso_out_rearm &= ~BIT(n); @@ -2883,7 +2923,6 @@ static void dwc2_handle_incompisoin(const struct device *dev) /* Check if endpoint didn't receive ISO IN 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 && @@ -2891,13 +2930,7 @@ static void dwc2_handle_incompisoin(const struct device *dev) udc_dwc2_ep_disable(dev, cfg, false, false); - buf = udc_buf_get(cfg); - if (buf) { - /* Data is no longer relevant */ - udc_submit_ep_event(dev, buf, 0); - - rearm |= BIT(i); - } + rearm |= BIT(i); } eps &= ~BIT(i); @@ -2940,7 +2973,6 @@ static void dwc2_handle_incompisoout(const struct device *dev) /* 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 && @@ -2948,12 +2980,7 @@ static void dwc2_handle_incompisoout(const struct device *dev) udc_dwc2_ep_disable(dev, cfg, false, false); - buf = udc_buf_get(cfg); - if (buf) { - udc_submit_ep_event(dev, buf, 0); - - rearm |= BIT(i); - } + rearm |= BIT(i); } eps &= ~BIT(i); @@ -2984,8 +3011,31 @@ static void dwc2_handle_goutnakeff(const struct device *dev) doepctl_reg = (mem_addr_t)&base->out_ep[ep_idx].doepctl; doepctl = sys_read32(doepctl_reg); - /* The application cannot disable control OUT endpoint 0. */ - if (ep_idx != 0) { + if (!(doepctl & USB_DWC2_DEPCTL_EPENA)) { + /* While endpoint was enabled when application requested + * to disable it, there is a race window between setting + * DCTL SGOUTNAK bit and it becoming effective. During + * the window, it is possible for host to actually send + * OUT DATA packet ending the transfer which disables + * the endpoint. + * + * If we arrived here due to INCOMPISOOUT, clearing + * the rearm flag is enough. + */ + if (priv->iso_out_rearm & BIT(ep_idx)) { + priv->iso_out_rearm &= ~BIT(ep_idx); + } + + /* If application requested STALL then set it here, but + * otherwise there's nothing to do. + */ + if (!(priv->ep_out_stall & BIT(ep_idx))) { + continue; + } + } else if (ep_idx != 0) { + /* Only set EPDIS if EPENA is set, but do not set it for + * endpoint 0 which cannot be disabled by application. + */ doepctl |= USB_DWC2_DEPCTL_EPDIS; }