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 58181135ab2689dd9502cdedbb152904d7374dce
Author: Jacob Dahl <[email protected]>
AuthorDate: Tue Aug 25 18:59:25 2026 -0600

    arch/arm/src/imxrt: Fix FlexCAN TX timeout aborting live mailboxes.
    
    imxrt_txtimeout_work() had four defects that together let one expired frame
    take the interface down permanently.
    
    It aborted mailbox RXMBCOUNT + mbi while the deadline it consulted belongs 
to
    RXMBCOUNT + 1 + mbi, so every abort landed one mailbox low and mbi == 0 
wrote
    CAN_TXMB_ABORT into the buffer reserved for the ERR005829 workaround, while
    the highest TX mailbox was never aborted at all.
    
    Its expiry test read `now.tv_sec > d.tv_sec || now.tv_usec > d.tv_usec`, 
which
    declares any deadline that crosses a second boundary expired: in that case 
the
    deadline's microsecond field is always the smaller of the two. The `now` it
    compared against was a struct timespec cast to a struct timeval, so writing
    tv_usec wrote over tv_nsec and tv_sec was whatever the cast happened to line
    up with.
    
    imxrt_txdone() cancelled the watchdog but left txmb[].deadline set, so a
    retired mailbox looked expired forever and the next watchdog expiry on any
    other mailbox aborted whatever frame had since been loaded there.
    
    The walk ran to TXMBCOUNT, which counts the reserved mailbox as well, so its
    last iteration addressed mailbox TOTALMBCOUNT - one past the ring, and
    mb_address[] one past its end. Only txmb[] never being written that far kept
    it in bounds. TXMBRINGSIZE now names the ring size that the rest of the 
driver
    already assumes.
    
    Aborting a frame that is already on the wire raises a bit error, so the
    transmit error counter climbs and the node goes error passive. Since
    imxrt_txmb_next() only hands out a mailbox above every pending one, a 
mailbox
    left in DATAORREMOTE also pins the allocator at TOTALMBCOUNT and transmit
    never recovers.
    
    Measured on an ARK FMU-v6XRT with a DroneCAN GNSS node on the bus, offering
    736 frames/s (9% of a 1 Mbit/s bus) from the PX4 uavcan driver: before, the
    interface transmitted 0 frames/s with ECR[TXERRCNT] pinned at 128 and
    ESR1[FLTCONF] error passive, and stayed dead across a reboot. After, 734
    frames/s, 0.1% loss, TXERRCNT 0, error active.
    
    Assisted-by: Claude:claude-opus-5
    Signed-off-by: Jacob Dahl <[email protected]>
---
 arch/arm/src/imxrt/imxrt_flexcan.c | 71 +++++++++++++++++++++++++++++---------
 1 file changed, 54 insertions(+), 17 deletions(-)

diff --git a/arch/arm/src/imxrt/imxrt_flexcan.c 
b/arch/arm/src/imxrt/imxrt_flexcan.c
index a6b7ff22bf1..19dc6036040 100644
--- a/arch/arm/src/imxrt/imxrt_flexcan.c
+++ b/arch/arm/src/imxrt/imxrt_flexcan.c
@@ -83,6 +83,13 @@
 #define TXMBCOUNT                   (CONFIG_IMXRT_FLEXCAN_TXMB + 1)
 #define TOTALMBCOUNT                RXMBCOUNT + TXMBCOUNT
 
+/* TXMBCOUNT spans the mailbox reserved for the ERR005829 workaround as well,
+ * so the transmit ring is one shorter than the mailboxes it covers: the
+ * usable ones are RXMBCOUNT + 1 .. TOTALMBCOUNT - 1.
+ */
+
+#define TXMBRINGSIZE                (TXMBCOUNT - 1)
+
 #define IFLAG1_RX                   ((1 << RXMBCOUNT)-1)
 #define IFLAG1_TX                   (((1 << TXMBCOUNT)-2) << RXMBCOUNT)
 
@@ -269,7 +276,7 @@ struct imxrt_driver_s
   int mb_address_offset;
   spinlock_t lock;
 #ifdef TX_TIMEOUT_WQ
-  struct wdog_s txtimeout[TXMBCOUNT]; /* TX timeout timer */
+  struct wdog_s txtimeout[TXMBRINGSIZE]; /* TX timeout timer */
 #endif
   struct work_s rcvwork;            /* For deferring interrupt work to the wq 
*/
   struct work_s irqwork;            /* For deferring interrupt work to the wq 
*/
@@ -289,7 +296,7 @@ struct imxrt_driver_s
   const struct flexcan_config_s *config;
 
 #ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
-  struct txmbstats txmb[TXMBCOUNT];
+  struct txmbstats txmb[TXMBRINGSIZE];
 #endif
 };
 
@@ -1044,6 +1051,16 @@ static void imxrt_txdone(struct imxrt_driver_s *priv)
            */
 
           wd_cancel(&priv->txtimeout[txmb]);
+
+          /* Retire the deadline with the frame. Left behind it sits in the
+           * past forever, and the next expiry of any other mailbox's
+           * watchdog makes imxrt_txtimeout_work() abort whatever frame has
+           * since been loaded here.
+           */
+
+          priv->txmb[txmb].deadline.tv_sec  = 0;
+          priv->txmb[txmb].deadline.tv_usec = 0;
+
           struct mb_s *mb = flexcan_get_mb(priv, mbi);
           mb->cs.code = CAN_TXMB_INACTIVE;
 #endif
@@ -1207,9 +1224,11 @@ static void imxrt_txtimeout_work(void *arg)
   uint32_t mb_bit;
 
   struct timespec ts;
-  struct timeval *now = (struct timeval *)&ts;
+  struct timeval now;
+
   clock_systime_timespec(&ts);
-  now->tv_usec = ts.tv_nsec / 1000; /* timespec to timeval conversion */
+  now.tv_sec  = ts.tv_sec;
+  now.tv_usec = ts.tv_nsec / 1000;
 
   /* The watchdog timed out, yet we still check mailboxes in case the
    * transmit function transmitted a new frame
@@ -1217,25 +1236,43 @@ static void imxrt_txtimeout_work(void *arg)
 
   flags  = getreg32(priv->base + IMXRT_CAN_IFLAG1_OFFSET);
 
-  for (mbi = 0; mbi < TXMBCOUNT; mbi++)
+  for (mbi = 0; mbi < TXMBRINGSIZE; mbi++)
     {
-      if (priv->txmb[mbi].deadline.tv_sec != 0
-          && (now->tv_sec > priv->txmb[mbi].deadline.tv_sec
-          || now->tv_usec > priv->txmb[mbi].deadline.tv_usec))
+      struct timeval *deadline = &priv->txmb[mbi].deadline;
+      struct mb_s *mb;
+
+      /* imxrt_txdone() zeroes the deadline of a mailbox it has retired, so a
+       * non-zero deadline here means the mailbox still holds a frame.
+       */
+
+      if (deadline->tv_sec == 0 && deadline->tv_usec == 0)
+        {
+          continue;
+        }
+
+      if (now.tv_sec < deadline->tv_sec
+          || (now.tv_sec == deadline->tv_sec
+              && now.tv_usec <= deadline->tv_usec))
         {
-          NETDEV_TXTIMEOUTS(&priv->dev);
+          continue;
+        }
 
-          mb_bit = 1 << (RXMBCOUNT +  mbi);
+      NETDEV_TXTIMEOUTS(&priv->dev);
 
-          if (flags & mb_bit)
-            {
-              putreg32(mb_bit, priv->base + IMXRT_CAN_IFLAG1_OFFSET);
-            }
+      /* imxrt_transmit() stores the deadline of mailbox RXMBCOUNT + 1 + mbi
+       * in txmb[mbi]; the mailbox this loop aborts has to match.
+       */
+
+      mb_bit = 1 << (RXMBCOUNT + 1 + mbi);
 
-          struct mb_s *mb = flexcan_get_mb(priv, mbi + RXMBCOUNT);
-          mb->cs.code = CAN_TXMB_ABORT;
-          priv->txmb[mbi].pending = TX_ABORT;
+      if (flags & mb_bit)
+        {
+          putreg32(mb_bit, priv->base + IMXRT_CAN_IFLAG1_OFFSET);
         }
+
+      mb = flexcan_get_mb(priv, RXMBCOUNT + 1 + mbi);
+      mb->cs.code = CAN_TXMB_ABORT;
+      priv->txmb[mbi].pending = TX_ABORT;
     }
 }
 

Reply via email to