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 128ee6087f587441ef9a4c926e390ebae833ccef Author: Ricard Rosson <[email protected]> AuthorDate: Wed Jul 8 22:04:50 2026 +0100 arch/rp2040: don't re-arm an endpoint request resubmitted from its callback rp2040_txcomplete() and rp2040_rxcomplete() unconditionally called rp2040_wrrequest()/rp2040_rdrequest() to start the next transfer after invoking a request's completion callback. When that callback resubmits a request on the same, now-idle endpoint -- which cdcncm and rndis do from their interrupt/notify completion handlers -- rp2040_epsubmit() already arms the hardware buffer for it. The unconditional re-arm in the completion path then arms the same buffer a second time, toggling the DATA0/DATA1 PID twice. The host sees a stale PID and silently discards the packet as a retransmission, so e.g. the cdcncm NETWORK_CONNECTION / SPEED_CHANGE notifications never reach the host and the interface stays NO-CARRIER. Track whether a request's hardware buffer has already been armed with a per-request flag (set in rp2040_wrrequest/rp2040_rdrequest, cleared in rp2040_epsubmit) and skip the redundant re-arm when the completion callback has already resubmitted. The in-progress multi-packet case (transfer not yet complete) still continues normally. Validated on raspberrypi-pico (RP2040): the cdcncm interrupt-IN notification is now delivered (confirmed with usbmon) and the host brings the link up; previously it never was. This is also the likely cause of the long-standing rndis control-response timeout on this controller. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AHJRvWeBMTHwzpwjaUg4HW Signed-off-by: Ricard Rosson <[email protected]> --- arch/arm/src/rp2040/rp2040_usbdev.c | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/arch/arm/src/rp2040/rp2040_usbdev.c b/arch/arm/src/rp2040/rp2040_usbdev.c index 3be598b60a7..0bdc06deb29 100644 --- a/arch/arm/src/rp2040/rp2040_usbdev.c +++ b/arch/arm/src/rp2040/rp2040_usbdev.c @@ -218,6 +218,7 @@ struct rp2040_req_s { struct usbdev_req_s req; /* Standard USB request */ struct rp2040_req_s *flink; /* Supports a singly linked list */ + bool armed; /* Hardware buffer armed for this request */ }; /* This is the internal representation of an endpoint */ @@ -647,6 +648,7 @@ static void rp2040_reqcomplete(struct rp2040_ep_s *privep, int16_t result) static void rp2040_txcomplete(struct rp2040_ep_s *privep) { struct rp2040_req_s *privreq; + bool completed = false; privreq = rp2040_rqpeek(privep); if (!privreq) @@ -663,10 +665,31 @@ static void rp2040_txcomplete(struct rp2040_ep_s *privep) usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); privep->txnullpkt = 0; rp2040_reqcomplete(privep, OK); + completed = true; } } - rp2040_wrrequest(privep); + if (completed) + { + /* Start the next queued request -- unless it was already armed. The + * completion callback above may have submitted a new request on the + * now idle endpoint; epsubmit then armed the hardware buffer itself, + * and arming it a second time here would toggle the data PID twice + * and make the host silently discard the packet as a retransmission. + */ + + privreq = rp2040_rqpeek(privep); + if (privreq && !privreq->armed) + { + rp2040_wrrequest(privep); + } + } + else if (privreq) + { + /* Continue with the next packet of the in-progress request */ + + rp2040_wrrequest(privep); + } } /**************************************************************************** @@ -699,6 +722,7 @@ static int rp2040_wrrequest(struct rp2040_ep_s *privep) { if (privep->epphy == 0) { + privreq->armed = true; rp2040_epwrite(privep, NULL, 0); } else @@ -724,6 +748,7 @@ static int rp2040_wrrequest(struct rp2040_ep_s *privep) * bytes to send. */ + privreq->armed = true; privep->txnullpkt = 0; if (bytesleft > privep->ep.maxpacket) { @@ -779,6 +804,16 @@ static void rp2040_rxcomplete(struct rp2040_ep_s *privep) { usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); rp2040_reqcomplete(privep, OK); + + /* Re-arm only if the completion callback didn't already do it by + * resubmitting a request (see rp2040_txcomplete). + */ + + privreq = rp2040_rqpeek(privep); + if (privreq && privreq->armed) + { + return; + } } rp2040_rdrequest(privep); @@ -809,6 +844,7 @@ static int rp2040_rdrequest(struct rp2040_ep_s *privep) usbtrace(TRACE_READ(privep->epphy), privreq->req.len); + privreq->armed = true; return rp2040_epread(privep, privreq->req.len); } @@ -1635,6 +1671,7 @@ static int rp2040_epsubmit(struct usbdev_ep_s *ep, req->result = -EINPROGRESS; req->xfrd = 0; + privreq->armed = false; flags = enter_critical_section();
