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();
 

Reply via email to