This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 5eeb0ae939f944bff578b6ab6967efd0db1c1b0f Author: raiden00pl <[email protected]> AuthorDate: Tue Jul 21 10:23:33 2026 +0200 arch/nrf5x: fix USBD data IN back-to-back packet loss A data IN endpoint has a single hardware buffer, but the driver armed the next packet before the host had read the previous one, silently overwriting it and dropping data under sustained bulk IN traffic. Track an armed-packet-in-flight state per endpoint (epinflight) and defer re-arming until the host read completes (EPDATASTATUS), sending the next packet from nrf52_epdatainterrupt(). Also release the DMA lock right after the busy-wait for ENDEPIN on data endpoints so other endpoints do not stall on an interrupt round-trip. Signed-off-by: raiden00pl <[email protected]> Assisted-by: Claude Code --- arch/arm/src/nrf52/nrf52_usbd.c | 64 +++++++++++++++++++++++++++++++++++++++-- arch/arm/src/nrf53/nrf53_usbd.c | 64 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/arch/arm/src/nrf52/nrf52_usbd.c b/arch/arm/src/nrf52/nrf52_usbd.c index a05391a520e..2d0398b082c 100644 --- a/arch/arm/src/nrf52/nrf52_usbd.c +++ b/arch/arm/src/nrf52/nrf52_usbd.c @@ -278,6 +278,8 @@ struct nrf52_usbdev_s bool dmanow; /* DMA transfer pending */ uint16_t dmaepinwait; /* EP IN waiting for DMA */ uint16_t dmaepoutwait; /* EP OUT waitning for DMA */ + uint16_t epinflight; /* EP IN packet armed, awaiting + * host read (EPDATASTATUS) */ /* E0 SETUP data buffering. * @@ -1054,6 +1056,22 @@ static void nrf52_epin_transfer(struct nrf52_ep_s *privep, uint8_t *buf, while (nrf52_getreg(NRF52_USBD_EVENTS_ENDEPIN(privep->epphy)) == 0 && nrf52_getreg(NRF52_USBD_EVENTS_USBRESET) == 0); + + /* For data endpoints the EasyDMA transfer into the endpoint buffer is now + * complete (busy-wait above), so errata 199 no longer applies. Clear the + * ENDEPIN event and release the DMA lock right away so that transfers on + * other endpoints can start without waiting for a separate ENDEPIN + * interrupt round-trip. Re-arming of this endpoint is prevented + * separately by priv->epinflight until the host reads the packet. EP0 is + * left to the normal interrupt flow (its control state machine depends on + * the ENDEPIN interrupt). + */ + + if (privep->epphy != EP0) + { + nrf52_putreg(0, NRF52_USBD_EVENTS_ENDEPIN(privep->epphy)); + nrf52_startdma_ack(priv); + } } /**************************************************************************** @@ -1146,6 +1164,18 @@ static void nrf52_epin_request(struct nrf52_usbdev_s *priv, return; } + /* If a packet is already armed on this data endpoint and is still waiting + * to be read by the host, do not overwrite the endpoint buffer. The next + * packet is sent from nrf52_epdatainterrupt() once the host has read this + * one (EPDATASTATUS). + */ + + if (privep->epphy != EP0 && + (priv->epinflight & (1 << privep->epphy)) != 0) + { + return; + } + /* Check the request from the head of the endpoint request queue */ privreq = nrf52_rqpeek(privep); @@ -1188,6 +1218,16 @@ static void nrf52_epin_request(struct nrf52_usbdev_s *priv, buf = privreq->req.buf + privreq->req.xfrd; nrf52_epin_transfer(privep, buf, nbytes); + /* Mark the endpoint as having a packet awaiting host read. This must + * be set before nrf52_req_complete() below, which may re-enter this + * function through the class driver's completion callback. + */ + + if (privep->epphy != EP0) + { + priv->epinflight |= (1 << privep->epphy); + } + /* Update for the next time through the loop */ privreq->req.xfrd += nbytes; @@ -1198,9 +1238,16 @@ static void nrf52_epin_request(struct nrf52_usbdev_s *priv, nrf52_epin_transfer(privep, NULL, 0); - /* ACK zero-length DMA transfer right away */ + if (privep->epphy != EP0) + { + priv->epinflight |= (1 << privep->epphy); + } + else + { + /* ACK zero-length DMA transfer right away */ - nrf52_startdma_ack(priv); + nrf52_startdma_ack(priv); + } } /* Has all the request data been sent? */ @@ -1610,6 +1657,13 @@ static void nrf52_usbreset(struct nrf52_usbdev_s *priv) priv->epavail[0] = NRF52_EP_AVAILABLE; priv->epavail[1] = NRF52_EP_AVAILABLE; + /* Reset DMA and endpoint in-flight state */ + + priv->dmanow = false; + priv->dmaepinwait = 0; + priv->dmaepoutwait = 0; + priv->epinflight = 0; + /* Disable all end points */ for (i = 0; i < NRF52_NENDPOINTS ; i++) @@ -1893,6 +1947,12 @@ static void nrf52_epdatainterrupt(struct nrf52_usbdev_s *priv) if (datastatus & USBD_EPDATASTATUS_EPIN(epno)) { + /* The host has read the armed packet, so the endpoint buffer can + * be refilled with the next packet. + */ + + priv->epinflight &= ~(1 << epno); + privep = &priv->epin[epno]; nrf52_epin_request(priv, privep); } diff --git a/arch/arm/src/nrf53/nrf53_usbd.c b/arch/arm/src/nrf53/nrf53_usbd.c index 19577ebe527..b6e5a30542d 100644 --- a/arch/arm/src/nrf53/nrf53_usbd.c +++ b/arch/arm/src/nrf53/nrf53_usbd.c @@ -278,6 +278,8 @@ struct nrf53_usbdev_s bool dmanow; /* DMA transfer pending */ uint16_t dmaepinwait; /* EP IN waiting for DMA */ uint16_t dmaepoutwait; /* EP OUT waitning for DMA */ + uint16_t epinflight; /* EP IN packet armed, awaiting + * host read (EPDATASTATUS) */ /* E0 SETUP data buffering. * @@ -1054,6 +1056,22 @@ static void nrf53_epin_transfer(struct nrf53_ep_s *privep, uint8_t *buf, while (nrf53_getreg(NRF53_USBD_EVENTS_ENDEPIN(privep->epphy)) == 0 && nrf53_getreg(NRF53_USBD_EVENTS_USBRESET) == 0); + + /* For data endpoints the EasyDMA transfer into the endpoint buffer is now + * complete (busy-wait above), so errata 199 no longer applies. Clear the + * ENDEPIN event and release the DMA lock right away so that transfers on + * other endpoints can start without waiting for a separate ENDEPIN + * interrupt round-trip. Re-arming of this endpoint is prevented + * separately by priv->epinflight until the host reads the packet. EP0 is + * left to the normal interrupt flow (its control state machine depends on + * the ENDEPIN interrupt). + */ + + if (privep->epphy != EP0) + { + nrf53_putreg(0, NRF53_USBD_EVENTS_ENDEPIN(privep->epphy)); + nrf53_startdma_ack(priv); + } } /**************************************************************************** @@ -1146,6 +1164,18 @@ static void nrf53_epin_request(struct nrf53_usbdev_s *priv, return; } + /* If a packet is already armed on this data endpoint and is still waiting + * to be read by the host, do not overwrite the endpoint buffer. The next + * packet is sent from nrf53_epdatainterrupt() once the host has read this + * one (EPDATASTATUS). + */ + + if (privep->epphy != EP0 && + (priv->epinflight & (1 << privep->epphy)) != 0) + { + return; + } + /* Check the request from the head of the endpoint request queue */ privreq = nrf53_rqpeek(privep); @@ -1188,6 +1218,16 @@ static void nrf53_epin_request(struct nrf53_usbdev_s *priv, buf = privreq->req.buf + privreq->req.xfrd; nrf53_epin_transfer(privep, buf, nbytes); + /* Mark the endpoint as having a packet awaiting host read. This must + * be set before nrf53_req_complete() below, which may re-enter this + * function through the class driver's completion callback. + */ + + if (privep->epphy != EP0) + { + priv->epinflight |= (1 << privep->epphy); + } + /* Update for the next time through the loop */ privreq->req.xfrd += nbytes; @@ -1198,9 +1238,16 @@ static void nrf53_epin_request(struct nrf53_usbdev_s *priv, nrf53_epin_transfer(privep, NULL, 0); - /* ACK zero-length DMA transfer right away */ + if (privep->epphy != EP0) + { + priv->epinflight |= (1 << privep->epphy); + } + else + { + /* ACK zero-length DMA transfer right away */ - nrf53_startdma_ack(priv); + nrf53_startdma_ack(priv); + } } /* Has all the request data been sent? */ @@ -1610,6 +1657,13 @@ static void nrf53_usbreset(struct nrf53_usbdev_s *priv) priv->epavail[0] = NRF53_EP_AVAILABLE; priv->epavail[1] = NRF53_EP_AVAILABLE; + /* Reset DMA and endpoint in-flight state */ + + priv->dmanow = false; + priv->dmaepinwait = 0; + priv->dmaepoutwait = 0; + priv->epinflight = 0; + /* Disable all end points */ for (i = 0; i < NRF53_NENDPOINTS ; i++) @@ -1893,6 +1947,12 @@ static void nrf53_epdatainterrupt(struct nrf53_usbdev_s *priv) if (datastatus & USBD_EPDATASTATUS_EPIN(epno)) { + /* The host has read the armed packet, so the endpoint buffer can + * be refilled with the next packet. + */ + + priv->epinflight &= ~(1 << epno); + privep = &priv->epin[epno]; nrf53_epin_request(priv, privep); }
