You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/10/18 12:46:44 UTC

[GitHub] [incubator-nuttx] raiden00pl opened a new pull request, #7346: stm32f7: add SocketCAN support

raiden00pl opened a new pull request, #7346:
URL: https://github.com/apache/incubator-nuttx/pull/7346

   ## Summary
   
   - stm32f7/nucleo-144: add CAN1 pins selection
   - stm32f7/nucleo-144: refactor bringup logic
   - stm32f7: add SocketCAN support
   - nucleo-144/f722: add CAN and SocketCAN configurations
   
   ## Impact
   
   ## Testing
   nucleo-144/f722-cansock with canutils/cansend and canutils/candump tools
   


-- 
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


[GitHub] [incubator-nuttx] raiden00pl commented on a diff in pull request #7346: stm32f7: add SocketCAN support

Posted by GitBox <gi...@apache.org>.
raiden00pl commented on code in PR #7346:
URL: https://github.com/apache/incubator-nuttx/pull/7346#discussion_r999322245


##########
boards/arm/stm32f7/nucleo-144/configs/f722-can/Make.defs:
##########
@@ -0,0 +1,39 @@
+############################################################################
+# boards/arm/stm32f7/nucleo-144/configs/f746-nsh/Make.defs

Review Comment:
   done



-- 
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


[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a diff in pull request #7346: stm32f7: add SocketCAN support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7346:
URL: https://github.com/apache/incubator-nuttx/pull/7346#discussion_r998345864


##########
boards/arm/stm32f7/nucleo-144/configs/f722-cansock/Make.defs:
##########
@@ -0,0 +1,39 @@
+############################################################################
+# boards/arm/stm32f7/nucleo-144/configs/f746-nsh/Make.defs

Review Comment:
   ditto



##########
boards/arm/stm32f7/nucleo-144/configs/f722-can/Make.defs:
##########
@@ -0,0 +1,39 @@
+############################################################################
+# boards/arm/stm32f7/nucleo-144/configs/f746-nsh/Make.defs

Review Comment:
   wrong path



-- 
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


[GitHub] [incubator-nuttx] raiden00pl commented on a diff in pull request #7346: stm32f7: add SocketCAN support

Posted by GitBox <gi...@apache.org>.
raiden00pl commented on code in PR #7346:
URL: https://github.com/apache/incubator-nuttx/pull/7346#discussion_r999325346


##########
arch/arm/src/stm32f7/stm32_can_sock.c:
##########
@@ -0,0 +1,2538 @@
+/****************************************************************************
+ * arch/arm/src/stm32f7/stm32_can_sock.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/wqueue.h>
+#include <nuttx/can.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/can.h>
+#include <netpacket/can.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32_gpio.h"
+#include "stm32_rcc.h"
+#include "stm32_can.h"
+
+/* Ported form arch/arm/src/stm32/stm32_can_sock.c */
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Delays *******************************************************************/
+
+/* Time out for INAK bit */
+
+#define INAK_TIMEOUT 65535
+
+/* Bit timing ***************************************************************/
+
+#define CAN_BIT_QUANTA (CONFIG_STM32F7_CAN_TSEG1 + CONFIG_STM32F7_CAN_TSEG2 + 1)
+
+#ifndef CONFIG_DEBUG_CAN_INFO
+#  undef CONFIG_STM32F7_CAN_REGDEBUG
+#endif
+
+/* Pool configuration *******************************************************/
+
+#define POOL_SIZE  (1)
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#endif
+
+/* The low priority work queue is preferred.  If it is not enabled, LPWORK
+ * will be the same as HPWORK.
+ *
+ * NOTE:  However, the network should NEVER run on the high priority work
+ * queue!  That queue is intended only to service short back end interrupt
+ * processing that never suspends.  Suspending the high priority work queue
+ * may bring the system to its knees!
+ */
+
+#define CANWORK LPWORK
+
+/* CAN error interrupts */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+#  define STM32_CAN_ERRINT (CAN_IER_LECIE | CAN_IER_ERRIE | \
+                            CAN_IER_BOFIE | CAN_IER_EPVIE | \
+                            CAN_IER_EWGIE)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32_can_s
+{
+  uint8_t  port;     /* CAN port number (1 or 2) */
+  uint8_t  canrx[2]; /* CAN RX FIFO 0/1 IRQ number */
+  uint8_t  cantx;    /* CAN TX IRQ number */
+#ifdef CONFIG_NET_CAN_ERRORS
+  uint8_t  cansce;   /* CAN SCE IRQ number */
+#endif
+  uint8_t  filter;   /* Filter number */
+  uint32_t base;     /* Base address of the CAN control registers */
+  uint32_t fbase;    /* Base address of the CAN filter registers */
+  uint32_t baud;     /* Configured baud */
+
+  bool                bifup;  /* true:ifup false:ifdown */
+  struct net_driver_s dev;    /* Interface understood by the network */
+
+  struct work_s irqwork;  /* For deferring interrupt work to the wq */
+  struct work_s pollwork; /* For deferring poll work to the work wq */
+
+  /* A pointers to the list of TX/RX descriptors */
+
+  struct can_frame *txdesc;
+  struct can_frame *rxdesc;
+
+  /* TX/RX pool */
+
+  uint8_t tx_pool[sizeof(struct can_frame)*POOL_SIZE];
+  uint8_t rx_pool[sizeof(struct can_frame)*POOL_SIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* CAN Register access */
+
+static uint32_t stm32can_getreg(struct stm32_can_s *priv,
+                                int offset);
+static uint32_t stm32can_getfreg(struct stm32_can_s *priv,
+                                 int offset);
+static void stm32can_putreg(struct stm32_can_s *priv, int offset,
+                            uint32_t value);
+static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
+                             uint32_t value);
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_dumpctrlregs(struct stm32_can_s *priv,
+                                  const char *msg);
+static void stm32can_dumpmbregs(struct stm32_can_s *priv,
+                                const char *msg);
+static void stm32can_dumpfiltregs(struct stm32_can_s *priv,
+                                  const char *msg);
+#else
+#  define stm32can_dumpctrlregs(priv,msg)
+#  define stm32can_dumpmbregs(priv,msg)
+#  define stm32can_dumpfiltregs(priv,msg)
+#endif
+
+/* CAN interrupt enable functions */
+
+static void stm32can_rx0int(struct stm32_can_s *priv, bool enable);
+static void stm32can_rx1int(struct stm32_can_s *priv, bool enable);
+static void stm32can_txint(struct stm32_can_s *priv, bool enable);
+#ifdef CONFIG_NET_CAN_ERRORS
+static void stm32can_errint(struct stm32_can_s *priv, bool enable);
+#endif
+
+/* Common TX logic */
+
+static int stm32can_transmit(struct stm32_can_s *priv);
+static bool stm32can_txready(struct stm32_can_s *priv);
+static int  stm32can_txpoll(struct net_driver_s *dev);
+
+/* CAN RX interrupt handling */
+
+static int  stm32can_rxinterrupt_work(struct stm32_can_s *priv,
+                                      int rxmb);
+
+static void stm32can_rx0interrupt_work(void *arg);
+static void stm32can_rx1interrupt_work(void *arg);
+static int stm32can_rxinterrupt(struct stm32_can_s *priv, int rxmb);
+
+static int  stm32can_rx0interrupt(int irq, void *context, void *arg);
+static int  stm32can_rx1interrupt(int irq, void *context, void *arg);
+
+/* CAN TX interrupt handling */
+
+static int  stm32can_txinterrupt(int irq, void *context, void *arg);
+static void stm32can_txdone_work(void *arg);
+static void stm32can_txdone(struct stm32_can_s *priv);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/* CAN errors interrupt handling */
+
+static void stm32can_sceinterrupt_work(void *arg);
+static int  stm32can_sceinterrupt(int irq, void *context, void *arg);
+#endif
+
+/* Initialization */
+
+static int  stm32can_setup(struct stm32_can_s *priv);
+static void  stm32can_shutdown(struct stm32_can_s *priv);
+static void  stm32can_reset(struct stm32_can_s *priv);
+static int  stm32can_enterinitmode(struct stm32_can_s *priv);
+static int  stm32can_exitinitmode(struct stm32_can_s *priv);
+static int  stm32can_bittiming(struct stm32_can_s *priv);
+static int  stm32can_cellinit(struct stm32_can_s *priv);
+static int  stm32can_filterinit(struct stm32_can_s *priv);
+
+/* TX mailbox status */
+
+static bool stm32can_txmb0empty(uint32_t tsr_regval);
+static bool stm32can_txmb1empty(uint32_t tsr_regval);
+static bool stm32can_txmb2empty(uint32_t tsr_regval);
+
+/* NuttX callback functions */
+
+static int  stm32can_ifup(struct net_driver_s *dev);
+static int  stm32can_ifdown(struct net_driver_s *dev);
+
+static void stm32can_txavail_work(void *arg);
+static int  stm32can_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int  stm32can_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                  unsigned long arg);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN1
+
+static struct stm32_can_s g_can1priv =
+{
+  .port             = 1,
+  .canrx            =
+  {
+                      STM32_IRQ_CAN1RX0,
+                      STM32_IRQ_CAN1RX1,
+  },
+  .cantx            = STM32_IRQ_CAN1TX,
+#ifdef CONFIG_NET_CAN_ERRORS
+  .cansce           = STM32_IRQ_CAN1SCE,
+#endif
+  .filter           = 0,
+  .base             = STM32_CAN1_BASE,
+  .fbase            = STM32_CAN1_BASE,
+  .baud             = CONFIG_STM32F7_CAN1_BAUD,
+};
+
+#endif
+
+#ifdef CONFIG_STM32F7_CAN2
+
+static struct stm32_can_s g_can2priv =
+{
+  .port             = 2,
+  .canrx            =
+  {
+                      STM32_IRQ_CAN2RX0,
+                      STM32_IRQ_CAN2RX1,
+  },
+  .cantx            = STM32_IRQ_CAN2TX,
+#ifdef CONFIG_NET_CAN_ERRORS
+  .cansce           = STM32_IRQ_CAN2SCE,
+#endif
+  .filter           = CAN_NFILTERS / 2,
+  .base             = STM32_CAN2_BASE,
+  .fbase            = STM32_CAN1_BASE,
+  .baud             = CONFIG_STM32F7_CAN2_BAUD,
+};
+
+#endif
+
+#ifdef CONFIG_STM32F7_CAN3
+
+static struct stm32_can_s g_can3priv =
+{
+  .port             = 3,
+  .canrx            =
+  {
+    STM32_IRQ_CAN3RX0,
+    STM32_IRQ_CAN3RX1,
+  },
+  .cantx            = STM32_IRQ_CAN3TX,
+#ifdef CONFIG_NET_CAN_ERRORS
+  .cansce           = STM32_IRQ_CAN3SCE,
+#endif
+  .filter           = CAN_NFILTERS / 2,
+  .base             = STM32_CAN3_BASE,
+  .fbase            = STM32_CAN3_BASE,
+  .baud             = CONFIG_STM32F7_CAN3_BAUD,
+};
+
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32can_getreg
+ * Name: stm32can_getfreg
+ *
+ * Description:
+ *   Read the value of a CAN register or filter block register.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static uint32_t stm32can_vgetreg(uint32_t addr)
+{
+  static uint32_t prevaddr = 0;
+  static uint32_t preval   = 0;
+  static uint32_t count    = 0;
+
+  /* Read the value from the register */
+
+  uint32_t val = getreg32(addr);
+
+  /* Is this the same value that we read from the same register last time?
+   * Are we polling the register?  If so, suppress some of the output.
+   */
+
+  if (addr == prevaddr && val == preval)
+    {
+      if (count == 0xffffffff || ++count > 3)
+        {
+          if (count == 4)
+            {
+              ninfo("...\n");
+            }
+
+          return val;
+        }
+    }
+
+  /* No this is a new address or value */
+
+  else
+    {
+      /* Did we print "..." for the previous value? */
+
+      if (count > 3)
+        {
+          /* Yes.. then show how many times the value repeated */
+
+          ninfo("[repeats %" PRIu32 " more times]\n", count - 3);
+        }
+
+      /* Save the new address, value, and count */
+
+      prevaddr = addr;
+      preval   = val;
+      count    = 1;
+    }
+
+  /* Show the register value read */
+
+  ninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
+  return val;
+}
+
+static uint32_t stm32can_getreg(struct stm32_can_s *priv, int offset)
+{
+  return stm32can_vgetreg(priv->base + offset);
+}
+
+static uint32_t stm32can_getfreg(struct stm32_can_s *priv, int offset)
+{
+  return stm32can_vgetreg(priv->fbase + offset);
+}
+
+#else
+static uint32_t stm32can_getreg(struct stm32_can_s *priv, int offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+static uint32_t stm32can_getfreg(struct stm32_can_s *priv, int offset)
+{
+  return getreg32(priv->fbase + offset);
+}
+
+#endif
+
+/****************************************************************************
+ * Name: stm32can_putreg
+ * Name: stm32can_putfreg
+ *
+ * Description:
+ *   Set the value of a CAN register or filter block register.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_vputreg(uint32_t addr, uint32_t value)
+{
+  /* Show the register value being written */
+
+  ninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
+
+  /* Write the value */
+
+  putreg32(value, addr);
+}
+
+static void stm32can_putreg(struct stm32_can_s *priv, int offset,
+                            uint32_t value)
+{
+  stm32can_vputreg(priv->base + offset, value);
+}
+
+static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
+                             uint32_t value)
+{
+  stm32can_vputreg(priv->fbase + offset, value);
+}
+
+#else
+static void stm32can_putreg(struct stm32_can_s *priv, int offset,
+                            uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
+                             uint32_t value)
+{
+  putreg32(value, priv->fbase + offset);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_dumpctrlregs
+ *
+ * Description:
+ *   Dump the contents of all CAN control registers
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *   msg  - message
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_dumpctrlregs(struct stm32_can_s *priv,
+                                  const char *msg)
+{
+  if (msg)
+    {
+      ninfo("Control Registers: %s\n", msg);
+    }
+  else
+    {
+      ninfo("Control Registers:\n");
+    }
+
+  /* CAN control and status registers */
+
+  ninfo("  MCR: %08" PRIx32 "   MSR: %08" PRIx32 "   TSR: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_MCR_OFFSET),
+          getreg32(priv->base + STM32_CAN_MSR_OFFSET),
+          getreg32(priv->base + STM32_CAN_TSR_OFFSET));
+
+  ninfo(" RF0R: %08" PRIx32 "  RF1R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_RF0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RF1R_OFFSET));
+
+  ninfo("  IER: %08" PRIx32 "   ESR: %08" PRIx32 "   BTR: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_IER_OFFSET),
+          getreg32(priv->base + STM32_CAN_ESR_OFFSET),
+          getreg32(priv->base + STM32_CAN_BTR_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_dumpmbregs
+ *
+ * Description:
+ *   Dump the contents of all CAN mailbox registers
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *   msg  - message
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_dumpmbregs(struct stm32_can_s *priv,
+                                const char *msg)
+{
+  if (msg)
+    {
+      ninfo("Mailbox Registers: %s\n", msg);
+    }
+  else
+    {
+      ninfo("Mailbox Registers:\n");
+    }
+
+  /* CAN mailbox registers (3 TX and 2 RX) */
+
+  ninfo(" TI0R: %08" PRIx32 " TDT0R: %08" PRIx32 " TDL0R: %08"
+          PRIx32 " TDH0R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_TI0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDT0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDL0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDH0R_OFFSET));
+
+  ninfo(" TI1R: %08" PRIx32 " TDT1R: %08" PRIx32 " TDL1R: %08"
+          PRIx32 " TDH1R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_TI1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDT1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDL1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDH1R_OFFSET));
+
+  ninfo(" TI2R: %08" PRIx32 " TDT2R: %08" PRIx32 " TDL2R: %08"
+          PRIx32 " TDH2R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_TI2R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDT2R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDL2R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDH2R_OFFSET));
+
+  ninfo(" RI0R: %08" PRIx32 " RDT0R: %08" PRIx32 " RDL0R: %08"
+          PRIx32 " RDH0R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_RI0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDT0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDL0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDH0R_OFFSET));
+
+  ninfo(" RI1R: %08" PRIx32 " RDT1R: %08" PRIx32 " RDL1R: %08"
+          PRIx32 " RDH1R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_RI1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDT1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDL1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDH1R_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_dumpfiltregs
+ *
+ * Description:
+ *   Dump the contents of all CAN filter registers
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *   msg  - message
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_dumpfiltregs(struct stm32_can_s *priv,
+                                  const char *msg)
+{
+  int i;
+
+  if (msg)
+    {
+      ninfo("Filter Registers: %s\n", msg);
+    }
+  else
+    {
+      ninfo("Filter Registers:\n");
+    }
+
+  ninfo(" FMR: %08" PRIx32 "   FM1R: %08" PRIx32 "  FS1R: %08"
+          PRIx32 " FFA1R: %08" PRIx32 "  FA1R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_FMR_OFFSET),
+          getreg32(priv->base + STM32_CAN_FM1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_FS1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_FFA1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_FA1R_OFFSET));
+
+  for (i = 0; i < CAN_NFILTERS; i++)
+    {
+      ninfo(" F%dR1: %08" PRIx32 " F%dR2: %08" PRIx32 "\n",
+              i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 1)),
+              i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 2)));
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_rx0int
+ *
+ * Description:
+ *   Call to enable or disable RX0 interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_rx0int(struct stm32_can_s *priv, bool enable)
+{
+  uint32_t regval = 0;
+
+  ninfo("CAN%" PRIu8 "RX0 enable: %d\n", priv->port, enable);
+
+  /* Enable/disable the FIFO 0 message pending interrupt */
+
+  regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  if (enable)
+    {
+      regval |= CAN_IER_FMPIE0;
+    }
+  else
+    {
+      regval &= ~CAN_IER_FMPIE0;
+    }
+
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Name: stm32can_rx1int
+ *
+ * Description:
+ *   Call to enable or disable RX1 interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_rx1int(struct stm32_can_s *priv, bool enable)
+{
+  uint32_t regval = 0;
+
+  ninfo("CAN%" PRIu8 "RX1 enable: %d\n", priv->port, enable);
+
+  /* Enable/disable the FIFO 1 message pending interrupt */
+
+  regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  if (enable)
+    {
+      regval |= CAN_IER_FMPIE1;
+    }
+  else
+    {
+      regval &= ~CAN_IER_FMPIE1;
+    }
+
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Name: stm32can_txint
+ *
+ * Description:
+ *   Call to enable or disable TX interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_txint(struct stm32_can_s *priv, bool enable)
+{
+  uint32_t regval = 0;
+
+  ninfo("CAN%" PRIu8 " txint enable: %d\n", priv->port, enable);
+
+  /* Enable/disable the transmit mailbox interrupt */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  if (enable)
+    {
+      regval |= CAN_IER_TMEIE;
+    }
+  else
+    {
+      regval &= ~CAN_IER_TMEIE;
+    }
+
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+}
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: stm32can_txint
+ *
+ * Description:
+ *   Call to enable or disable CAN SCE interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_errint(struct stm32_can_s *priv, bool enable)
+{
+  uint32_t regval = 0;
+
+  /* Enable/disable the transmit mailbox interrupt */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  if (enable)
+    {
+      regval |= STM32_CAN_ERRINT;
+    }
+  else
+    {
+      regval &= ~STM32F7_CAN_ERRINT;
+    }
+
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+}
+#endif
+
+/****************************************************************************
+ * Function: stm32can_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int stm32can_ifup(struct net_driver_s *dev)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+
+  /* Setup CAN */
+
+  stm32can_setup(priv);
+
+  /* Enable interrupts */
+
+  stm32can_rx0int(priv, true);
+  stm32can_rx1int(priv, true);
+  stm32can_txint(priv, true);
+#ifdef CONFIG_NET_CAN_ERRORS
+  stm32can_errint(priv, true);
+#endif
+
+  /* Enable the interrupts at the NVIC */
+
+  up_enable_irq(priv->canrx[0]);
+  up_enable_irq(priv->canrx[1]);
+  up_enable_irq(priv->cantx);
+#ifdef CONFIG_NET_CAN_ERRORS
+  up_enable_irq(priv->cansce);
+#endif
+
+  priv->bifup = true;
+
+  priv->txdesc = (struct can_frame *)priv->tx_pool;
+  priv->rxdesc = (struct can_frame *)priv->rx_pool;
+
+  priv->dev.d_buf = (uint8_t *)priv->txdesc;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: stm32can_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int stm32can_ifdown(struct net_driver_s *dev)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+
+  /* Disable CAN interrupts */
+
+  stm32can_shutdown(priv);
+
+  /* Reset CAN */
+
+  stm32can_reset(priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32can_txready
+ *
+ * Description:
+ *   Return true if the CAN hardware can accept another TX message.
+ *
+ ****************************************************************************/
+
+static bool stm32can_txready(struct stm32_can_s *priv)
+{
+  uint32_t regval;
+
+  /* Return true if any mailbox is available */
+
+  regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
+  ninfo("CAN%" PRIu8 " TSR: %08" PRIx32 "\n", priv->port, regval);
+
+  return (stm32can_txmb0empty(regval) || stm32can_txmb1empty(regval) ||
+          stm32can_txmb2empty(regval));
+}
+
+/****************************************************************************
+ * Name: stm32can_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the txdone interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int stm32can_transmit(struct stm32_can_s *priv)
+{
+  struct can_frame *frame = (struct can_frame *)priv->dev.d_buf;
+  uint8_t          *ptr;
+  uint32_t          regval;
+  uint32_t          tmp;
+  int               dlc;
+  int               txmb;
+
+  ninfo("CAN%" PRIu8 " ID: %" PRIu32 " DLC: %" PRIu8 "\n",
+        priv->port, (uint32_t)frame->can_id, frame->can_dlc);
+
+  /* Select one empty transmit mailbox */
+
+  regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
+  if (stm32can_txmb0empty(regval))
+    {
+      txmb = 0;
+    }
+  else if (stm32can_txmb1empty(regval))
+    {
+      txmb = 1;
+    }
+  else if (stm32can_txmb2empty(regval))
+    {
+      txmb = 2;
+    }
+  else
+    {
+      canerr("ERROR: No available mailbox\n");
+      return -EBUSY;
+    }
+
+  /* Clear TXRQ, RTR, IDE, EXID, and STID fields */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb));
+  regval &= ~(CAN_TIR_TXRQ | CAN_TIR_RTR | CAN_TIR_IDE |
+              CAN_TIR_EXID_MASK | CAN_TIR_STID_MASK);
+  stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
+
+  /* Set up the ID, standard 11-bit or extended 29-bit. */
+
+#ifdef CONFIG_NET_CAN_EXTID
+  regval &= ~CAN_TIR_EXID_MASK;
+  if (frame->can_id & CAN_EFF_FLAG)
+    {
+      DEBUGASSERT(frame->can_id < (1 << 29));
+      regval |= (frame->can_id << CAN_TIR_EXID_SHIFT) | CAN_TIR_IDE;
+    }
+  else
+    {
+      DEBUGASSERT(frame->can_id < (1 << 11));
+      regval |= frame->can_id << CAN_TIR_STID_SHIFT;
+    }
+
+#else
+  regval |= (((uint32_t) frame->can_id << CAN_TIR_STID_SHIFT) &
+               CAN_TIR_STID_MASK);
+
+#endif
+
+#ifdef CONFIG_CAN_USE_RTR
+  regval |= ((frame->can_id & CAN_RTR_FLAG) ? CAN_TIR_RTR : 0);
+#endif
+
+  stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
+
+  /* Set up the DLC */
+
+  dlc     = frame->can_dlc;
+  regval  = stm32can_getreg(priv, STM32_CAN_TDTR_OFFSET(txmb));
+  regval &= ~(CAN_TDTR_DLC_MASK | CAN_TDTR_TGT);
+  regval |= (uint32_t)dlc << CAN_TDTR_DLC_SHIFT;
+  stm32can_putreg(priv, STM32_CAN_TDTR_OFFSET(txmb), regval);
+
+  /* Set up the data fields */
+
+  ptr    = frame->data;
+  regval = 0;
+
+  if (dlc > 0)
+    {
+      tmp    = (uint32_t)*ptr++;
+      regval = tmp << CAN_TDLR_DATA0_SHIFT;
+
+      if (dlc > 1)
+        {
+          tmp     = (uint32_t)*ptr++;
+          regval |= tmp << CAN_TDLR_DATA1_SHIFT;
+
+          if (dlc > 2)
+            {
+              tmp     = (uint32_t)*ptr++;
+              regval |= tmp << CAN_TDLR_DATA2_SHIFT;
+
+              if (dlc > 3)
+                {
+                  tmp     = (uint32_t)*ptr++;
+                  regval |= tmp << CAN_TDLR_DATA3_SHIFT;
+                }
+            }
+        }
+    }
+
+  stm32can_putreg(priv, STM32_CAN_TDLR_OFFSET(txmb), regval);
+
+  regval = 0;
+  if (dlc > 4)
+    {
+      tmp    = (uint32_t)*ptr++;
+      regval = tmp << CAN_TDHR_DATA4_SHIFT;
+
+      if (dlc > 5)
+        {
+          tmp     = (uint32_t)*ptr++;
+          regval |= tmp << CAN_TDHR_DATA5_SHIFT;
+
+          if (dlc > 6)
+            {
+              tmp     = (uint32_t)*ptr++;
+              regval |= tmp << CAN_TDHR_DATA6_SHIFT;
+
+              if (dlc > 7)
+                {
+                  tmp     = (uint32_t)*ptr++;
+                  regval |= tmp << CAN_TDHR_DATA7_SHIFT;
+                }
+            }
+        }
+    }
+
+  stm32can_putreg(priv, STM32_CAN_TDHR_OFFSET(txmb), regval);
+
+  /* Enable the transmit mailbox empty interrupt (may already be enabled) */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  regval |= CAN_IER_TMEIE;
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+
+  /* Request transmission */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb));
+  regval |= CAN_TIR_TXRQ;  /* Transmit Mailbox Request */
+  stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
+
+  stm32can_dumpmbregs(priv, "After send");
+  return OK;
+}
+
+/****************************************************************************
+ * Function: stm32can_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int stm32can_txpoll(struct net_driver_s *dev)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      if (!devif_loopback(&priv->dev))
+        {
+          stm32can_txdone(priv);
+
+          /* Send the packet */
+
+          stm32can_transmit(priv);
+
+          /* Check if there is room in the device to hold another packet. If
+           * not, return a non-zero value to terminate the poll.
+           */
+
+          if (stm32can_txready(priv) == false)
+            {
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: stm32can_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void stm32can_txavail_work(void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->bifup)
+    {
+      /* Check if there is room in the hardware to hold another outgoing
+       * packet.
+       */
+
+      if (stm32can_txready(priv))
+        {
+          /* No, there is space for another transfer.  Poll the network for
+           * new XMIT data.
+           */
+
+          devif_poll(&priv->dev, stm32can_txpoll);
+        }
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: stm32can_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int stm32can_txavail(struct net_driver_s *dev)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      stm32can_txavail_work(priv);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: stm32can_ioctl
+ *
+ * Description:
+ *   PHY ioctl command handler
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   cmd  - ioctl command
+ *   arg  - Argument accompanying the command
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int stm32can_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                 unsigned long arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+  int                 ret  = OK;
+
+  switch (cmd)
+    {
+      /* TODO */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Name: stm32can_rxinterrupt_work
+ *
+ * Description:
+ *   CAN RX FIFO 0/1 interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *   rxmb - The RX mailbox number.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_rxinterrupt_work(struct stm32_can_s *priv, int rxmb)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rxdesc;
+  uint32_t          regval;
+  int               ret   = OK;
+
+  DEBUGASSERT(priv != NULL);
+
+  if (rxmb == 0)
+    {
+      stm32can_dumpmbregs(priv, "RX0 interrupt");
+    }
+  else
+    {
+      stm32can_dumpmbregs(priv, "RX1 interrupt");
+    }
+
+  /* Get the CAN identifier. */
+
+  regval = stm32can_getreg(priv, STM32_CAN_RIR_OFFSET(rxmb));
+
+#ifdef CONFIG_NET_CAN_EXTID
+  if ((regval & CAN_RIR_IDE) != 0)
+    {
+      frame->can_id  = (regval & CAN_RIR_EXID_MASK) >> CAN_RIR_EXID_SHIFT;
+      frame->can_id &= ~CAN_EFF_FLAG;
+    }
+  else
+    {
+      frame->can_id  = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT;
+      frame->can_id |= CAN_EFF_FLAG;
+    }
+#else
+  if ((regval & CAN_RIR_IDE) != 0)
+    {
+      nerr("ERROR: Received message with extended identifier.  Dropped\n");
+      ret = -ENOSYS;
+      goto errout;
+    }
+
+  frame->can_id = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT;
+#endif
+
+  /* Extract the RTR bit */
+
+  if ((regval & CAN_RIR_RTR) != 0)
+    {
+      frame->can_id |= CAN_RTR_FLAG;
+    }
+
+  /* Get the DLC */
+
+  regval        = stm32can_getreg(priv, STM32_CAN_RDTR_OFFSET(rxmb));
+  frame->can_dlc = (regval & CAN_RDTR_DLC_MASK) >> CAN_RDTR_DLC_SHIFT;
+
+  /* Save the message data */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_RDLR_OFFSET(rxmb));
+  frame->data[0] = (regval & CAN_RDLR_DATA0_MASK) >> CAN_RDLR_DATA0_SHIFT;
+  frame->data[1] = (regval & CAN_RDLR_DATA1_MASK) >> CAN_RDLR_DATA1_SHIFT;
+  frame->data[2] = (regval & CAN_RDLR_DATA2_MASK) >> CAN_RDLR_DATA2_SHIFT;
+  frame->data[3] = (regval & CAN_RDLR_DATA3_MASK) >> CAN_RDLR_DATA3_SHIFT;
+
+  regval  = stm32can_getreg(priv, STM32_CAN_RDHR_OFFSET(rxmb));
+  frame->data[4] = (regval & CAN_RDHR_DATA4_MASK) >> CAN_RDHR_DATA4_SHIFT;
+  frame->data[5] = (regval & CAN_RDHR_DATA5_MASK) >> CAN_RDHR_DATA5_SHIFT;
+  frame->data[6] = (regval & CAN_RDHR_DATA6_MASK) >> CAN_RDHR_DATA6_SHIFT;
+  frame->data[7] = (regval & CAN_RDHR_DATA7_MASK) >> CAN_RDHR_DATA7_SHIFT;
+
+  /* Copy the buffer pointer to priv->dev..  Set amount of data
+   * in priv->dev.d_len
+   */
+
+  priv->dev.d_len = sizeof(struct can_frame);
+  priv->dev.d_buf = (uint8_t *)frame;
+
+  /* Send to socket interface */
+
+  NETDEV_RXPACKETS(&priv->dev);
+
+  can_input(&priv->dev);
+
+  /* Point the packet buffer back to the next Tx buffer that will be
+   * used during the next write.  If the write queue is full, then
+   * this will point at an active buffer, which must not be written
+   * to.  This is OK because devif_poll won't be called unless the
+   * queue is not full.
+   */
+
+  priv->dev.d_buf = (uint8_t *)priv->txdesc;
+
+  /* Release the FIFO */
+
+#ifndef CONFIG_NET_CAN_EXTID
+errout:
+#endif
+  regval  = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb));
+  regval |= CAN_RFR_RFOM;
+  stm32can_putreg(priv, STM32_CAN_RFR_OFFSET(rxmb), regval);
+
+  /* Re-enable CAN RX interrupts */
+
+  if (rxmb == 0)
+    {
+      stm32can_rx0int(priv, true);
+    }
+  else if (rxmb == 1)
+    {
+      stm32can_rx1int(priv, true);
+    }
+  else
+    {
+      DEBUGPANIC();
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32can_rx0interrupt_work
+ ****************************************************************************/
+
+static void stm32can_rx0interrupt_work(void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  stm32can_rxinterrupt_work(priv, 0);
+}
+
+/****************************************************************************
+ * Name: stm32can_rx1interrupt_work
+ ****************************************************************************/
+
+static void stm32can_rx1interrupt_work(void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  stm32can_rxinterrupt_work(priv, 1);
+}
+
+/****************************************************************************
+ * Name: stm32can_rxinterrupt
+ *
+ * Description:
+ *   CAN RX FIFO common interrupt handler
+ *
+ ****************************************************************************/
+
+static int stm32can_rxinterrupt(struct stm32_can_s *priv, int rxmb)
+{
+  uint32_t regval   = 0;
+  int      npending = 0;
+
+  /* Verify that a message is pending in the FIFO */
+
+  regval   = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb));
+  npending = (regval & CAN_RFR_FMP_MASK) >> CAN_RFR_FMP_SHIFT;
+  if (npending < 1)
+    {
+      nwarn("WARNING: No messages pending\n");
+      return OK;
+    }
+
+  /* Disable further CAN RX interrupts and schedule to perform the
+   * interrupt processing on the worker thread
+   */
+
+  if (rxmb == 0)
+    {
+      stm32can_rx0int(priv, false);
+      work_queue(CANWORK, &priv->irqwork,
+                 stm32can_rx0interrupt_work, priv, 0);
+    }
+  else if (rxmb == 1)
+    {
+      stm32can_rx1int(priv, false);
+      work_queue(CANWORK, &priv->irqwork,
+                 stm32can_rx1interrupt_work, priv, 0);
+    }
+  else
+    {
+      DEBUGPANIC();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32can_rx0interrupt
+ *
+ * Description:
+ *   CAN RX FIFO 0 interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_rx0interrupt(int irq, void *context, void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  return stm32can_rxinterrupt(priv, 0);
+}
+
+/****************************************************************************
+ * Name: stm32can_rx1interrupt
+ *
+ * Description:
+ *   CAN RX FIFO 1 interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_rx1interrupt(int irq, void *context, void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  return stm32can_rxinterrupt(priv, 1);
+}
+
+/****************************************************************************
+ * Name: stm32can_txinterrupt
+ *
+ * Description:
+ *   CAN TX mailbox complete interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_txinterrupt(int irq, void *context, void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Get the transmit status */
+
+  regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
+
+  /* Check for RQCP0: Request completed mailbox 0 */
+
+  if ((regval & CAN_TSR_RQCP0) != 0)
+    {
+      /* Writing '1' to RCP0 clears RCP0 and all the status bits (TXOK0,
+       * ALST0 and TERR0) for Mailbox 0.
+       */
+
+      stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP0);
+
+      /* Tell the upper half that the transfer is finished. */
+
+      /* Disable further TX CAN interrupts. here can be no race
+       * condition here.
+       */
+
+      stm32can_txint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0);
+    }
+
+  /* Check for RQCP1: Request completed mailbox 1 */
+
+  if ((regval & CAN_TSR_RQCP1) != 0)
+    {
+      /* Writing '1' to RCP1 clears RCP1 and all the status bits (TXOK1,
+       * ALST1 and TERR1) for Mailbox 1.
+       */
+
+      stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP1);
+
+      /* Tell the upper half that the transfer is finished. */
+
+      /* Disable further TX CAN interrupts. here can be no race
+       * condition here.
+       */
+
+      stm32can_txint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0);
+    }
+
+  /* Check for RQCP2: Request completed mailbox 2 */
+
+  if ((regval & CAN_TSR_RQCP2) != 0)
+    {
+      /* Writing '1' to RCP2 clears RCP2 and all the status bits (TXOK2,
+       * ALST2 and TERR2) for Mailbox 2.
+       */
+
+      stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP2);
+
+      /* Disable further TX CAN interrupts. here can be no race
+       * condition here.
+       */
+
+      stm32can_txint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32can_txdone_work
+ ****************************************************************************/
+
+static void stm32can_txdone_work(void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+
+  stm32can_txdone(priv);
+
+  /* There should be space for a new TX in any event.  Poll the network for
+   * new XMIT data
+   */
+
+  net_lock();
+  devif_poll(&priv->dev, stm32can_txpoll);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: stm32can_txdone
+ ****************************************************************************/
+
+static void stm32can_txdone(struct stm32_can_s *priv)
+{
+  stm32can_txint(priv, true);
+
+  NETDEV_TXDONE(&priv->dev);
+}
+
+#ifdef CONFIG_NET_CAN_ERRORS
+
+/****************************************************************************
+ * Name: stm32can_sceinterrupt_work
+ *
+ * Description:
+ *   CAN status change interrupt work
+ *
+ * Input Parameters:
+ *   arg  - reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_sceinterrupt_work(void *arg)
+{
+  struct stm32_can_s *priv    = (struct stm32_can_s *)arg;
+  struct can_frame   *frame   = (struct can_frame *)priv->rxdesc;
+  uint32_t            regval  = 0;
+  uint16_t            errbits = 0;
+  uint8_t             data[CAN_ERR_DLC];
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Check Error Interrupt flag */
+
+  regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET);
+  if (regval & CAN_MSR_ERRI)
+    {
+      /* Encode error bits */
+
+      errbits = 0;
+      memset(data, 0, sizeof(data));
+
+      /* Get Error statur register */
+
+      regval = stm32can_getreg(priv, STM32_CAN_ESR_OFFSET);
+
+      if (regval & CAN_ESR_EWGF)
+        {
+          /* Error warning flag */
+
+          data[1] |= (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING);
+          errbits |= CAN_ERR_CRTL;
+        }
+
+      if (regval & CAN_ESR_EPVF)
+        {
+          /* Error passive flag */
+
+          data[1] |= (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE);
+          errbits |= CAN_ERR_CRTL;
+        }
+
+      if (regval & CAN_ESR_BOFF)
+        {
+          /* Bus-off flag */
+
+          errbits |= CAN_ERR_BUSOFF;
+        }
+
+      /* Last error code */
+
+      if (regval & CAN_ESR_LEC_MASK)
+        {
+          if (regval & CAN_ESR_STUFFERROR)
+            {
+              /* Stuff Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_STUFF;
+            }
+          else if (regval & CAN_ESR_FORMERROR)
+            {
+              /* Format Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_FORM;
+            }
+          else if (regval & CAN_ESR_ACKERROR)
+            {
+              /* Acknowledge Error */
+
+              errbits |= CAN_ERR_ACK;
+            }
+          else if (regval & CAN_ESR_BRECERROR)
+            {
+              /* Bit recessive Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_BIT1;
+            }
+          else if (regval & CAN_ESR_BDOMERROR)
+            {
+              /* Bit dominant Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_BIT0;
+            }
+          else if (regval & CAN_ESR_CRCERRPR)
+            {
+              /* Receive CRC Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[3] |= CAN_ERR_PROT_LOC_CRCSEQ;
+            }
+        }
+
+      /* Get transmit status register */
+
+      regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
+
+      if (regval & CAN_TSR_ALST0 || regval & CAN_TSR_ALST1 ||
+          regval & CAN_TSR_ALST2)
+        {
+          /* Lost arbitration Error */
+
+          errbits |= CAN_ERR_LOSTARB;
+        }
+
+      /* Clear TSR register */
+
+      stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, regval);
+
+      /* Clear ERRI flag */
+
+      stm32can_putreg(priv, STM32_CAN_MSR_OFFSET, CAN_MSR_ERRI);
+    }
+
+  /* Report a CAN error */
+
+  if (errbits != 0)
+    {
+      canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
+
+      /* Copy frame */
+
+      frame->can_id  = errbits;
+      frame->can_dlc = CAN_ERR_DLC;
+
+      memcpy(frame->data, data, CAN_ERR_DLC);
+
+      /* Copy the buffer pointer to priv->dev..  Set amount of data
+       * in priv->dev.d_len
+       */
+
+      priv->dev.d_len = sizeof(struct can_frame);
+      priv->dev.d_buf = (uint8_t *)frame;
+
+      /* Send to socket interface */
+
+      NETDEV_ERRORS(&priv->dev);
+
+      can_input(&priv->dev);
+
+      /* Point the packet buffer back to the next Tx buffer that will be
+       * used during the next write.  If the write queue is full, then
+       * this will point at an active buffer, which must not be written
+       * to.  This is OK because devif_poll won't be called unless the
+       * queue is not full.
+       */
+
+      priv->dev.d_buf = (uint8_t *)priv->txdesc;
+    }
+
+  /* Re-enable CAN SCE interrupts */
+
+  stm32can_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: stm32can_sceinterrupt
+ *
+ * Description:
+ *   CAN status change interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_sceinterrupt(int irq, void *context, void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+
+  /* Disable further CAN SCE interrupts and schedule to perform the
+   * interrupt processing on the worker thread
+   */
+
+  stm32can_errint(priv, false);
+  work_queue(CANWORK, &priv->irqwork,
+             stm32can_sceinterrupt_work, priv, 0);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_bittiming
+ *
+ * Description:
+ *   Set the CAN bit timing register (BTR) based on the configured BAUD.
+ *
+ * "The bit timing logic monitors the serial bus-line and performs sampling
+ *  and adjustment of the sample point by synchronizing on the start-bit edge
+ *  and resynchronizing on the following edges.
+ *
+ * "Its operation may be explained simply by splitting nominal bit time into
+ *  three segments as follows:
+ *
+ * 1. "Synchronization segment (SYNC_SEG): a bit change is expected to occur
+ *     within this time segment. It has a fixed length of one time quantum
+ *     (1 x tCAN).
+ * 2. "Bit segment 1 (BS1): defines the location of the sample point. It
+ *     includes the PROP_SEG and PHASE_SEG1 of the CAN standard. Its duration
+ *     is programmable between 1 and 16 time quanta but may be automatically
+ *     lengthened to compensate for positive phase drifts due to differences
+ *     in the frequency of the various nodes of the network.
+ * 3. "Bit segment 2 (BS2): defines the location of the transmit point. It
+ *     represents the PHASE_SEG2 of the CAN standard. Its duration is
+ *     programmable between 1 and 8 time quanta but may also be automatically
+ *     shortened to compensate for negative phase drifts."
+ *
+ * Pictorially:
+ *
+ *  |<----------------- NOMINAL BIT TIME ----------------->|
+ *  |<- SYNC_SEG ->|<------ BS1 ------>|<------ BS2 ------>|
+ *  |<---- Tq ---->|<----- Tbs1 ------>|<----- Tbs2 ------>|
+ *
+ * Where
+ *   Tbs1 is the duration of the BS1 segment
+ *   Tbs2 is the duration of the BS2 segment
+ *   Tq is the "Time Quantum"
+ *
+ * Relationships:
+ *
+ *   baud = 1 / bit_time
+ *   bit_time = Tq + Tbs1 + Tbs2
+ *   Tbs1 = Tq * ts1
+ *   Tbs2 = Tq * ts2
+ *   Tq = brp * Tpclk1
+ *   baud = Fpclk1 / (brp  * (1 + ts1 + ts2))
+ *
+ * Where:
+ *   Tpclk1 is the period of the APB1 clock (PCLK1).
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_bittiming(struct stm32_can_s *priv)
+{
+  uint32_t tmp;
+  uint32_t brp;
+  uint32_t ts1;
+  uint32_t ts2;
+
+  ninfo("CAN%" PRIu8 " PCLK1: %lu baud: %" PRIu32 "\n",
+          priv->port, (unsigned long) STM32_PCLK1_FREQUENCY, priv->baud);
+
+  /* Try to get CAN_BIT_QUANTA quanta in one bit_time.
+   *
+   *   bit_time = Tq*(ts1 + ts2 + 1)
+   *   nquanta  = bit_time / Tq
+   *   nquanta  = (ts1 + ts2 + 1)
+   *
+   *   bit_time = brp * Tpclk1 * (ts1 + ts2 + 1)
+   *   nquanta  = bit_time / brp / Tpclk1
+   *            = PCLK1 / baud / brp
+   *   brp      = PCLK1 / baud / nquanta;
+   *
+   * Example:
+   *   PCLK1 = 42,000,000 baud = 1,000,000 nquanta = 14 : brp = 3
+   *   PCLK1 = 42,000,000 baud =   700,000 nquanta = 14 : brp = 4
+   */
+
+  tmp = STM32_PCLK1_FREQUENCY / priv->baud;
+  if (tmp < CAN_BIT_QUANTA)
+    {
+      /* At the smallest brp value (1), there are already too few bit times
+       * (PCLCK1 / baud) to meet our goal.  brp must be one and we need
+       * make some reasonable guesses about ts1 and ts2.
+       */
+
+      brp = 1;
+
+      /* In this case, we have to guess a good value for ts1 and ts2 */
+
+      ts1 = (tmp - 1) >> 1;
+      ts2 = tmp - ts1 - 1;
+      if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX)
+        {
+          ts1--;
+          ts2++;
+        }
+    }
+
+  /* Otherwise, nquanta is CAN_BIT_QUANTA, ts1 is CONFIG_STM32F7_CAN_TSEG1,
+   * ts2 is CONFIG_STM32F7_CAN_TSEG2 and we calculate brp to achieve
+   * CAN_BIT_QUANTA quanta in the bit time
+   */
+
+  else
+    {
+      ts1 = CONFIG_STM32F7_CAN_TSEG1;
+      ts2 = CONFIG_STM32F7_CAN_TSEG2;
+      brp = (tmp + (CAN_BIT_QUANTA / 2)) / CAN_BIT_QUANTA;
+      DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX);
+    }
+
+  ninfo("TS1: %" PRIu32 " TS2: %" PRIu32 " BRP: %" PRIu32 "\n",
+               ts1, ts2, brp);
+
+  /* Configure bit timing.  This also does the following, less obvious
+   * things.  Unless loopback mode is enabled, it:
+   *
+   * - Disables silent mode.
+   * - Disables loopback mode.
+   *
+   * NOTE that for the time being, SJW is set to 1 just because I don't
+   * know any better.
+   */
+
+  tmp = ((brp - 1) << CAN_BTR_BRP_SHIFT) | ((ts1 - 1) << CAN_BTR_TS1_SHIFT) |
+        ((ts2 - 1) << CAN_BTR_TS2_SHIFT) | ((1 - 1) << CAN_BTR_SJW_SHIFT);

Review Comment:
   I prefer to keep it as it is for compliance with other STM32 CAN implementations



-- 
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


[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a diff in pull request #7346: stm32f7: add SocketCAN support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7346:
URL: https://github.com/apache/incubator-nuttx/pull/7346#discussion_r998345440


##########
boards/arm/stm32f7/nucleo-144/configs/f722-can/Make.defs:
##########
@@ -0,0 +1,39 @@
+############################################################################
+# boards/arm/stm32f7/nucleo-144/configs/f746-nsh/Make.defs
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(TOPDIR)/.config

Review Comment:
   why not share the same Make.defs for all configs



-- 
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


[GitHub] [incubator-nuttx] pkarashchenko commented on a diff in pull request #7346: stm32f7: add SocketCAN support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #7346:
URL: https://github.com/apache/incubator-nuttx/pull/7346#discussion_r998631074


##########
arch/arm/src/stm32f7/stm32_can_sock.c:
##########
@@ -0,0 +1,2538 @@
+/****************************************************************************
+ * arch/arm/src/stm32f7/stm32_can_sock.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/wqueue.h>
+#include <nuttx/can.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/can.h>
+#include <netpacket/can.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32_gpio.h"
+#include "stm32_rcc.h"
+#include "stm32_can.h"
+
+/* Ported form arch/arm/src/stm32/stm32_can_sock.c */
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Delays *******************************************************************/
+
+/* Time out for INAK bit */
+
+#define INAK_TIMEOUT 65535
+
+/* Bit timing ***************************************************************/
+
+#define CAN_BIT_QUANTA (CONFIG_STM32F7_CAN_TSEG1 + CONFIG_STM32F7_CAN_TSEG2 + 1)
+
+#ifndef CONFIG_DEBUG_CAN_INFO
+#  undef CONFIG_STM32F7_CAN_REGDEBUG
+#endif
+
+/* Pool configuration *******************************************************/
+
+#define POOL_SIZE  (1)
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#endif
+
+/* The low priority work queue is preferred.  If it is not enabled, LPWORK
+ * will be the same as HPWORK.
+ *
+ * NOTE:  However, the network should NEVER run on the high priority work
+ * queue!  That queue is intended only to service short back end interrupt
+ * processing that never suspends.  Suspending the high priority work queue
+ * may bring the system to its knees!
+ */
+
+#define CANWORK LPWORK
+
+/* CAN error interrupts */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+#  define STM32_CAN_ERRINT (CAN_IER_LECIE | CAN_IER_ERRIE | \
+                            CAN_IER_BOFIE | CAN_IER_EPVIE | \
+                            CAN_IER_EWGIE)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32_can_s
+{
+  uint8_t  port;     /* CAN port number (1 or 2) */
+  uint8_t  canrx[2]; /* CAN RX FIFO 0/1 IRQ number */
+  uint8_t  cantx;    /* CAN TX IRQ number */
+#ifdef CONFIG_NET_CAN_ERRORS
+  uint8_t  cansce;   /* CAN SCE IRQ number */
+#endif
+  uint8_t  filter;   /* Filter number */
+  uint32_t base;     /* Base address of the CAN control registers */
+  uint32_t fbase;    /* Base address of the CAN filter registers */
+  uint32_t baud;     /* Configured baud */
+
+  bool                bifup;  /* true:ifup false:ifdown */
+  struct net_driver_s dev;    /* Interface understood by the network */
+
+  struct work_s irqwork;  /* For deferring interrupt work to the wq */
+  struct work_s pollwork; /* For deferring poll work to the work wq */
+
+  /* A pointers to the list of TX/RX descriptors */
+
+  struct can_frame *txdesc;
+  struct can_frame *rxdesc;
+
+  /* TX/RX pool */
+
+  uint8_t tx_pool[sizeof(struct can_frame)*POOL_SIZE];
+  uint8_t rx_pool[sizeof(struct can_frame)*POOL_SIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* CAN Register access */
+
+static uint32_t stm32can_getreg(struct stm32_can_s *priv,
+                                int offset);
+static uint32_t stm32can_getfreg(struct stm32_can_s *priv,
+                                 int offset);
+static void stm32can_putreg(struct stm32_can_s *priv, int offset,
+                            uint32_t value);
+static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
+                             uint32_t value);
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_dumpctrlregs(struct stm32_can_s *priv,
+                                  const char *msg);
+static void stm32can_dumpmbregs(struct stm32_can_s *priv,
+                                const char *msg);
+static void stm32can_dumpfiltregs(struct stm32_can_s *priv,
+                                  const char *msg);
+#else
+#  define stm32can_dumpctrlregs(priv,msg)
+#  define stm32can_dumpmbregs(priv,msg)
+#  define stm32can_dumpfiltregs(priv,msg)
+#endif
+
+/* CAN interrupt enable functions */
+
+static void stm32can_rx0int(struct stm32_can_s *priv, bool enable);
+static void stm32can_rx1int(struct stm32_can_s *priv, bool enable);
+static void stm32can_txint(struct stm32_can_s *priv, bool enable);
+#ifdef CONFIG_NET_CAN_ERRORS
+static void stm32can_errint(struct stm32_can_s *priv, bool enable);
+#endif
+
+/* Common TX logic */
+
+static int stm32can_transmit(struct stm32_can_s *priv);
+static bool stm32can_txready(struct stm32_can_s *priv);
+static int  stm32can_txpoll(struct net_driver_s *dev);
+
+/* CAN RX interrupt handling */
+
+static int  stm32can_rxinterrupt_work(struct stm32_can_s *priv,
+                                      int rxmb);
+
+static void stm32can_rx0interrupt_work(void *arg);
+static void stm32can_rx1interrupt_work(void *arg);
+static int stm32can_rxinterrupt(struct stm32_can_s *priv, int rxmb);
+
+static int  stm32can_rx0interrupt(int irq, void *context, void *arg);
+static int  stm32can_rx1interrupt(int irq, void *context, void *arg);
+
+/* CAN TX interrupt handling */
+
+static int  stm32can_txinterrupt(int irq, void *context, void *arg);
+static void stm32can_txdone_work(void *arg);
+static void stm32can_txdone(struct stm32_can_s *priv);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/* CAN errors interrupt handling */
+
+static void stm32can_sceinterrupt_work(void *arg);
+static int  stm32can_sceinterrupt(int irq, void *context, void *arg);
+#endif
+
+/* Initialization */
+
+static int  stm32can_setup(struct stm32_can_s *priv);
+static void  stm32can_shutdown(struct stm32_can_s *priv);
+static void  stm32can_reset(struct stm32_can_s *priv);
+static int  stm32can_enterinitmode(struct stm32_can_s *priv);
+static int  stm32can_exitinitmode(struct stm32_can_s *priv);
+static int  stm32can_bittiming(struct stm32_can_s *priv);
+static int  stm32can_cellinit(struct stm32_can_s *priv);
+static int  stm32can_filterinit(struct stm32_can_s *priv);
+
+/* TX mailbox status */
+
+static bool stm32can_txmb0empty(uint32_t tsr_regval);
+static bool stm32can_txmb1empty(uint32_t tsr_regval);
+static bool stm32can_txmb2empty(uint32_t tsr_regval);
+
+/* NuttX callback functions */
+
+static int  stm32can_ifup(struct net_driver_s *dev);
+static int  stm32can_ifdown(struct net_driver_s *dev);
+
+static void stm32can_txavail_work(void *arg);
+static int  stm32can_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int  stm32can_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                  unsigned long arg);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN1
+
+static struct stm32_can_s g_can1priv =
+{
+  .port             = 1,
+  .canrx            =
+  {
+                      STM32_IRQ_CAN1RX0,
+                      STM32_IRQ_CAN1RX1,
+  },
+  .cantx            = STM32_IRQ_CAN1TX,
+#ifdef CONFIG_NET_CAN_ERRORS
+  .cansce           = STM32_IRQ_CAN1SCE,
+#endif
+  .filter           = 0,
+  .base             = STM32_CAN1_BASE,
+  .fbase            = STM32_CAN1_BASE,
+  .baud             = CONFIG_STM32F7_CAN1_BAUD,
+};
+
+#endif
+
+#ifdef CONFIG_STM32F7_CAN2
+
+static struct stm32_can_s g_can2priv =
+{
+  .port             = 2,
+  .canrx            =
+  {
+                      STM32_IRQ_CAN2RX0,
+                      STM32_IRQ_CAN2RX1,
+  },
+  .cantx            = STM32_IRQ_CAN2TX,
+#ifdef CONFIG_NET_CAN_ERRORS
+  .cansce           = STM32_IRQ_CAN2SCE,
+#endif
+  .filter           = CAN_NFILTERS / 2,
+  .base             = STM32_CAN2_BASE,
+  .fbase            = STM32_CAN1_BASE,
+  .baud             = CONFIG_STM32F7_CAN2_BAUD,
+};
+
+#endif
+
+#ifdef CONFIG_STM32F7_CAN3
+
+static struct stm32_can_s g_can3priv =
+{
+  .port             = 3,
+  .canrx            =
+  {
+    STM32_IRQ_CAN3RX0,
+    STM32_IRQ_CAN3RX1,
+  },
+  .cantx            = STM32_IRQ_CAN3TX,
+#ifdef CONFIG_NET_CAN_ERRORS
+  .cansce           = STM32_IRQ_CAN3SCE,
+#endif
+  .filter           = CAN_NFILTERS / 2,
+  .base             = STM32_CAN3_BASE,
+  .fbase            = STM32_CAN3_BASE,
+  .baud             = CONFIG_STM32F7_CAN3_BAUD,
+};
+
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32can_getreg
+ * Name: stm32can_getfreg
+ *
+ * Description:
+ *   Read the value of a CAN register or filter block register.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static uint32_t stm32can_vgetreg(uint32_t addr)
+{
+  static uint32_t prevaddr = 0;
+  static uint32_t preval   = 0;
+  static uint32_t count    = 0;
+
+  /* Read the value from the register */
+
+  uint32_t val = getreg32(addr);
+
+  /* Is this the same value that we read from the same register last time?
+   * Are we polling the register?  If so, suppress some of the output.
+   */
+
+  if (addr == prevaddr && val == preval)
+    {
+      if (count == 0xffffffff || ++count > 3)
+        {
+          if (count == 4)
+            {
+              ninfo("...\n");
+            }
+
+          return val;
+        }
+    }
+
+  /* No this is a new address or value */
+
+  else
+    {
+      /* Did we print "..." for the previous value? */
+
+      if (count > 3)
+        {
+          /* Yes.. then show how many times the value repeated */
+
+          ninfo("[repeats %" PRIu32 " more times]\n", count - 3);
+        }
+
+      /* Save the new address, value, and count */
+
+      prevaddr = addr;
+      preval   = val;
+      count    = 1;
+    }
+
+  /* Show the register value read */
+
+  ninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
+  return val;
+}
+
+static uint32_t stm32can_getreg(struct stm32_can_s *priv, int offset)
+{
+  return stm32can_vgetreg(priv->base + offset);
+}
+
+static uint32_t stm32can_getfreg(struct stm32_can_s *priv, int offset)
+{
+  return stm32can_vgetreg(priv->fbase + offset);
+}
+
+#else
+static uint32_t stm32can_getreg(struct stm32_can_s *priv, int offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+static uint32_t stm32can_getfreg(struct stm32_can_s *priv, int offset)
+{
+  return getreg32(priv->fbase + offset);
+}
+
+#endif
+
+/****************************************************************************
+ * Name: stm32can_putreg
+ * Name: stm32can_putfreg
+ *
+ * Description:
+ *   Set the value of a CAN register or filter block register.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_vputreg(uint32_t addr, uint32_t value)
+{
+  /* Show the register value being written */
+
+  ninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
+
+  /* Write the value */
+
+  putreg32(value, addr);
+}
+
+static void stm32can_putreg(struct stm32_can_s *priv, int offset,
+                            uint32_t value)
+{
+  stm32can_vputreg(priv->base + offset, value);
+}
+
+static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
+                             uint32_t value)
+{
+  stm32can_vputreg(priv->fbase + offset, value);
+}
+
+#else
+static void stm32can_putreg(struct stm32_can_s *priv, int offset,
+                            uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
+                             uint32_t value)
+{
+  putreg32(value, priv->fbase + offset);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_dumpctrlregs
+ *
+ * Description:
+ *   Dump the contents of all CAN control registers
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *   msg  - message
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_dumpctrlregs(struct stm32_can_s *priv,
+                                  const char *msg)
+{
+  if (msg)
+    {
+      ninfo("Control Registers: %s\n", msg);
+    }
+  else
+    {
+      ninfo("Control Registers:\n");
+    }
+
+  /* CAN control and status registers */
+
+  ninfo("  MCR: %08" PRIx32 "   MSR: %08" PRIx32 "   TSR: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_MCR_OFFSET),
+          getreg32(priv->base + STM32_CAN_MSR_OFFSET),
+          getreg32(priv->base + STM32_CAN_TSR_OFFSET));
+
+  ninfo(" RF0R: %08" PRIx32 "  RF1R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_RF0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RF1R_OFFSET));
+
+  ninfo("  IER: %08" PRIx32 "   ESR: %08" PRIx32 "   BTR: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_IER_OFFSET),
+          getreg32(priv->base + STM32_CAN_ESR_OFFSET),
+          getreg32(priv->base + STM32_CAN_BTR_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_dumpmbregs
+ *
+ * Description:
+ *   Dump the contents of all CAN mailbox registers
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *   msg  - message
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_dumpmbregs(struct stm32_can_s *priv,
+                                const char *msg)
+{
+  if (msg)
+    {
+      ninfo("Mailbox Registers: %s\n", msg);
+    }
+  else
+    {
+      ninfo("Mailbox Registers:\n");
+    }
+
+  /* CAN mailbox registers (3 TX and 2 RX) */
+
+  ninfo(" TI0R: %08" PRIx32 " TDT0R: %08" PRIx32 " TDL0R: %08"
+          PRIx32 " TDH0R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_TI0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDT0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDL0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDH0R_OFFSET));
+
+  ninfo(" TI1R: %08" PRIx32 " TDT1R: %08" PRIx32 " TDL1R: %08"
+          PRIx32 " TDH1R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_TI1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDT1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDL1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDH1R_OFFSET));
+
+  ninfo(" TI2R: %08" PRIx32 " TDT2R: %08" PRIx32 " TDL2R: %08"
+          PRIx32 " TDH2R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_TI2R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDT2R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDL2R_OFFSET),
+          getreg32(priv->base + STM32_CAN_TDH2R_OFFSET));
+
+  ninfo(" RI0R: %08" PRIx32 " RDT0R: %08" PRIx32 " RDL0R: %08"
+          PRIx32 " RDH0R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_RI0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDT0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDL0R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDH0R_OFFSET));
+
+  ninfo(" RI1R: %08" PRIx32 " RDT1R: %08" PRIx32 " RDL1R: %08"
+          PRIx32 " RDH1R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_RI1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDT1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDL1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_RDH1R_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_dumpfiltregs
+ *
+ * Description:
+ *   Dump the contents of all CAN filter registers
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *   msg  - message
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_CAN_REGDEBUG
+static void stm32can_dumpfiltregs(struct stm32_can_s *priv,
+                                  const char *msg)
+{
+  int i;
+
+  if (msg)
+    {
+      ninfo("Filter Registers: %s\n", msg);
+    }
+  else
+    {
+      ninfo("Filter Registers:\n");
+    }
+
+  ninfo(" FMR: %08" PRIx32 "   FM1R: %08" PRIx32 "  FS1R: %08"
+          PRIx32 " FFA1R: %08" PRIx32 "  FA1R: %08" PRIx32 "\n",
+          getreg32(priv->base + STM32_CAN_FMR_OFFSET),
+          getreg32(priv->base + STM32_CAN_FM1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_FS1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_FFA1R_OFFSET),
+          getreg32(priv->base + STM32_CAN_FA1R_OFFSET));
+
+  for (i = 0; i < CAN_NFILTERS; i++)
+    {
+      ninfo(" F%dR1: %08" PRIx32 " F%dR2: %08" PRIx32 "\n",
+              i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 1)),
+              i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 2)));
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_rx0int
+ *
+ * Description:
+ *   Call to enable or disable RX0 interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_rx0int(struct stm32_can_s *priv, bool enable)
+{
+  uint32_t regval = 0;
+
+  ninfo("CAN%" PRIu8 "RX0 enable: %d\n", priv->port, enable);
+
+  /* Enable/disable the FIFO 0 message pending interrupt */
+
+  regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  if (enable)
+    {
+      regval |= CAN_IER_FMPIE0;
+    }
+  else
+    {
+      regval &= ~CAN_IER_FMPIE0;
+    }
+
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Name: stm32can_rx1int
+ *
+ * Description:
+ *   Call to enable or disable RX1 interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_rx1int(struct stm32_can_s *priv, bool enable)
+{
+  uint32_t regval = 0;
+
+  ninfo("CAN%" PRIu8 "RX1 enable: %d\n", priv->port, enable);
+
+  /* Enable/disable the FIFO 1 message pending interrupt */
+
+  regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  if (enable)
+    {
+      regval |= CAN_IER_FMPIE1;
+    }
+  else
+    {
+      regval &= ~CAN_IER_FMPIE1;
+    }
+
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Name: stm32can_txint
+ *
+ * Description:
+ *   Call to enable or disable TX interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_txint(struct stm32_can_s *priv, bool enable)
+{
+  uint32_t regval = 0;
+
+  ninfo("CAN%" PRIu8 " txint enable: %d\n", priv->port, enable);
+
+  /* Enable/disable the transmit mailbox interrupt */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  if (enable)
+    {
+      regval |= CAN_IER_TMEIE;
+    }
+  else
+    {
+      regval &= ~CAN_IER_TMEIE;
+    }
+
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+}
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: stm32can_txint
+ *
+ * Description:
+ *   Call to enable or disable CAN SCE interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_errint(struct stm32_can_s *priv, bool enable)
+{
+  uint32_t regval = 0;
+
+  /* Enable/disable the transmit mailbox interrupt */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  if (enable)
+    {
+      regval |= STM32_CAN_ERRINT;
+    }
+  else
+    {
+      regval &= ~STM32F7_CAN_ERRINT;
+    }
+
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+}
+#endif
+
+/****************************************************************************
+ * Function: stm32can_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int stm32can_ifup(struct net_driver_s *dev)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+
+  /* Setup CAN */
+
+  stm32can_setup(priv);
+
+  /* Enable interrupts */
+
+  stm32can_rx0int(priv, true);
+  stm32can_rx1int(priv, true);
+  stm32can_txint(priv, true);
+#ifdef CONFIG_NET_CAN_ERRORS
+  stm32can_errint(priv, true);
+#endif
+
+  /* Enable the interrupts at the NVIC */
+
+  up_enable_irq(priv->canrx[0]);
+  up_enable_irq(priv->canrx[1]);
+  up_enable_irq(priv->cantx);
+#ifdef CONFIG_NET_CAN_ERRORS
+  up_enable_irq(priv->cansce);
+#endif
+
+  priv->bifup = true;
+
+  priv->txdesc = (struct can_frame *)priv->tx_pool;
+  priv->rxdesc = (struct can_frame *)priv->rx_pool;
+
+  priv->dev.d_buf = (uint8_t *)priv->txdesc;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: stm32can_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int stm32can_ifdown(struct net_driver_s *dev)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+
+  /* Disable CAN interrupts */
+
+  stm32can_shutdown(priv);
+
+  /* Reset CAN */
+
+  stm32can_reset(priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32can_txready
+ *
+ * Description:
+ *   Return true if the CAN hardware can accept another TX message.
+ *
+ ****************************************************************************/
+
+static bool stm32can_txready(struct stm32_can_s *priv)
+{
+  uint32_t regval;
+
+  /* Return true if any mailbox is available */
+
+  regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
+  ninfo("CAN%" PRIu8 " TSR: %08" PRIx32 "\n", priv->port, regval);
+
+  return (stm32can_txmb0empty(regval) || stm32can_txmb1empty(regval) ||
+          stm32can_txmb2empty(regval));
+}
+
+/****************************************************************************
+ * Name: stm32can_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the txdone interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int stm32can_transmit(struct stm32_can_s *priv)
+{
+  struct can_frame *frame = (struct can_frame *)priv->dev.d_buf;
+  uint8_t          *ptr;
+  uint32_t          regval;
+  uint32_t          tmp;
+  int               dlc;
+  int               txmb;
+
+  ninfo("CAN%" PRIu8 " ID: %" PRIu32 " DLC: %" PRIu8 "\n",
+        priv->port, (uint32_t)frame->can_id, frame->can_dlc);
+
+  /* Select one empty transmit mailbox */
+
+  regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
+  if (stm32can_txmb0empty(regval))
+    {
+      txmb = 0;
+    }
+  else if (stm32can_txmb1empty(regval))
+    {
+      txmb = 1;
+    }
+  else if (stm32can_txmb2empty(regval))
+    {
+      txmb = 2;
+    }
+  else
+    {
+      canerr("ERROR: No available mailbox\n");
+      return -EBUSY;
+    }
+
+  /* Clear TXRQ, RTR, IDE, EXID, and STID fields */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb));
+  regval &= ~(CAN_TIR_TXRQ | CAN_TIR_RTR | CAN_TIR_IDE |
+              CAN_TIR_EXID_MASK | CAN_TIR_STID_MASK);
+  stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
+
+  /* Set up the ID, standard 11-bit or extended 29-bit. */
+
+#ifdef CONFIG_NET_CAN_EXTID
+  regval &= ~CAN_TIR_EXID_MASK;
+  if (frame->can_id & CAN_EFF_FLAG)
+    {
+      DEBUGASSERT(frame->can_id < (1 << 29));
+      regval |= (frame->can_id << CAN_TIR_EXID_SHIFT) | CAN_TIR_IDE;
+    }
+  else
+    {
+      DEBUGASSERT(frame->can_id < (1 << 11));
+      regval |= frame->can_id << CAN_TIR_STID_SHIFT;
+    }
+
+#else
+  regval |= (((uint32_t) frame->can_id << CAN_TIR_STID_SHIFT) &
+               CAN_TIR_STID_MASK);
+
+#endif
+
+#ifdef CONFIG_CAN_USE_RTR
+  regval |= ((frame->can_id & CAN_RTR_FLAG) ? CAN_TIR_RTR : 0);
+#endif
+
+  stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
+
+  /* Set up the DLC */
+
+  dlc     = frame->can_dlc;
+  regval  = stm32can_getreg(priv, STM32_CAN_TDTR_OFFSET(txmb));
+  regval &= ~(CAN_TDTR_DLC_MASK | CAN_TDTR_TGT);
+  regval |= (uint32_t)dlc << CAN_TDTR_DLC_SHIFT;
+  stm32can_putreg(priv, STM32_CAN_TDTR_OFFSET(txmb), regval);
+
+  /* Set up the data fields */
+
+  ptr    = frame->data;
+  regval = 0;
+
+  if (dlc > 0)
+    {
+      tmp    = (uint32_t)*ptr++;
+      regval = tmp << CAN_TDLR_DATA0_SHIFT;
+
+      if (dlc > 1)
+        {
+          tmp     = (uint32_t)*ptr++;
+          regval |= tmp << CAN_TDLR_DATA1_SHIFT;
+
+          if (dlc > 2)
+            {
+              tmp     = (uint32_t)*ptr++;
+              regval |= tmp << CAN_TDLR_DATA2_SHIFT;
+
+              if (dlc > 3)
+                {
+                  tmp     = (uint32_t)*ptr++;
+                  regval |= tmp << CAN_TDLR_DATA3_SHIFT;
+                }
+            }
+        }
+    }
+
+  stm32can_putreg(priv, STM32_CAN_TDLR_OFFSET(txmb), regval);
+
+  regval = 0;
+  if (dlc > 4)
+    {
+      tmp    = (uint32_t)*ptr++;
+      regval = tmp << CAN_TDHR_DATA4_SHIFT;
+
+      if (dlc > 5)
+        {
+          tmp     = (uint32_t)*ptr++;
+          regval |= tmp << CAN_TDHR_DATA5_SHIFT;
+
+          if (dlc > 6)
+            {
+              tmp     = (uint32_t)*ptr++;
+              regval |= tmp << CAN_TDHR_DATA6_SHIFT;
+
+              if (dlc > 7)
+                {
+                  tmp     = (uint32_t)*ptr++;
+                  regval |= tmp << CAN_TDHR_DATA7_SHIFT;
+                }
+            }
+        }
+    }
+
+  stm32can_putreg(priv, STM32_CAN_TDHR_OFFSET(txmb), regval);
+
+  /* Enable the transmit mailbox empty interrupt (may already be enabled) */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
+  regval |= CAN_IER_TMEIE;
+  stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
+
+  /* Request transmission */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb));
+  regval |= CAN_TIR_TXRQ;  /* Transmit Mailbox Request */
+  stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
+
+  stm32can_dumpmbregs(priv, "After send");
+  return OK;
+}
+
+/****************************************************************************
+ * Function: stm32can_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int stm32can_txpoll(struct net_driver_s *dev)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      if (!devif_loopback(&priv->dev))
+        {
+          stm32can_txdone(priv);
+
+          /* Send the packet */
+
+          stm32can_transmit(priv);
+
+          /* Check if there is room in the device to hold another packet. If
+           * not, return a non-zero value to terminate the poll.
+           */
+
+          if (stm32can_txready(priv) == false)
+            {
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: stm32can_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void stm32can_txavail_work(void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->bifup)
+    {
+      /* Check if there is room in the hardware to hold another outgoing
+       * packet.
+       */
+
+      if (stm32can_txready(priv))
+        {
+          /* No, there is space for another transfer.  Poll the network for
+           * new XMIT data.
+           */
+
+          devif_poll(&priv->dev, stm32can_txpoll);
+        }
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: stm32can_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int stm32can_txavail(struct net_driver_s *dev)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      stm32can_txavail_work(priv);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: stm32can_ioctl
+ *
+ * Description:
+ *   PHY ioctl command handler
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   cmd  - ioctl command
+ *   arg  - Argument accompanying the command
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int stm32can_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                 unsigned long arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)dev->d_private;
+  int                 ret  = OK;
+
+  switch (cmd)
+    {
+      /* TODO */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Name: stm32can_rxinterrupt_work
+ *
+ * Description:
+ *   CAN RX FIFO 0/1 interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *   rxmb - The RX mailbox number.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_rxinterrupt_work(struct stm32_can_s *priv, int rxmb)
+{
+  struct can_frame *frame = (struct can_frame *)priv->rxdesc;
+  uint32_t          regval;
+  int               ret   = OK;
+
+  DEBUGASSERT(priv != NULL);
+
+  if (rxmb == 0)
+    {
+      stm32can_dumpmbregs(priv, "RX0 interrupt");
+    }
+  else
+    {
+      stm32can_dumpmbregs(priv, "RX1 interrupt");
+    }
+
+  /* Get the CAN identifier. */
+
+  regval = stm32can_getreg(priv, STM32_CAN_RIR_OFFSET(rxmb));
+
+#ifdef CONFIG_NET_CAN_EXTID
+  if ((regval & CAN_RIR_IDE) != 0)
+    {
+      frame->can_id  = (regval & CAN_RIR_EXID_MASK) >> CAN_RIR_EXID_SHIFT;
+      frame->can_id &= ~CAN_EFF_FLAG;
+    }
+  else
+    {
+      frame->can_id  = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT;
+      frame->can_id |= CAN_EFF_FLAG;
+    }
+#else
+  if ((regval & CAN_RIR_IDE) != 0)
+    {
+      nerr("ERROR: Received message with extended identifier.  Dropped\n");
+      ret = -ENOSYS;
+      goto errout;
+    }
+
+  frame->can_id = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT;
+#endif
+
+  /* Extract the RTR bit */
+
+  if ((regval & CAN_RIR_RTR) != 0)
+    {
+      frame->can_id |= CAN_RTR_FLAG;
+    }
+
+  /* Get the DLC */
+
+  regval        = stm32can_getreg(priv, STM32_CAN_RDTR_OFFSET(rxmb));
+  frame->can_dlc = (regval & CAN_RDTR_DLC_MASK) >> CAN_RDTR_DLC_SHIFT;
+
+  /* Save the message data */
+
+  regval  = stm32can_getreg(priv, STM32_CAN_RDLR_OFFSET(rxmb));
+  frame->data[0] = (regval & CAN_RDLR_DATA0_MASK) >> CAN_RDLR_DATA0_SHIFT;
+  frame->data[1] = (regval & CAN_RDLR_DATA1_MASK) >> CAN_RDLR_DATA1_SHIFT;
+  frame->data[2] = (regval & CAN_RDLR_DATA2_MASK) >> CAN_RDLR_DATA2_SHIFT;
+  frame->data[3] = (regval & CAN_RDLR_DATA3_MASK) >> CAN_RDLR_DATA3_SHIFT;
+
+  regval  = stm32can_getreg(priv, STM32_CAN_RDHR_OFFSET(rxmb));
+  frame->data[4] = (regval & CAN_RDHR_DATA4_MASK) >> CAN_RDHR_DATA4_SHIFT;
+  frame->data[5] = (regval & CAN_RDHR_DATA5_MASK) >> CAN_RDHR_DATA5_SHIFT;
+  frame->data[6] = (regval & CAN_RDHR_DATA6_MASK) >> CAN_RDHR_DATA6_SHIFT;
+  frame->data[7] = (regval & CAN_RDHR_DATA7_MASK) >> CAN_RDHR_DATA7_SHIFT;
+
+  /* Copy the buffer pointer to priv->dev..  Set amount of data
+   * in priv->dev.d_len
+   */
+
+  priv->dev.d_len = sizeof(struct can_frame);
+  priv->dev.d_buf = (uint8_t *)frame;
+
+  /* Send to socket interface */
+
+  NETDEV_RXPACKETS(&priv->dev);
+
+  can_input(&priv->dev);
+
+  /* Point the packet buffer back to the next Tx buffer that will be
+   * used during the next write.  If the write queue is full, then
+   * this will point at an active buffer, which must not be written
+   * to.  This is OK because devif_poll won't be called unless the
+   * queue is not full.
+   */
+
+  priv->dev.d_buf = (uint8_t *)priv->txdesc;
+
+  /* Release the FIFO */
+
+#ifndef CONFIG_NET_CAN_EXTID
+errout:
+#endif
+  regval  = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb));
+  regval |= CAN_RFR_RFOM;
+  stm32can_putreg(priv, STM32_CAN_RFR_OFFSET(rxmb), regval);
+
+  /* Re-enable CAN RX interrupts */
+
+  if (rxmb == 0)
+    {
+      stm32can_rx0int(priv, true);
+    }
+  else if (rxmb == 1)
+    {
+      stm32can_rx1int(priv, true);
+    }
+  else
+    {
+      DEBUGPANIC();
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32can_rx0interrupt_work
+ ****************************************************************************/
+
+static void stm32can_rx0interrupt_work(void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  stm32can_rxinterrupt_work(priv, 0);
+}
+
+/****************************************************************************
+ * Name: stm32can_rx1interrupt_work
+ ****************************************************************************/
+
+static void stm32can_rx1interrupt_work(void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  stm32can_rxinterrupt_work(priv, 1);
+}
+
+/****************************************************************************
+ * Name: stm32can_rxinterrupt
+ *
+ * Description:
+ *   CAN RX FIFO common interrupt handler
+ *
+ ****************************************************************************/
+
+static int stm32can_rxinterrupt(struct stm32_can_s *priv, int rxmb)
+{
+  uint32_t regval   = 0;
+  int      npending = 0;
+
+  /* Verify that a message is pending in the FIFO */
+
+  regval   = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb));
+  npending = (regval & CAN_RFR_FMP_MASK) >> CAN_RFR_FMP_SHIFT;
+  if (npending < 1)
+    {
+      nwarn("WARNING: No messages pending\n");
+      return OK;
+    }
+
+  /* Disable further CAN RX interrupts and schedule to perform the
+   * interrupt processing on the worker thread
+   */
+
+  if (rxmb == 0)
+    {
+      stm32can_rx0int(priv, false);
+      work_queue(CANWORK, &priv->irqwork,
+                 stm32can_rx0interrupt_work, priv, 0);
+    }
+  else if (rxmb == 1)
+    {
+      stm32can_rx1int(priv, false);
+      work_queue(CANWORK, &priv->irqwork,
+                 stm32can_rx1interrupt_work, priv, 0);
+    }
+  else
+    {
+      DEBUGPANIC();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32can_rx0interrupt
+ *
+ * Description:
+ *   CAN RX FIFO 0 interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_rx0interrupt(int irq, void *context, void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  return stm32can_rxinterrupt(priv, 0);
+}
+
+/****************************************************************************
+ * Name: stm32can_rx1interrupt
+ *
+ * Description:
+ *   CAN RX FIFO 1 interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_rx1interrupt(int irq, void *context, void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  return stm32can_rxinterrupt(priv, 1);
+}
+
+/****************************************************************************
+ * Name: stm32can_txinterrupt
+ *
+ * Description:
+ *   CAN TX mailbox complete interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_txinterrupt(int irq, void *context, void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+  uint32_t regval;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Get the transmit status */
+
+  regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
+
+  /* Check for RQCP0: Request completed mailbox 0 */
+
+  if ((regval & CAN_TSR_RQCP0) != 0)
+    {
+      /* Writing '1' to RCP0 clears RCP0 and all the status bits (TXOK0,
+       * ALST0 and TERR0) for Mailbox 0.
+       */
+
+      stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP0);
+
+      /* Tell the upper half that the transfer is finished. */
+
+      /* Disable further TX CAN interrupts. here can be no race
+       * condition here.
+       */
+
+      stm32can_txint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0);
+    }
+
+  /* Check for RQCP1: Request completed mailbox 1 */
+
+  if ((regval & CAN_TSR_RQCP1) != 0)
+    {
+      /* Writing '1' to RCP1 clears RCP1 and all the status bits (TXOK1,
+       * ALST1 and TERR1) for Mailbox 1.
+       */
+
+      stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP1);
+
+      /* Tell the upper half that the transfer is finished. */
+
+      /* Disable further TX CAN interrupts. here can be no race
+       * condition here.
+       */
+
+      stm32can_txint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0);
+    }
+
+  /* Check for RQCP2: Request completed mailbox 2 */
+
+  if ((regval & CAN_TSR_RQCP2) != 0)
+    {
+      /* Writing '1' to RCP2 clears RCP2 and all the status bits (TXOK2,
+       * ALST2 and TERR2) for Mailbox 2.
+       */
+
+      stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP2);
+
+      /* Disable further TX CAN interrupts. here can be no race
+       * condition here.
+       */
+
+      stm32can_txint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32can_txdone_work
+ ****************************************************************************/
+
+static void stm32can_txdone_work(void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+
+  stm32can_txdone(priv);
+
+  /* There should be space for a new TX in any event.  Poll the network for
+   * new XMIT data
+   */
+
+  net_lock();
+  devif_poll(&priv->dev, stm32can_txpoll);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: stm32can_txdone
+ ****************************************************************************/
+
+static void stm32can_txdone(struct stm32_can_s *priv)
+{
+  stm32can_txint(priv, true);
+
+  NETDEV_TXDONE(&priv->dev);
+}
+
+#ifdef CONFIG_NET_CAN_ERRORS
+
+/****************************************************************************
+ * Name: stm32can_sceinterrupt_work
+ *
+ * Description:
+ *   CAN status change interrupt work
+ *
+ * Input Parameters:
+ *   arg  - reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32can_sceinterrupt_work(void *arg)
+{
+  struct stm32_can_s *priv    = (struct stm32_can_s *)arg;
+  struct can_frame   *frame   = (struct can_frame *)priv->rxdesc;
+  uint32_t            regval  = 0;
+  uint16_t            errbits = 0;
+  uint8_t             data[CAN_ERR_DLC];
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Check Error Interrupt flag */
+
+  regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET);
+  if (regval & CAN_MSR_ERRI)
+    {
+      /* Encode error bits */
+
+      errbits = 0;
+      memset(data, 0, sizeof(data));
+
+      /* Get Error statur register */
+
+      regval = stm32can_getreg(priv, STM32_CAN_ESR_OFFSET);
+
+      if (regval & CAN_ESR_EWGF)
+        {
+          /* Error warning flag */
+
+          data[1] |= (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING);
+          errbits |= CAN_ERR_CRTL;
+        }
+
+      if (regval & CAN_ESR_EPVF)
+        {
+          /* Error passive flag */
+
+          data[1] |= (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE);
+          errbits |= CAN_ERR_CRTL;
+        }
+
+      if (regval & CAN_ESR_BOFF)
+        {
+          /* Bus-off flag */
+
+          errbits |= CAN_ERR_BUSOFF;
+        }
+
+      /* Last error code */
+
+      if (regval & CAN_ESR_LEC_MASK)
+        {
+          if (regval & CAN_ESR_STUFFERROR)
+            {
+              /* Stuff Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_STUFF;
+            }
+          else if (regval & CAN_ESR_FORMERROR)
+            {
+              /* Format Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_FORM;
+            }
+          else if (regval & CAN_ESR_ACKERROR)
+            {
+              /* Acknowledge Error */
+
+              errbits |= CAN_ERR_ACK;
+            }
+          else if (regval & CAN_ESR_BRECERROR)
+            {
+              /* Bit recessive Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_BIT1;
+            }
+          else if (regval & CAN_ESR_BDOMERROR)
+            {
+              /* Bit dominant Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_BIT0;
+            }
+          else if (regval & CAN_ESR_CRCERRPR)
+            {
+              /* Receive CRC Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[3] |= CAN_ERR_PROT_LOC_CRCSEQ;
+            }
+        }
+
+      /* Get transmit status register */
+
+      regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
+
+      if (regval & CAN_TSR_ALST0 || regval & CAN_TSR_ALST1 ||
+          regval & CAN_TSR_ALST2)
+        {
+          /* Lost arbitration Error */
+
+          errbits |= CAN_ERR_LOSTARB;
+        }
+
+      /* Clear TSR register */
+
+      stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, regval);
+
+      /* Clear ERRI flag */
+
+      stm32can_putreg(priv, STM32_CAN_MSR_OFFSET, CAN_MSR_ERRI);
+    }
+
+  /* Report a CAN error */
+
+  if (errbits != 0)
+    {
+      canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
+
+      /* Copy frame */
+
+      frame->can_id  = errbits;
+      frame->can_dlc = CAN_ERR_DLC;
+
+      memcpy(frame->data, data, CAN_ERR_DLC);
+
+      /* Copy the buffer pointer to priv->dev..  Set amount of data
+       * in priv->dev.d_len
+       */
+
+      priv->dev.d_len = sizeof(struct can_frame);
+      priv->dev.d_buf = (uint8_t *)frame;
+
+      /* Send to socket interface */
+
+      NETDEV_ERRORS(&priv->dev);
+
+      can_input(&priv->dev);
+
+      /* Point the packet buffer back to the next Tx buffer that will be
+       * used during the next write.  If the write queue is full, then
+       * this will point at an active buffer, which must not be written
+       * to.  This is OK because devif_poll won't be called unless the
+       * queue is not full.
+       */
+
+      priv->dev.d_buf = (uint8_t *)priv->txdesc;
+    }
+
+  /* Re-enable CAN SCE interrupts */
+
+  stm32can_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: stm32can_sceinterrupt
+ *
+ * Description:
+ *   CAN status change interrupt handler
+ *
+ * Input Parameters:
+ *   irq - The IRQ number of the interrupt.
+ *   context - The register state save array at the time of the interrupt.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_sceinterrupt(int irq, void *context, void *arg)
+{
+  struct stm32_can_s *priv = (struct stm32_can_s *)arg;
+
+  /* Disable further CAN SCE interrupts and schedule to perform the
+   * interrupt processing on the worker thread
+   */
+
+  stm32can_errint(priv, false);
+  work_queue(CANWORK, &priv->irqwork,
+             stm32can_sceinterrupt_work, priv, 0);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32can_bittiming
+ *
+ * Description:
+ *   Set the CAN bit timing register (BTR) based on the configured BAUD.
+ *
+ * "The bit timing logic monitors the serial bus-line and performs sampling
+ *  and adjustment of the sample point by synchronizing on the start-bit edge
+ *  and resynchronizing on the following edges.
+ *
+ * "Its operation may be explained simply by splitting nominal bit time into
+ *  three segments as follows:
+ *
+ * 1. "Synchronization segment (SYNC_SEG): a bit change is expected to occur
+ *     within this time segment. It has a fixed length of one time quantum
+ *     (1 x tCAN).
+ * 2. "Bit segment 1 (BS1): defines the location of the sample point. It
+ *     includes the PROP_SEG and PHASE_SEG1 of the CAN standard. Its duration
+ *     is programmable between 1 and 16 time quanta but may be automatically
+ *     lengthened to compensate for positive phase drifts due to differences
+ *     in the frequency of the various nodes of the network.
+ * 3. "Bit segment 2 (BS2): defines the location of the transmit point. It
+ *     represents the PHASE_SEG2 of the CAN standard. Its duration is
+ *     programmable between 1 and 8 time quanta but may also be automatically
+ *     shortened to compensate for negative phase drifts."
+ *
+ * Pictorially:
+ *
+ *  |<----------------- NOMINAL BIT TIME ----------------->|
+ *  |<- SYNC_SEG ->|<------ BS1 ------>|<------ BS2 ------>|
+ *  |<---- Tq ---->|<----- Tbs1 ------>|<----- Tbs2 ------>|
+ *
+ * Where
+ *   Tbs1 is the duration of the BS1 segment
+ *   Tbs2 is the duration of the BS2 segment
+ *   Tq is the "Time Quantum"
+ *
+ * Relationships:
+ *
+ *   baud = 1 / bit_time
+ *   bit_time = Tq + Tbs1 + Tbs2
+ *   Tbs1 = Tq * ts1
+ *   Tbs2 = Tq * ts2
+ *   Tq = brp * Tpclk1
+ *   baud = Fpclk1 / (brp  * (1 + ts1 + ts2))
+ *
+ * Where:
+ *   Tpclk1 is the period of the APB1 clock (PCLK1).
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int stm32can_bittiming(struct stm32_can_s *priv)
+{
+  uint32_t tmp;
+  uint32_t brp;
+  uint32_t ts1;
+  uint32_t ts2;
+
+  ninfo("CAN%" PRIu8 " PCLK1: %lu baud: %" PRIu32 "\n",
+          priv->port, (unsigned long) STM32_PCLK1_FREQUENCY, priv->baud);
+
+  /* Try to get CAN_BIT_QUANTA quanta in one bit_time.
+   *
+   *   bit_time = Tq*(ts1 + ts2 + 1)
+   *   nquanta  = bit_time / Tq
+   *   nquanta  = (ts1 + ts2 + 1)
+   *
+   *   bit_time = brp * Tpclk1 * (ts1 + ts2 + 1)
+   *   nquanta  = bit_time / brp / Tpclk1
+   *            = PCLK1 / baud / brp
+   *   brp      = PCLK1 / baud / nquanta;
+   *
+   * Example:
+   *   PCLK1 = 42,000,000 baud = 1,000,000 nquanta = 14 : brp = 3
+   *   PCLK1 = 42,000,000 baud =   700,000 nquanta = 14 : brp = 4
+   */
+
+  tmp = STM32_PCLK1_FREQUENCY / priv->baud;
+  if (tmp < CAN_BIT_QUANTA)
+    {
+      /* At the smallest brp value (1), there are already too few bit times
+       * (PCLCK1 / baud) to meet our goal.  brp must be one and we need
+       * make some reasonable guesses about ts1 and ts2.
+       */
+
+      brp = 1;
+
+      /* In this case, we have to guess a good value for ts1 and ts2 */
+
+      ts1 = (tmp - 1) >> 1;
+      ts2 = tmp - ts1 - 1;
+      if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX)
+        {
+          ts1--;
+          ts2++;
+        }
+    }
+
+  /* Otherwise, nquanta is CAN_BIT_QUANTA, ts1 is CONFIG_STM32F7_CAN_TSEG1,
+   * ts2 is CONFIG_STM32F7_CAN_TSEG2 and we calculate brp to achieve
+   * CAN_BIT_QUANTA quanta in the bit time
+   */
+
+  else
+    {
+      ts1 = CONFIG_STM32F7_CAN_TSEG1;
+      ts2 = CONFIG_STM32F7_CAN_TSEG2;
+      brp = (tmp + (CAN_BIT_QUANTA / 2)) / CAN_BIT_QUANTA;
+      DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX);
+    }
+
+  ninfo("TS1: %" PRIu32 " TS2: %" PRIu32 " BRP: %" PRIu32 "\n",
+               ts1, ts2, brp);
+
+  /* Configure bit timing.  This also does the following, less obvious
+   * things.  Unless loopback mode is enabled, it:
+   *
+   * - Disables silent mode.
+   * - Disables loopback mode.
+   *
+   * NOTE that for the time being, SJW is set to 1 just because I don't
+   * know any better.
+   */
+
+  tmp = ((brp - 1) << CAN_BTR_BRP_SHIFT) | ((ts1 - 1) << CAN_BTR_TS1_SHIFT) |
+        ((ts2 - 1) << CAN_BTR_TS2_SHIFT) | ((1 - 1) << CAN_BTR_SJW_SHIFT);

Review Comment:
   why (1 - 1) is needed? maybe better to add `const uint32_t sjw = 1;` just for readability?



-- 
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


[GitHub] [incubator-nuttx] raiden00pl commented on a diff in pull request #7346: stm32f7: add SocketCAN support

Posted by GitBox <gi...@apache.org>.
raiden00pl commented on code in PR #7346:
URL: https://github.com/apache/incubator-nuttx/pull/7346#discussion_r999334879


##########
boards/arm/stm32f7/nucleo-144/configs/f722-can/Make.defs:
##########
@@ -0,0 +1,39 @@
+############################################################################
+# boards/arm/stm32f7/nucleo-144/configs/f746-nsh/Make.defs
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(TOPDIR)/.config

Review Comment:
   All configurations in stm32f7/nucleo-144 look like this. 
   Personally, I think stm32f7/nucleo-144 should be divided into separate boards as it is done for the rest of Nucleo boards.
   The current solution is misleading - there are nucleo-144 boards that are not based on stm32f7.  
   I don't know if any other boards family has a similar organization.



-- 
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


[GitHub] [incubator-nuttx] xiaoxiang781216 merged pull request #7346: stm32f7: add SocketCAN support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged PR #7346:
URL: https://github.com/apache/incubator-nuttx/pull/7346


-- 
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