You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by "raiden00pl (via GitHub)" <gi...@apache.org> on 2023/01/28 09:59:36 UTC

[GitHub] [nuttx] raiden00pl commented on a diff in pull request #8250: Support for stm32h7 socket CAN error handling.

raiden00pl commented on code in PR #8250:
URL: https://github.com/apache/nuttx/pull/8250#discussion_r1089688571


##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -367,6 +379,7 @@ struct fdcan_driver_s
   struct work_s rxwork;
   struct work_s txcwork;
   struct work_s txdwork;
+  struct work_s irqwork;  /* For deferring interrupt work to the wq */
   struct work_s pollwork;

Review Comment:
   ```suggestion
     struct work_s pollwork;
   #ifdef CONFIG_NET_CAN_ERRORS
     struct work_s irqwork; 
   #endif
   ```
   We already have appropriate comment above the work_s section



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -1340,35 +1385,60 @@ static void fdcan_txdone_work(void *arg)
 static int fdcan_interrupt(int irq, void *context,
                            void *arg)
 {
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+
+  DEBUGASSERT(priv != NULL);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  uint32_t              pending = 0;
+
+  /* Get the set of pending interrupts. */
+
+  pending = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+
+  /* Check for any errors */
+
+  if ((pending & FDCAN_ANYERR_INTS) != 0)
+    {
+      /* Disable further CAN ERROR interrupts and schedule
+       * to perform the interrupt processing on the worker
+       * thread
+       */
+
+      fdcan_errint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, fdcan_error_work, priv, 0);
+    }
+#endif
+
   switch (irq)

Review Comment:
   Looks like the whole section can be now simplified like this:
   ```
   if (irq == priv->config->mb_irq[0])
     {
       fdcan_receive(priv);
     }
   else if (irq == priv->config->mb_irq[1])
     {
       fdcan_txdone(priv);
     }
   
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):
+
+            /* Format Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_FORM;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR):
+
+            /* Acknowledge Error */
+
+            errbits |= (CAN_ERR_PROT | CAN_ERR_ACK);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR):

Review Comment:
   ```suggestion
               break;
               
             case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR):
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -466,6 +479,18 @@ static int  fdcan_netdev_ioctl(struct net_driver_s *dev, int cmd,
 static int  fdcan_initialize(struct fdcan_driver_s *priv);
 static void fdcan_reset(struct fdcan_driver_s *priv);
 
+#ifdef CONFIG_NET_CAN_ERRORS
+
+/* CAN errors interrupt handling */
+
+static void fdcan_error_work(void *arg);
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr);
+
+static void fdcan_errint(struct fdcan_driver_s *priv, bool enable);
+#endif

Review Comment:
   ```suggestion
   static void fdcan_error_work(void *arg);
   static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
                           uint32_t psr);
   static void fdcan_errint(struct fdcan_driver_s *priv, bool enable);
   #endif
   ```



##########
include/nuttx/can.h:
##########
@@ -221,6 +221,8 @@
 #define CAN_ERR_BUSOFF           (1 << 6) /* Bit 6: Bus off */
 #define CAN_ERR_BUSERROR         (1 << 7) /* Bit 7: Bus error */
 #define CAN_ERR_RESTARTED        (1 << 8) /* Bit 8: Controller restarted */
+#define CAN_ERR_CNT              (1 << 9) /* Tx error counter / data[6] */
+                                          /* Rx error counter / data[7] */

Review Comment:
   ```suggestion
   #define CAN_ERR_CNT              (1 << 9) /* Tx error counter / data[6]
                                              * Rx error counter / data[7] 
                                              */
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);

Review Comment:
   Missing indentation



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);

Review Comment:
   ```suggestion
             data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);

Review Comment:
   ```suggestion
             data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;

Review Comment:
   ```suggestion
             data[1] |= CAN_ERR_CRTL_RX_WARNING;
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):

Review Comment:
   ```suggestion
               break;
               
             case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):
+
+            /* Format Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_FORM;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR):
+
+            /* Acknowledge Error */
+
+            errbits |= (CAN_ERR_PROT | CAN_ERR_ACK);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR):
+
+            /* Bit0 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT0;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR):
+
+            /* Bit1 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT1;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR):

Review Comment:
   ```suggestion
               break;
               
             case FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR):
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):
+
+            /* Format Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_FORM;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR):

Review Comment:
   ```suggestion
               break;
               
             case FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR):
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;

Review Comment:
   ```suggestion
             data[1] |= CAN_ERR_CRTL_TX_WARNING;
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):
+
+            /* Format Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_FORM;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR):
+
+            /* Acknowledge Error */
+
+            errbits |= (CAN_ERR_PROT | CAN_ERR_ACK);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR):
+
+            /* Bit0 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT0;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR):
+
+            /* Bit1 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT1;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR):
+
+            /* Receive CRC Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_NO_CHANGE):
+
+            /* No change (nothing has cleared the error) */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_UNSPEC;
+            break;
+          default:
+
+            /* no error  */
+
+            break;
+        }
+    }
+
+  if ((status & FDCAN_IR_PED) != 0)
+    {
+      /* Protocol Error in Data Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_DLEC_MASK)
+        {
+          case FDCAN_PSR_DLEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_DLEC(FDCAN_PSR_EC_FORM_ERROR):

Review Comment:
   ditto



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):
+
+            /* Format Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_FORM;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR):
+
+            /* Acknowledge Error */
+
+            errbits |= (CAN_ERR_PROT | CAN_ERR_ACK);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR):
+
+            /* Bit0 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT0;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR):
+
+            /* Bit1 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT1;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR):
+
+            /* Receive CRC Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_NO_CHANGE):
+
+            /* No change (nothing has cleared the error) */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_UNSPEC;
+            break;
+          default:

Review Comment:
   ```suggestion
               break;
               
             default:
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):
+
+            /* Format Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_FORM;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR):
+
+            /* Acknowledge Error */
+
+            errbits |= (CAN_ERR_PROT | CAN_ERR_ACK);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR):
+
+            /* Bit0 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT0;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR):

Review Comment:
   ```suggestion
               break;
               
             case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR):
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):
+
+            /* Format Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_FORM;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR):
+
+            /* Acknowledge Error */
+
+            errbits |= (CAN_ERR_PROT | CAN_ERR_ACK);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR):
+
+            /* Bit0 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT0;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR):
+
+            /* Bit1 Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_BIT1;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR):
+
+            /* Receive CRC Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL);
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_NO_CHANGE):

Review Comment:
   ```suggestion
               break;
               
             case FDCAN_PSR_LEC(FDCAN_PSR_EC_NO_CHANGE):
   ```



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128

Review Comment:
   This should be placed above in the `Pre-processor Definitions` section.



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96

Review Comment:
   ditto



##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -2524,3 +2598,444 @@ void arm_netinitialize(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(void *arg)
+{
+  struct fdcan_driver_s *priv    = (struct fdcan_driver_s *)arg;
+  uint32_t              pending = 0;
+  uint32_t              ir      = 0;
+  uint32_t              ie      = 0;
+  uint32_t              psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  ie = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  psr = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
+
+  pending = (ir);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_IE_PEAE | FDCAN_IE_PEDE);
+          putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+        }
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_CMNERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_TXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      putreg32(ie, priv->base + STM32_FDCAN_IE_OFFSET);
+
+      /* Clear the error indications */
+
+      putreg32(FDCAN_RXERR_INTS, priv->base + STM32_FDCAN_IR_OFFSET);
+    }
+
+  /* Report errors */
+
+  net_lock();
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS, psr);
+  net_unlock();
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - psr register content
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(struct fdcan_driver_s *priv, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+  uint32_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* Always fill in "static" error conditions, but set the signaling bit
+   * only if the condition has changed (see IRQ-Flags below)
+   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
+   */
+
+  errbits |= CAN_ERR_CNT;
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  data[6] = (uint8_t)((regval & FDCAN_ECR_TEC_MASK) >> FDCAN_ECR_TEC_SHIFT);
+  data[7] = (uint8_t)((regval & FDCAN_ECR_TREC_MASK) >>
+                      FDCAN_ECR_TREC_SHIFT);
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      if ((regval & FDCAN_ECR_RP) != 0)
+        {
+        data[1] |= (CAN_ERR_CRTL_RX_PASSIVE);
+        }
+
+#define CAN_ERROR_PASSIVE_THRESHOLD 128
+      if (data[6] >= CAN_ERROR_PASSIVE_THRESHOLD)
+        {
+        data[1] |= (CAN_ERR_CRTL_TX_PASSIVE);
+        }
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+#define CAN_ERROR_WARNING_THRESHOLD 96
+      if ((data[6] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+
+      if ((data[7] >= CAN_ERROR_WARNING_THRESHOLD))
+        {
+        data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_IR_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_ACT) == FDCAN_PSR_ACT)
+        {
+          /* transmit error */
+
+          data[2] |= CAN_ERR_PROT_TX;
+        }
+
+      switch (psr & FDCAN_PSR_LEC_MASK)
+        {
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR):
+
+            /* Stuff Error */
+
+            errbits |= CAN_ERR_PROT;
+            data[2] |= CAN_ERR_PROT_STUFF;
+            break;
+          case FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR):

Review Comment:
   Case logic should be followed by a single blank line.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org