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

[GitHub] [nuttx] hartmannathan commented on a diff in pull request #8304: Added SocketCAN driver implementation to the tiva chip, modified the …

hartmannathan commented on code in PR #8304:
URL: https://github.com/apache/nuttx/pull/8304#discussion_r1089860366


##########
arch/arm/src/tiva/common/tiva_sock_can.c:
##########
@@ -0,0 +1,2371 @@
+/****************************************************************************
+ * arch/arm/src/tiva/common/tiva_sock_can.c
+ * SocketCAN driver implementation for Tiva. Based on the chardev driver.
+ *
+ * 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 <math.h>
+#include <nuttx/mutex.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 "tiva_can.h"
+#include "hardware/tiva_can.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only the last few mailboxes are used for TX to help ensure that responses
+ * to remote request frames we send are always collected by an RX FIFO and
+ * not by the mailbox used to send the remote frame. (The Tiva hardware uses
+ * the mailbox with the lowest number first.)
+ *
+ * This number is zero-indexed, althought the Command Request Register isn't.

Review Comment:
   ```suggestion
    * This number is zero-indexed, although the Command Request Register isn't.
   ```



##########
arch/arm/src/tiva/common/tiva_sock_can.c:
##########
@@ -0,0 +1,2371 @@
+/****************************************************************************
+ * arch/arm/src/tiva/common/tiva_sock_can.c
+ * SocketCAN driver implementation for Tiva. Based on the chardev driver.
+ *
+ * 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 <math.h>
+#include <nuttx/mutex.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 "tiva_can.h"
+#include "hardware/tiva_can.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only the last few mailboxes are used for TX to help ensure that responses
+ * to remote request frames we send are always collected by an RX FIFO and
+ * not by the mailbox used to send the remote frame. (The Tiva hardware uses
+ * the mailbox with the lowest number first.)
+ *
+ * This number is zero-indexed, althought the Command Request Register isn't.
+ */
+#define TIVA_CAN_TX_FIFO_START (TIVA_CAN_NUM_MBOXES - \
+        CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+
+/* Frequency of async error message reporting when rate-limited */
+#define TIVA_CAN_ERR_HANDLER_TICKS MSEC2TICK(CONFIG_TIVA_CAN_ERR_HANDLER_PER)
+
+/* For tivacan_initfilter */
+#define TIVA_CAN_FILTER_TYPE_STD 0
+#define TIVA_CAN_FILTER_TYPE_EXT 1
+
+/* Delays *******************************************************************/
+
+/* Defines for bit timing */
+#define CAN_BIT_QUANTA 20
+#define CAN_TRANSMISSION_DELAY 400E-9
+
+#if defined(CONFIG_TIVA_CAN) && (defined(CONFIG_TIVA_CAN0) || \
+        defined(CONFIG_TIVA_CAN1)) && defined(CONFIG_TIVA_SOCKET_CAN)
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#endif /* CONFIG_SCHED_WORKQUEUE */
+
+/* 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
+
+/* Combines two 16-bit registers into a single 32 bit value */
+#define tivacan_readsplitreg32(base, r1, r2) \
+        ((getreg32((base) + (r1)) & 0xffff) | (getreg32((base) + (r2)) << 16))
+
+#define tivacan_get_nwda32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_NWDA1,\
+                               TIVA_CAN_OFFSET_NWDA2)
+#define tivacan_get_txrq32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_TXRQ1,\
+                               TIVA_CAN_OFFSET_TXRQ2)
+#define tivacan_get_msgval32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_MSG1VAL,\
+                               TIVA_CAN_OFFSET_MSG2VAL)
+
+#define TIVA_CAN_AUTRECOVER
+
+/* Set the maximum number of iterations for the tivacan_bittiming_set timing
+ * parameters calculation
+ */
+#define TIVA_CAN_SET_TIMING_MAX_TER 5
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef uint32_t tiva_can_fifo_t;
+struct tiva_can_s
+{
+  /* Module number */
+
+  int       modnum;
+
+  /* Registers base address */
+
+  uint32_t  base;
+
+  /* Contents of the CANSTS reg the last time errors were processed. */
+
+  uint32_t  status;
+
+  /* Contents of the CANERR reg the last time errors were processed. */
+
+  uint32_t  errors;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  struct work_s error_work; /* For deferring error work to the work wq */
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* Configured baud rate */
+
+  uint32_t baud;
+
+  /* Interface registers base address for threads threads */
+
+  uint32_t  thd_iface_base;
+
+  /* Interface registers base address reserved exclusively for the ISR */
+
+  uint32_t  isr_iface_base;
+
+  /* Mutex for threads accessing the interface registers */
+
+  mutex_t   thd_iface_lock;
+
+  /* Mutex for claiming, freeing, and potentially resizing RX FIFOs.
+   * The TX FIFO should never be resized at runtime.
+   */
+
+  mutex_t   fifo_lock;
+
+  /* true:ifup false:ifdown */
+
+  bool                bifup;
+
+  /* Interface understood by the network */
+
+  struct net_driver_s dev;
+
+  struct work_s irqwork;  /* For deferring interrupt work to the wq */
+  struct work_s pollwork; /* For deferring poll work to the work wq */
+  struct work_s rxwork;   /* For deferring receive work to the work wq */
+
+  /* flag to repeat the loop getting data from all receive mailboxes */
+
+  uint8_t rx_work_repeat;
+
+  /* index of mailbox which is currently processed for data reception */
+
+  uint8_t rx_mbox_index;
+
+  /* List of all FIFOs. Contains the indices of used mailboxes for the
+   * appropriate FIFO.
+   */
+
+  tiva_can_fifo_t fifos[CONFIG_TIVA_CAN_FILTERS_MAX + 1];
+
+  /* Pointer to default catch-all RX FIFO initialized by the driver. */
+
+  tiva_can_fifo_t *rxdefault_fifo;
+
+  /* Pointer to the permanent, fixed-size TX FIFO. This is always located at
+   * the end of the series of mailboxes to help ensure that responses to
+   * remote request frames go to an RX (filter) FIFO and don't interfere.
+   */
+
+  tiva_can_fifo_t *tx_fifo;
+
+  /* Index in the TX FIFO where new messages should be added. The Tiva CAN
+   * module doesn't support a ring buffer, so new messages are only added
+   * when the TX FIFO is empty (tx_fifo_tail == 0).
+   */
+
+  int tx_fifo_tail;
+
+  /* NuttX interface TX/RX pool */
+
+  uint8_t tx_pool[sizeof(struct can_frame)];
+  uint8_t rx_pool[sizeof(struct can_frame)];
+};
+
+/* This structure represents the CAN bit timing settings as seen by the hw. */
+
+struct tiva_can_timing_s
+{
+  int       prescaler;
+  int       tseg1;
+  int       tseg2;
+  int       sjw;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* CAN error interrupts and ISR */
+
+static int  tivacan_isr(int irq, void *context, void *dev);
+
+/* Internal utility functions */
+
+static int tivacan_txpoll(struct net_driver_s *dev);
+static struct tiva_can_timing_s
+tivacan_bittiming_get(struct net_driver_s *dev);
+static void tivacan_bittiming_set(struct net_driver_s *dev,
+                                  uint32_t desired_baud_rate);
+
+static int tivacan_setup(struct net_driver_s *dev);
+static void tivacan_shutdown(struct net_driver_s *dev);
+static void tivacan_reset(struct net_driver_s *dev);
+static int tivacan_send(struct net_driver_s *dev);
+static int tivacan_alloc_fifo(struct net_driver_s *dev, int depth);
+static void tivacan_free_fifo(struct net_driver_s *dev,
+                              tiva_can_fifo_t *fifo);
+static void tivacan_disable_mbox(struct net_driver_s *dev,
+                                 uint32_t iface_base,
+                                 int num);
+static int  tivacan_initfilter(struct net_driver_s *dev,
+                               tiva_can_fifo_t  *fifo,
+                               void             *filter,
+                               int               type);
+static void tivacan_receive_work(void *priv);
+int tivacan_handle_errors(struct net_driver_s *dev);
+
+static bool tivacan_txready(struct net_driver_s *dev);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+void tivacan_handle_errors_wqueue(void *dev);
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+/* NuttX callback functions */
+
+static int tivacan_ifup(struct net_driver_s *dev);
+static int tivacan_ifdown(struct net_driver_s *dev);
+
+static void tivacan_txavail_work(void *arg);
+static int tivacan_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int tivacan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                unsigned long arg);
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_TIVA_CAN0
+static struct tiva_can_s g_tivacan0priv =
+    {
+        .modnum          = 0,
+        .base            = TIVA_CAN_BASE(0),
+        .thd_iface_base  = TIVA_CAN_IFACE_BASE(0, 0),
+        .isr_iface_base  = TIVA_CAN_IFACE_BASE(0, 1),
+        .bifup           = false,  /* true:ifup false:ifdown */
+        .baud            = CONFIG_TIVA_CAN0_BAUD,
+        .rx_work_repeat  = 0,
+        .rx_mbox_index   = 0,
+    };
+
+#endif /* CONFIG_TIVA_CAN0 */
+
+#ifdef CONFIG_TIVA_CAN1
+static struct tiva_can_s g_tivacan1priv =
+    {
+        .modnum           = 1,
+        .base             = TIVA_CAN_BASE(1),
+        .thd_iface_base   = TIVA_CAN_IFACE_BASE(1, 0),
+        .isr_iface_base   = TIVA_CAN_IFACE_BASE(1, 1),
+        .bifup            = false,  /* true:ifup false:ifdown */
+        .baud             = CONFIG_TIVA_CAN1_BAUD,
+        .rx_work_repeat   = 0,
+        .rx_mbox_index    = 0,
+    };
+#endif /* CONFIG_TIVA_CAN1 */
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tivacan_receive_work
+ *
+ * Description: Worker callback for processing received messages
+ ****************************************************************************/
+
+static void tivacan_receive_work(void *priv)
+{
+  struct net_driver_s *dev    = (struct net_driver_s *)priv;
+  struct tiva_can_s   *canmod = dev->d_private;
+  struct can_frame    *frame  = (struct can_frame *)canmod->rx_pool;
+  uint8_t             *mbox   = &canmod->rx_mbox_index;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  int ret;
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* The while loop iteration looks for new data in all RX mailboxes.
+   * If a message has arrived while in the loop the canmod->rx_work_repeat
+   * should be set so that the reception loop is repeated.
+   */
+
+  while (canmod->rx_work_repeat)
+    {
+      canmod->rx_work_repeat = 0;
+      nxmutex_lock(&canmod->thd_iface_lock);
+
+      /* Process the received message(s). Since hardware RX FIFOS are used
+       * and new messages are received into the mailbox with the lowest
+       * number, we can't use CANINT since a new message might arrive in a
+       * mailbox we'd already read from before the higher-address mailboxes
+       * in the FIFO were emptied. (Besides, the ISR clears CANINT).
+       */
+
+      for (*mbox = 0;
+          *mbox < TIVA_CAN_NUM_MBOXES - CONFIG_TIVA_CAN_TX_FIFO_DEPTH;
+          ++*mbox)
+        {
+          uint32_t reg;
+
+          /* New Data registers */
+
+          reg = tivacan_get_nwda32(canmod->base);
+
+          if (!(reg & ~*(canmod->tx_fifo)))
+            {
+              /* No new messages to process in the RX FIFOs */
+
+              break;
+            }
+          else if (! (reg & 1 << *mbox))
+            {
+              /* No new message in this mailbox */
+
+              continue;
+            }
+
+          /* Command Mask register
+           * Pull arbitration bits, control bits, and data bits from the
+           * message SRAM. Clear the new data bit; the ISR cleared the IRQ.
+           */
+
+          reg =   TIVA_CANIF_CMSK_ARB       | TIVA_CANIF_CMSK_CONTROL
+              | TIVA_CANIF_CMSK_NEWDAT    | TIVA_CANIF_CMSK_DATAA
+              | TIVA_CANIF_CMSK_DATAB;
+          putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+          /* Submit request (1-indexed) */
+
+          putreg32(*mbox + 1,
+                   canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+          /* Wait for response */
+
+          while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+              & TIVA_CANIF_CRQ_BUSY);
+
+          /* ARB2 - upper chunk of the message ID, "direction," and extended
+           * message indication. (NOTE: sadly, DIR is always 0 (receive) even
+           * for RTR messages. There is no way to determine whether the RTR
+           * bit was set on a received frame.
+           */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+#ifdef CONFIG_NET_CAN_EXTID
+          uint8_t ext_id = (0 != (reg & TIVA_CANIF_ARB2_XTD));
+
+          if (ext_id)
+            {
+              frame->can_id = (reg & TIVA_CANIF_ARB2_ID_EXT_MASK)
+                      << TIVA_CANIF_ARB2_ID_EXT_PRESHIFT;
+
+              /* ARB1 - lower chunk of the message ID for extended IDs */
+
+              reg = getreg32(
+                  canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+              frame->can_id |= reg & TIVA_CANIF_ARB1_ID_EXT_MASK;
+            }
+          else
+            {
+              frame->can_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                      >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+            }
+#else
+          frame->can_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                  >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+#endif /* CONFIG_NET_CAN_EXTID */
+
+          /* Message Control - remote enable (RMTEN) & data length code */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+          frame->can_dlc = reg & TIVA_CANIF_MCTL_DLC_MASK;
+
+          /* Data registers - these are little endian but the uint8_t
+           * array in can_msg_s is big endian.
+           */
+
+          for (int j = 0; j < (frame->can_dlc + 1) / 2; ++j)
+            {
+              reg = getreg32(canmod->thd_iface_base
+                             + TIVA_CANIF_OFFSET_DATA(j));
+
+              frame->data[j * 2]      = (reg & TIVA_CANIF_DATA_HBYTE_MASK)
+                      >> TIVA_CANIF_DATA_HBYTE_SHIFT;
+              frame->data[j * 2 + 1]  = (reg & TIVA_CANIF_DATA_LBYTE_MASK)
+                      >> TIVA_CANIF_DATA_LBYTE_SHIFT;
+            }
+
+          /* Point the d_buf buffer to the received can frame */
+
+          dev->d_len = sizeof(struct can_frame);
+          dev->d_buf = (uint8_t *)frame;
+
+          NETDEV_RXPACKETS(dev);
+
+          /* Process the received frame */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+          ret = can_input(dev);
+#else
+          can_input(dev);
+#endif /* CONFIG_NET_CAN_ERRORS */
+        }
+
+      /* 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.
+       */
+
+      dev->d_buf = (uint8_t *)canmod->tx_pool;
+      nxmutex_unlock(&canmod->thd_iface_lock);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+      if (ret == OK)
+        {
+          /* Switch to processing errors by interrupt when the application
+           * is able to keep up with the message volume and messages are
+           * being received successfully.
+           */
+
+          work_cancel(CANWORK, &canmod->error_work);
+          modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+                      0, TIVA_CAN_CTL_SIE);
+        }
+#endif /* CONFIG_NET_CAN_ERRORS */
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+/****************************************************************************
+ * Name: tivacan_ioctl
+ *
+ * Description:
+ *   Perform the action requested by an ioctl syscall that was not handled by
+ *   the "upper half" CAN driver.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   cmd - One of the CANIOC_* ioctls
+ *   arg - Second argument to the ioctl call.
+ *
+ * Returned value:
+ *   Zero on success; a negated errno on failure
+ ****************************************************************************/
+
+static int tivacan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                unsigned long arg)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  int ret;
+
+  /* TODO: The IOCTL commands are not implemented yet. */
+
+#warning Missing logic
+
+  switch (cmd)
+  {
+    default:
+      canerr("ERROR: Unrecognized ioctl\n");
+      ret = -ENOSYS;
+      break;
+  }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Name: tivacan_send
+ *
+ * Description:
+ *   Enqueue a message to transmit into the hardware FIFO. Returns
+ *   immediately if the FIFO is full. This function must not fail when
+ *   tivacan_txready returns true, or the message will be lost.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   Zero on success; a negated errorno on failure.
+ ****************************************************************************/
+
+static int tivacan_send(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  struct can_frame *frame = (struct can_frame *)dev->d_buf;
+  uint32_t reg = 0;
+
+  /* REVISIT: Should can_txdone be called to advance the FIFO buffer
+   * head when dev_send is called when it shouldn't be?
+   */
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      NETDEV_TXDONE(canmod->dev);
+      return -ENETDOWN;
+    }
+
+  if (canmod->tx_fifo_tail >= CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+    {
+      NETDEV_TXDONE(canmod->dev);
+      return -EBUSY;
+    }
+
+  nxmutex_lock(&canmod->thd_iface_lock);
+
+  /* Protect the message object due the minute chance that the mailbox was

Review Comment:
   ```suggestion
     /* Protect the message object due to the minute chance that the mailbox was
   ```



##########
arch/arm/src/tiva/common/tiva_sock_can.c:
##########
@@ -0,0 +1,2371 @@
+/****************************************************************************
+ * arch/arm/src/tiva/common/tiva_sock_can.c
+ * SocketCAN driver implementation for Tiva. Based on the chardev driver.
+ *
+ * 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 <math.h>
+#include <nuttx/mutex.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 "tiva_can.h"
+#include "hardware/tiva_can.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only the last few mailboxes are used for TX to help ensure that responses
+ * to remote request frames we send are always collected by an RX FIFO and
+ * not by the mailbox used to send the remote frame. (The Tiva hardware uses
+ * the mailbox with the lowest number first.)
+ *
+ * This number is zero-indexed, althought the Command Request Register isn't.
+ */
+#define TIVA_CAN_TX_FIFO_START (TIVA_CAN_NUM_MBOXES - \
+        CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+
+/* Frequency of async error message reporting when rate-limited */
+#define TIVA_CAN_ERR_HANDLER_TICKS MSEC2TICK(CONFIG_TIVA_CAN_ERR_HANDLER_PER)
+
+/* For tivacan_initfilter */
+#define TIVA_CAN_FILTER_TYPE_STD 0
+#define TIVA_CAN_FILTER_TYPE_EXT 1
+
+/* Delays *******************************************************************/
+
+/* Defines for bit timing */
+#define CAN_BIT_QUANTA 20
+#define CAN_TRANSMISSION_DELAY 400E-9
+
+#if defined(CONFIG_TIVA_CAN) && (defined(CONFIG_TIVA_CAN0) || \
+        defined(CONFIG_TIVA_CAN1)) && defined(CONFIG_TIVA_SOCKET_CAN)
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#endif /* CONFIG_SCHED_WORKQUEUE */
+
+/* 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!
+ */
+

Review Comment:
   Hmmm., someone might not find this until after a gruesome debugging session... Maybe something like the following could save them that headache?
   
   ```suggestion
   #if !defined(CONFIG_SCHED_LPWORK)
   #  warning "SocketCAN will run on HPWORK queue (not recommended). Consider enabling LPWORK queue."
   #endif /* !CONFIG_SCHED_LPWORK */
   ```



##########
arch/arm/src/tiva/common/tiva_sock_can.c:
##########
@@ -0,0 +1,2371 @@
+/****************************************************************************
+ * arch/arm/src/tiva/common/tiva_sock_can.c
+ * SocketCAN driver implementation for Tiva. Based on the chardev driver.
+ *
+ * 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 <math.h>
+#include <nuttx/mutex.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 "tiva_can.h"
+#include "hardware/tiva_can.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only the last few mailboxes are used for TX to help ensure that responses
+ * to remote request frames we send are always collected by an RX FIFO and
+ * not by the mailbox used to send the remote frame. (The Tiva hardware uses
+ * the mailbox with the lowest number first.)
+ *
+ * This number is zero-indexed, althought the Command Request Register isn't.
+ */
+#define TIVA_CAN_TX_FIFO_START (TIVA_CAN_NUM_MBOXES - \
+        CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+
+/* Frequency of async error message reporting when rate-limited */
+#define TIVA_CAN_ERR_HANDLER_TICKS MSEC2TICK(CONFIG_TIVA_CAN_ERR_HANDLER_PER)
+
+/* For tivacan_initfilter */
+#define TIVA_CAN_FILTER_TYPE_STD 0
+#define TIVA_CAN_FILTER_TYPE_EXT 1
+
+/* Delays *******************************************************************/
+
+/* Defines for bit timing */
+#define CAN_BIT_QUANTA 20
+#define CAN_TRANSMISSION_DELAY 400E-9
+
+#if defined(CONFIG_TIVA_CAN) && (defined(CONFIG_TIVA_CAN0) || \
+        defined(CONFIG_TIVA_CAN1)) && defined(CONFIG_TIVA_SOCKET_CAN)
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#endif /* CONFIG_SCHED_WORKQUEUE */
+
+/* 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
+
+/* Combines two 16-bit registers into a single 32 bit value */
+#define tivacan_readsplitreg32(base, r1, r2) \
+        ((getreg32((base) + (r1)) & 0xffff) | (getreg32((base) + (r2)) << 16))
+
+#define tivacan_get_nwda32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_NWDA1,\
+                               TIVA_CAN_OFFSET_NWDA2)
+#define tivacan_get_txrq32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_TXRQ1,\
+                               TIVA_CAN_OFFSET_TXRQ2)
+#define tivacan_get_msgval32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_MSG1VAL,\
+                               TIVA_CAN_OFFSET_MSG2VAL)
+
+#define TIVA_CAN_AUTRECOVER
+
+/* Set the maximum number of iterations for the tivacan_bittiming_set timing
+ * parameters calculation
+ */
+#define TIVA_CAN_SET_TIMING_MAX_TER 5
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef uint32_t tiva_can_fifo_t;
+struct tiva_can_s
+{
+  /* Module number */
+
+  int       modnum;
+
+  /* Registers base address */
+
+  uint32_t  base;
+
+  /* Contents of the CANSTS reg the last time errors were processed. */
+
+  uint32_t  status;
+
+  /* Contents of the CANERR reg the last time errors were processed. */
+
+  uint32_t  errors;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  struct work_s error_work; /* For deferring error work to the work wq */
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* Configured baud rate */
+
+  uint32_t baud;
+
+  /* Interface registers base address for threads threads */
+
+  uint32_t  thd_iface_base;
+
+  /* Interface registers base address reserved exclusively for the ISR */
+
+  uint32_t  isr_iface_base;
+
+  /* Mutex for threads accessing the interface registers */
+
+  mutex_t   thd_iface_lock;
+
+  /* Mutex for claiming, freeing, and potentially resizing RX FIFOs.
+   * The TX FIFO should never be resized at runtime.
+   */
+
+  mutex_t   fifo_lock;
+
+  /* true:ifup false:ifdown */
+
+  bool                bifup;
+
+  /* Interface understood by the network */
+
+  struct net_driver_s dev;
+
+  struct work_s irqwork;  /* For deferring interrupt work to the wq */
+  struct work_s pollwork; /* For deferring poll work to the work wq */
+  struct work_s rxwork;   /* For deferring receive work to the work wq */
+
+  /* flag to repeat the loop getting data from all receive mailboxes */
+
+  uint8_t rx_work_repeat;
+
+  /* index of mailbox which is currently processed for data reception */
+
+  uint8_t rx_mbox_index;
+
+  /* List of all FIFOs. Contains the indices of used mailboxes for the
+   * appropriate FIFO.
+   */
+
+  tiva_can_fifo_t fifos[CONFIG_TIVA_CAN_FILTERS_MAX + 1];
+
+  /* Pointer to default catch-all RX FIFO initialized by the driver. */
+
+  tiva_can_fifo_t *rxdefault_fifo;
+
+  /* Pointer to the permanent, fixed-size TX FIFO. This is always located at
+   * the end of the series of mailboxes to help ensure that responses to
+   * remote request frames go to an RX (filter) FIFO and don't interfere.
+   */
+
+  tiva_can_fifo_t *tx_fifo;
+
+  /* Index in the TX FIFO where new messages should be added. The Tiva CAN
+   * module doesn't support a ring buffer, so new messages are only added
+   * when the TX FIFO is empty (tx_fifo_tail == 0).
+   */
+
+  int tx_fifo_tail;
+
+  /* NuttX interface TX/RX pool */
+
+  uint8_t tx_pool[sizeof(struct can_frame)];
+  uint8_t rx_pool[sizeof(struct can_frame)];
+};
+
+/* This structure represents the CAN bit timing settings as seen by the hw. */
+
+struct tiva_can_timing_s
+{
+  int       prescaler;
+  int       tseg1;
+  int       tseg2;
+  int       sjw;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* CAN error interrupts and ISR */
+
+static int  tivacan_isr(int irq, void *context, void *dev);
+
+/* Internal utility functions */
+
+static int tivacan_txpoll(struct net_driver_s *dev);
+static struct tiva_can_timing_s
+tivacan_bittiming_get(struct net_driver_s *dev);
+static void tivacan_bittiming_set(struct net_driver_s *dev,
+                                  uint32_t desired_baud_rate);
+
+static int tivacan_setup(struct net_driver_s *dev);
+static void tivacan_shutdown(struct net_driver_s *dev);
+static void tivacan_reset(struct net_driver_s *dev);
+static int tivacan_send(struct net_driver_s *dev);
+static int tivacan_alloc_fifo(struct net_driver_s *dev, int depth);
+static void tivacan_free_fifo(struct net_driver_s *dev,
+                              tiva_can_fifo_t *fifo);
+static void tivacan_disable_mbox(struct net_driver_s *dev,
+                                 uint32_t iface_base,
+                                 int num);
+static int  tivacan_initfilter(struct net_driver_s *dev,
+                               tiva_can_fifo_t  *fifo,
+                               void             *filter,
+                               int               type);
+static void tivacan_receive_work(void *priv);
+int tivacan_handle_errors(struct net_driver_s *dev);
+
+static bool tivacan_txready(struct net_driver_s *dev);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+void tivacan_handle_errors_wqueue(void *dev);
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+/* NuttX callback functions */
+
+static int tivacan_ifup(struct net_driver_s *dev);
+static int tivacan_ifdown(struct net_driver_s *dev);
+
+static void tivacan_txavail_work(void *arg);
+static int tivacan_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int tivacan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                unsigned long arg);
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_TIVA_CAN0
+static struct tiva_can_s g_tivacan0priv =
+    {
+        .modnum          = 0,
+        .base            = TIVA_CAN_BASE(0),
+        .thd_iface_base  = TIVA_CAN_IFACE_BASE(0, 0),
+        .isr_iface_base  = TIVA_CAN_IFACE_BASE(0, 1),
+        .bifup           = false,  /* true:ifup false:ifdown */
+        .baud            = CONFIG_TIVA_CAN0_BAUD,
+        .rx_work_repeat  = 0,
+        .rx_mbox_index   = 0,
+    };
+
+#endif /* CONFIG_TIVA_CAN0 */
+
+#ifdef CONFIG_TIVA_CAN1
+static struct tiva_can_s g_tivacan1priv =
+    {
+        .modnum           = 1,
+        .base             = TIVA_CAN_BASE(1),
+        .thd_iface_base   = TIVA_CAN_IFACE_BASE(1, 0),
+        .isr_iface_base   = TIVA_CAN_IFACE_BASE(1, 1),
+        .bifup            = false,  /* true:ifup false:ifdown */
+        .baud             = CONFIG_TIVA_CAN1_BAUD,
+        .rx_work_repeat   = 0,
+        .rx_mbox_index    = 0,
+    };
+#endif /* CONFIG_TIVA_CAN1 */
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tivacan_receive_work
+ *
+ * Description: Worker callback for processing received messages
+ ****************************************************************************/
+
+static void tivacan_receive_work(void *priv)
+{
+  struct net_driver_s *dev    = (struct net_driver_s *)priv;
+  struct tiva_can_s   *canmod = dev->d_private;
+  struct can_frame    *frame  = (struct can_frame *)canmod->rx_pool;
+  uint8_t             *mbox   = &canmod->rx_mbox_index;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  int ret;
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* The while loop iteration looks for new data in all RX mailboxes.
+   * If a message has arrived while in the loop the canmod->rx_work_repeat
+   * should be set so that the reception loop is repeated.
+   */
+
+  while (canmod->rx_work_repeat)
+    {
+      canmod->rx_work_repeat = 0;
+      nxmutex_lock(&canmod->thd_iface_lock);
+
+      /* Process the received message(s). Since hardware RX FIFOS are used
+       * and new messages are received into the mailbox with the lowest
+       * number, we can't use CANINT since a new message might arrive in a
+       * mailbox we'd already read from before the higher-address mailboxes
+       * in the FIFO were emptied. (Besides, the ISR clears CANINT).
+       */
+
+      for (*mbox = 0;
+          *mbox < TIVA_CAN_NUM_MBOXES - CONFIG_TIVA_CAN_TX_FIFO_DEPTH;
+          ++*mbox)
+        {
+          uint32_t reg;
+
+          /* New Data registers */
+
+          reg = tivacan_get_nwda32(canmod->base);
+
+          if (!(reg & ~*(canmod->tx_fifo)))
+            {
+              /* No new messages to process in the RX FIFOs */
+
+              break;
+            }
+          else if (! (reg & 1 << *mbox))
+            {
+              /* No new message in this mailbox */
+
+              continue;
+            }
+
+          /* Command Mask register
+           * Pull arbitration bits, control bits, and data bits from the
+           * message SRAM. Clear the new data bit; the ISR cleared the IRQ.
+           */
+
+          reg =   TIVA_CANIF_CMSK_ARB       | TIVA_CANIF_CMSK_CONTROL
+              | TIVA_CANIF_CMSK_NEWDAT    | TIVA_CANIF_CMSK_DATAA
+              | TIVA_CANIF_CMSK_DATAB;
+          putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+          /* Submit request (1-indexed) */
+
+          putreg32(*mbox + 1,
+                   canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+          /* Wait for response */
+
+          while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+              & TIVA_CANIF_CRQ_BUSY);
+
+          /* ARB2 - upper chunk of the message ID, "direction," and extended
+           * message indication. (NOTE: sadly, DIR is always 0 (receive) even
+           * for RTR messages. There is no way to determine whether the RTR
+           * bit was set on a received frame.
+           */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+#ifdef CONFIG_NET_CAN_EXTID
+          uint8_t ext_id = (0 != (reg & TIVA_CANIF_ARB2_XTD));
+
+          if (ext_id)
+            {
+              frame->can_id = (reg & TIVA_CANIF_ARB2_ID_EXT_MASK)
+                      << TIVA_CANIF_ARB2_ID_EXT_PRESHIFT;
+
+              /* ARB1 - lower chunk of the message ID for extended IDs */
+
+              reg = getreg32(
+                  canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+              frame->can_id |= reg & TIVA_CANIF_ARB1_ID_EXT_MASK;
+            }
+          else
+            {
+              frame->can_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                      >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+            }
+#else
+          frame->can_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                  >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+#endif /* CONFIG_NET_CAN_EXTID */
+
+          /* Message Control - remote enable (RMTEN) & data length code */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+          frame->can_dlc = reg & TIVA_CANIF_MCTL_DLC_MASK;
+
+          /* Data registers - these are little endian but the uint8_t
+           * array in can_msg_s is big endian.
+           */
+
+          for (int j = 0; j < (frame->can_dlc + 1) / 2; ++j)
+            {
+              reg = getreg32(canmod->thd_iface_base
+                             + TIVA_CANIF_OFFSET_DATA(j));
+
+              frame->data[j * 2]      = (reg & TIVA_CANIF_DATA_HBYTE_MASK)
+                      >> TIVA_CANIF_DATA_HBYTE_SHIFT;
+              frame->data[j * 2 + 1]  = (reg & TIVA_CANIF_DATA_LBYTE_MASK)
+                      >> TIVA_CANIF_DATA_LBYTE_SHIFT;
+            }
+
+          /* Point the d_buf buffer to the received can frame */
+
+          dev->d_len = sizeof(struct can_frame);
+          dev->d_buf = (uint8_t *)frame;
+
+          NETDEV_RXPACKETS(dev);
+
+          /* Process the received frame */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+          ret = can_input(dev);
+#else
+          can_input(dev);
+#endif /* CONFIG_NET_CAN_ERRORS */
+        }
+
+      /* 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.
+       */
+
+      dev->d_buf = (uint8_t *)canmod->tx_pool;
+      nxmutex_unlock(&canmod->thd_iface_lock);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+      if (ret == OK)
+        {
+          /* Switch to processing errors by interrupt when the application
+           * is able to keep up with the message volume and messages are
+           * being received successfully.
+           */
+
+          work_cancel(CANWORK, &canmod->error_work);
+          modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+                      0, TIVA_CAN_CTL_SIE);
+        }
+#endif /* CONFIG_NET_CAN_ERRORS */
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+/****************************************************************************
+ * Name: tivacan_ioctl
+ *
+ * Description:
+ *   Perform the action requested by an ioctl syscall that was not handled by
+ *   the "upper half" CAN driver.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   cmd - One of the CANIOC_* ioctls
+ *   arg - Second argument to the ioctl call.
+ *
+ * Returned value:
+ *   Zero on success; a negated errno on failure
+ ****************************************************************************/
+
+static int tivacan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                unsigned long arg)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  int ret;
+
+  /* TODO: The IOCTL commands are not implemented yet. */
+
+#warning Missing logic
+
+  switch (cmd)
+  {
+    default:
+      canerr("ERROR: Unrecognized ioctl\n");
+      ret = -ENOSYS;
+      break;
+  }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Name: tivacan_send
+ *
+ * Description:
+ *   Enqueue a message to transmit into the hardware FIFO. Returns
+ *   immediately if the FIFO is full. This function must not fail when
+ *   tivacan_txready returns true, or the message will be lost.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   Zero on success; a negated errorno on failure.
+ ****************************************************************************/
+
+static int tivacan_send(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  struct can_frame *frame = (struct can_frame *)dev->d_buf;
+  uint32_t reg = 0;
+
+  /* REVISIT: Should can_txdone be called to advance the FIFO buffer
+   * head when dev_send is called when it shouldn't be?
+   */
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      NETDEV_TXDONE(canmod->dev);
+      return -ENETDOWN;
+    }
+
+  if (canmod->tx_fifo_tail >= CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+    {
+      NETDEV_TXDONE(canmod->dev);
+      return -EBUSY;
+    }
+
+  nxmutex_lock(&canmod->thd_iface_lock);
+
+  /* Protect the message object due the minute chance that the mailbox was
+   * previously used for a remote frame and could receive messages, causing
+   * an SRAM conflict.
+   */
+
+  tivacan_disable_mbox(dev, canmod->thd_iface_base,
+                       TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail);
+
+  /* Command mask register */
+
+  reg = TIVA_CANIF_CMSK_WRNRD | TIVA_CANIF_CMSK_ARB | TIVA_CANIF_CMSK_CONTROL
+      | TIVA_CANIF_CMSK_CLRINTPND
+      | TIVA_CANIF_CMSK_DATAA | TIVA_CANIF_CMSK_DATAB;
+  putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+  /* Arbitration registers (ARB1 and ARB2)
+   * Check if the frame is RTR
+   */
+
+  uint8_t rtr = frame->can_id & CAN_RTR_FLAG;
+  frame->can_id &= ~CAN_RTR_FLAG;
+
+#ifdef CONFIG_NET_CAN_EXTID
+  if (frame->can_id & CAN_EFF_FLAG)
+    {
+      frame->can_id &= ~CAN_EFF_FLAG;
+
+      reg = frame->can_id & TIVA_CANIF_ARB1_ID_EXT_MASK;
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+      reg = (frame->can_id >> TIVA_CANIF_ARB2_ID_EXT_PRESHIFT)
+              & TIVA_CANIF_ARB2_ID_EXT_MASK;
+      reg |= TIVA_CANIF_ARB2_MSGVAL | TIVA_CANIF_ARB2_XTD;
+
+#ifdef CONFIG_CAN_USE_RTR
+      reg |= (rtr ? 0 : TIVA_CANIF_ARB2_DIR);
+#else
+      reg |= TIVA_CANIF_ARB2_DIR;
+#endif /* CONFIG_CAN_USE_RTR */
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+    }
+  else
+    {
+#endif /* CONFIG_NET_CAN_EXTID */
+      reg = (frame->can_id << TIVA_CANIF_ARB2_ID_STD_SHIFT)
+          & TIVA_CANIF_ARB2_ID_STD_MASK;
+
+      reg |= TIVA_CANIF_ARB2_MSGVAL;
+
+#ifdef CONFIG_CAN_USE_RTR
+      reg |= (rtr ? 0 : TIVA_CANIF_ARB2_DIR);
+#else
+      reg |= TIVA_CANIF_ARB2_DIR;
+#endif /* CONFIG_CAN_USE_RTR */
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+#ifdef CONFIG_NET_CAN_EXTID
+    }
+#endif /* CONFIG_NET_CAN_EXTID */
+
+  /* Message Control (MCTL) register */
+
+  reg = frame->can_dlc & TIVA_CANIF_MCTL_DLC_MASK;
+
+  /* Protect the mailbox from rx messages; see comment in tivacan_setup */
+
+  reg |= TIVA_CANIF_MCTL_UMASK;
+
+  /* NOTE: Not sure whether the End Of Buffer bit means anything here */
+
+  reg |= TIVA_CANIF_MCTL_NEWDAT | TIVA_CANIF_MCTL_TXIE
+      | TIVA_CANIF_MCTL_TXRQST | TIVA_CANIF_MCTL_EOB;
+  putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+
+  /* Data registers, there are four of them. */
+
+  for (int i = 0; i < (frame->can_dlc + 1) / 2; ++i)
+    {
+      reg = (uint32_t)frame->data[i * 2]
+                                  << TIVA_CANIF_DATA_HBYTE_SHIFT;
+      reg |= (uint32_t)frame->data[i * 2 + 1]
+                                   << TIVA_CANIF_DATA_LBYTE_SHIFT;
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_DATA(i));
+    }
+
+  /* Submit request; this register is one-indexed. */
+
+  putreg32(TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail + 1,
+           canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+  /* Wait for completion */
+
+  while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+      & TIVA_CANIF_CRQ_BUSY);
+
+  nxmutex_unlock(&canmod->thd_iface_lock);
+
+  /* Move to the next message in the h/w TX FIFO.
+   * Tell the upper-half the message has been submitted... this recurses
+   * back on us until the software TX FIFO is empty.
+   */
+
+  ++canmod->tx_fifo_tail;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: tivacan_txready
+ *
+ * Description:
+ *   Determine whether the hardware is ready to transmit another TX message.
+ *   This checks for bus-off conditions as well as if the FIFO is full.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   True if the hardware can accept another TX message, false otherwise.
+ ****************************************************************************/
+
+static bool tivacan_txready(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      return false;
+    }
+
+  return (canmod->tx_fifo_tail < CONFIG_TIVA_CAN_TX_FIFO_DEPTH);
+}
+
+/****************************************************************************
+ * Name: tivacan_handle_errors_wqueue
+ *
+ * Description:
+ *   Periodically handle errors when status interrupts are disabled due to
+ *   overload. Only applicable when CAN error reporting is enabled.
+ *
+ *   Since error messages are processed directly in the ISR, it is easy to
+ *   overwhelm the application with error messages. If tivacan_handle_errors
+ *   fails to send an error message in the ISR, status interrupts are
+ *   disabled and the ISR adds this function to the work queue.
+ *
+ *   On the other hand, when a message is successfully sent, or successfully
+ *   passes through the filters and is received by the application, the ISR
+ *   or the RX kthread will call work_cancel and re-enable status interrupts
+ *   if they are disabled.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver (typed as
+ *         void* for compatibility with worker_t.
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_CAN_ERRORS
+void tivacan_handle_errors_wqueue(void *dev)
+{
+  irqstate_t flags;
+  struct tiva_can_s *canmod = ((struct net_driver_s *)dev)->d_private;
+
+  flags = enter_critical_section();
+  tivacan_handle_errors((struct net_driver_s *)dev);
+  work_queue(LPWORK,
+             &canmod->error_work,
+             tivacan_handle_errors_wqueue,
+             dev,
+             TIVA_CAN_ERR_HANDLER_TICKS);
+  leave_critical_section(flags);
+}
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+/****************************************************************************
+ * Name: tivacan_handle_errors
+ *
+ * Description:
+ *   Process everything in the status register; i.e. initiate bus-off
+ *   recovery and send error messages if desired.  This function may be
+ *   called from either the main ISR or (when error messages are desired but
+ *   the application is unable to handle the volume of errors) from the
+ *   high-priority work queue. However, interrupts must be disabled when
+ *   called from the work queue context to ensure that error messages are
+ *   sent to the application in the correct order.
+ *
+ *   An IRQ is always raised for bus-off and error counter "warning" level,
+ *   so this function may be executed from the ISR while still waiting in
+ *   the work queue. In this case, the call to work_queue in the ISR
+ *   will just reset the timer, which is fine.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   1 if an error message was successfully sent to the application, the
+ *   return value of can_receive() (a negated errno) if it failed, and
+ *   zero if no error message was needed.
+ ****************************************************************************/
+
+int tivacan_handle_errors(struct net_driver_s *dev)
+{
+  uint32_t cansts;
+  uint32_t canerr;
+  struct tiva_can_s *canmod = dev->d_private;
+  struct can_frame    *frame  = (struct can_frame *)canmod->rx_pool;
+
+  int      ret = 0;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  int      errcnt;
+  int      prev_errcnt;
+
+  memset(frame, 0, sizeof(struct can_frame));
+  frame->can_dlc = CAN_ERR_DLC;
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* Clear the status interrupt by reading CANSTS */
+
+  cansts = getreg32(canmod->base + TIVA_CAN_OFFSET_STS);
+
+  /* Get the contents of the error counter register */
+
+  canerr = getreg32(canmod->base + TIVA_CAN_OFFSET_ERR);
+
+  /* Bus-off handling
+   * This is a new bus-off event. Proceed with error processing (if error
+   * reporting is enabled) so that the cause of the "last straw" error is
+   * included with the bus-off error message sent to the app.
+   * Clear the INIT bit if autorecovery is enabled.
+   */
+
+  if (cansts & TIVA_CAN_STS_BOFF
+      && ! (canmod->status & TIVA_CAN_STS_BOFF))
+    {
+#ifdef TIVA_CAN_AUTRECOVER
+      modreg32(0, TIVA_CAN_CTL_INIT,
+               canmod->base + TIVA_CAN_OFFSET_CTL);
+#endif /* TIVA_CAN_AUTRECOVER */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+      frame->can_id = CAN_ERR_BUSOFF;
+#endif /* CONFIG_NET_CAN_ERRORS */
+    }
+#ifdef CONFIG_NET_CAN_ERRORS
+
+  /* If this an existing bus-off event and the module is recovering, don't do
+   * anything. The interrupt is caused by the Last Error Code being updated
+   * with a bit-0 error to indicate that the module is recovering.
+   */
+
+  else if (cansts & TIVA_CAN_STS_BOFF
+      && canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      goto save_regs_and_return;
+    }
+
+  /* The module has just finished recovering from bus-off; indicate this to
+   * the app. If the LEC is a BIT0 error it must be ignored (see below).
+   * We still check for other errors though because if we're running from the
+   * work queue, the module could have recovered and then immediately
+   * encountered another error without us being notified in between.
+   */
+
+  else if (! (cansts & TIVA_CAN_STS_BOFF)
+      && canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      frame->can_id = CAN_ERR_RESTARTED;
+    }
+
+  /* Lost arbitration detection */
+
+  if (tivacan_get_txrq32(canmod->base) & ~tivacan_get_nwda32(canmod->base)
+  && cansts & TIVA_CAN_STS_RXOK)
+    {
+      /* Received a message successfually but sending a message failed */
+
+      frame->can_id |= CAN_ERR_LOSTARB;
+    }
+
+  /* Electrical fault/framing errors ****************************************/
+
+  /* This is not a bus-off event, or it is a brand-new bus-off event
+   * that we need to indicate the cause of the error for.
+   */
+
+  switch (canmod->status & TIVA_CAN_STS_LEC_MASK)
+  {
+    case TIVA_CAN_STS_LEC_NONE:
+
+      /* Skip unnecessary comparisons for the most common case */
+
+      break;
+
+    case TIVA_CAN_STS_LEC_STUFF:
+      {
+        frame->can_id  |= CAN_ERR_PROT_BIT0;
+        frame->data[2]    = CAN_ERR_PROT_STUFF;
+      }
+      break;
+    case TIVA_CAN_STS_LEC_FORM:
+      {
+        frame->can_id  |= CAN_ERR_PROT;
+        frame->data[2]    = CAN_ERR_PROT_FORM;
+      }
+      break;
+    case TIVA_CAN_STS_LEC_ACK:
+      {
+        frame->can_id |= CAN_ERR_ACK | CAN_ERR_PROT;
+        frame->data[2]    = CAN_ERR_PROT_TX;
+      }
+      break;
+    case TIVA_CAN_STS_LEC_BIT1:
+      {
+        frame->can_id  |=  CAN_ERR_PROT;
+        frame->data[2]     =  CAN_ERR_PROT_BIT | CAN_ERR_PROT_BIT1
+            | CAN_ERR_PROT_TX;
+      }
+      break;
+    case TIVA_CAN_STS_LEC_BIT0:
+      {
+        /* Note: this error code is set every time there is a sequence
+         * of 11 recessive bits during bus-off recovery, the LEC is not
+         * checked in that case.
+         */
+
+        frame->can_id |=  CAN_ERR_PROT;
+        frame->data[2]    =  CAN_ERR_PROT_BIT | CAN_ERR_PROT_BIT0
+            | CAN_ERR_PROT_TX;
+      }
+      break;
+    case TIVA_CAN_STS_LEC_CRC:
+      {
+        frame->can_id  |= CAN_ERR_PROT;
+        frame->data[2]    = CAN_ERR_PROT_UNSPEC;
+        frame->data[3]    = CAN_ERR_PROT_LOC_CRC_SEQ;
+      }
+      break;
+    default:
+      {
+        /* Possible other LEC values:
+         * TIVA_CAN_STS_LEC_NONE: normal conditions, including lost
+         *                        arbitration; no action required.
+         * TIVA_CAN_STS_NOEVENT:  possible if running in the work queue;
+         *                        no action required.
+         * Something else:        should never happen
+         */
+      }
+  }
+
+  /* Error counters
+   * --------------
+   * Rules for sending error messages to the application for error counters:
+   * (1) Reaching the warning or passive level on their own don't generate
+   *     error messages, but the information will be included in the error
+   *     when an actual event has occurred.
+   * (2) The application will be notified if the error goes away with an
+   *     error message of type CAN_ERROR_RESTARTED.
+   */
+
+  errcnt = (canerr & (TIVA_CAN_ERR_RP | TIVA_CAN_ERR_REC_MASK))
+      >> TIVA_CAN_ERR_REC_SHIFT;
+  prev_errcnt = (canmod->errors
+      & (TIVA_CAN_ERR_RP | TIVA_CAN_ERR_REC_MASK))
+          >> TIVA_CAN_ERR_REC_SHIFT;
+
+  if (errcnt >= 96)
+    {
+      if (errcnt >= 128)
+        {
+          frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+        }
+      else if (prev_errcnt >= 128)
+        {
+          frame->can_id |= CAN_ERR_RESTARTED;
+        }
+
+      if (frame->can_id)
+        {
+          frame->can_id |= CAN_ERR_CRTL;
+          frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+        }
+    }
+  else if (prev_errcnt >= 96)
+    {
+      frame->can_id |= CAN_ERR_RESTARTED;
+    }
+
+  errcnt = (canerr & TIVA_CAN_ERR_TEC_MASK) >> TIVA_CAN_ERR_TEC_SHIFT;
+  prev_errcnt = (canmod->errors & TIVA_CAN_ERR_TEC_MASK)
+                                        >> TIVA_CAN_ERR_TEC_SHIFT;
+
+  if (errcnt >= 96)
+    {
+      if (errcnt >= 128)
+        {
+          frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+        }
+      else if (prev_errcnt >= 128)
+        {
+          frame->can_id |= CAN_ERR_RESTARTED;
+        }
+
+      if (frame->can_id)
+        {
+          frame->can_id |= CAN_ERR_CRTL;
+          frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+        }
+    }
+  else if (prev_errcnt >= 96)
+    {
+      frame->can_id |= CAN_ERR_RESTARTED;
+    }
+
+  /* Forwarding an error message to the app */
+
+  if (frame->can_id)
+    {
+      /* If there was a good reason to send an error message, send it */
+
+      dev->d_len = sizeof(struct can_frame);
+      dev->d_buf = (uint8_t *)frame;
+
+      NETDEV_RXERRORS(dev);
+      ret = can_input(dev);
+
+      dev->d_buf = (uint8_t *)canmod->tx_pool;
+      /* Differentiate between "error message successfully sent" and
+       * "no error message required" for the caller.
+       */
+
+      if (ret >= 0)
+        {
+          ret = 1;
+        }
+    }
+
+  /* Reset the Last Error Code and TXOK/RXOK bits */
+
+  modreg32(TIVA_CAN_STS_LEC_NOEVENT,
+           TIVA_CAN_STS_LEC_MASK | TIVA_CAN_STS_TXOK | TIVA_CAN_STS_RXOK,
+           canmod->base + TIVA_CAN_OFFSET_STS);
+
+  save_regs_and_return:
+#endif /* CONFIG_CAN_ERRORS */
+
+  /* Save contents of CANSTS and CANERR for other functions to use
+   * and for comparison next time
+   */
+
+  canmod->status = cansts;
+  canmod->errors = canerr;
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: tivacan_isr
+ *
+ * Description:
+ *   ISR executed for all interrupts generated by the CAN module.
+ *
+ *   This ISR is registered in tivacan_setup.
+ *
+ * Returned value: Zero on success, negated errno if an error is encountered.
+ ****************************************************************************/
+
+static int  tivacan_isr(int irq, void *context, void *dev)
+{
+  struct tiva_can_s *canmod = ((struct net_driver_s *)dev)->d_private;
+  uint32_t canint;
+  int      ret = OK;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  uint32_t reg;
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  while ((canint = getreg32(canmod->base + TIVA_CAN_OFFSET_INT)
+      & TIVA_CAN_INT_INTID_MASK))
+    {
+      if (canint == TIVA_CAN_INT_INTID_STATUS)
+        {
+          ret = tivacan_handle_errors((struct net_driver_s *)dev);
+#ifdef CONFIG_NET_CAN_ERRORS
+          if (ret < 0)
+            {
+              /* We are spamming the application and it can't keep up...
+               * disable status interrupts and send error messages
+               * periodically.
+               */
+
+              modreg32(0, TIVA_CAN_CTL_SIE,
+                       canmod->base + TIVA_CAN_OFFSET_CTL);
+              work_queue(CANWORK,
+                         &canmod->error_work,
+                         tivacan_handle_errors_wqueue,
+                         dev,
+                         TIVA_CAN_ERR_HANDLER_TICKS);
+            }
+
+#endif /* CONFIG_NET_CAN_ERRORS */
+          continue;
+        }
+      else
+        {
+          /* Note: the datasheet incorrectly states "A message interrupt is
+           * cleared by clearing the message object's INTPND bit in the
+           * CANIFnMCTL register or by reading the CAN Status (CANSTS)
+           * register."
+           *
+           * In fact, a message interrupt can only be cleared via the
+           * interface registers. Here, we use the "shortcut" in the
+           * command mask register to avoid a read-modify-write of MCTL.
+           */
+
+          putreg32(TIVA_CANIF_CMSK_CLRINTPND,
+                   canmod->isr_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+          /* Submit request and wait for completion */
+
+          putreg32(canint, canmod->isr_iface_base + TIVA_CANIF_OFFSET_CRQ);
+          while (getreg32(canmod->isr_iface_base + TIVA_CANIF_OFFSET_CRQ)
+              & TIVA_CANIF_CRQ_BUSY);
+
+          if (canint >= TIVA_CAN_TX_FIFO_START + 1)
+            {
+              /* tx_fifo_tail contains the index of the next mailbox in the
+               * hardware TX FIFO; i.e. where the next message will be put.
+               * It is always one more than the last message to be added to
+               * the FIFO, so even though canint is one-indexed and
+               * TIVA_CAN_TX_FIFO_START is zero-indexed we do not add 1 here.
+               */
+
+              if (canint == TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail)
+                {
+                  /* Only wake up the write thread when we've emptied the
+                   * FIFO; the Tiva CAN module doesn't support a TX ring
+                   * buffer.
+                   */
+
+                  can_txready(dev);
+                  canmod->tx_fifo_tail = 0;
+                }
+
+#ifdef CONFIG_NET_CAN_ERRORS
+              reg = getreg32(canmod->base + TIVA_CAN_OFFSET_CTL);
+
+              if (! (reg & TIVA_CAN_CTL_SIE))
+                {
+                  /* The application is not overwhelmed (it was able to call
+                   * write()) and the message was successfully sent.
+                   * Re-enable status interrupts to immediately notify of
+                   * e.g. lost arbitration.
+                   */
+
+                  work_cancel(CANWORK, &canmod->error_work);
+                  putreg32(reg | TIVA_CAN_CTL_SIE,
+                           canmod->base + TIVA_CAN_OFFSET_CTL);
+                }
+#endif /* CONFIG_NET_CAN_ERRORS */
+            }
+          else
+            {
+              /* Repeat the message retrieval work if blocked
+               * to loop through all the mailboxes again once
+               * it finishes its current iteration.
+               */
+
+              if (canmod->rx_work_repeat == 0
+                  || canint - 1 < canmod->rx_mbox_index)
+                {
+                  canmod->rx_work_repeat = 1;
+                  if (work_available(&canmod->rxwork))
+                    {
+                      work_queue(CANWORK,
+                                 &canmod->rxwork,
+                                 tivacan_receive_work,
+                                 dev,
+                                 0);
+                    }
+                }
+            }
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: tivacan_bittiming_get
+ *
+ * Description:
+ *   Get the CAN bit timing parameters from the hardware (prescaler, TSEG1,
+ *   TSEG2, and SJW). The values returned are the actual values in time
+ *   quanta, not the values stored in the register (which are off by one).
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   struct tiva_can_timing_s containing the four parameters listed above.
+ ****************************************************************************/
+
+static struct tiva_can_timing_s
+tivacan_bittiming_get(struct net_driver_s *dev)
+{
+  struct tiva_can_timing_s timing;
+  uint32_t canbase = ((struct tiva_can_s *)dev->d_private)->base;
+
+  uint32_t canbit;
+  uint32_t brpe;
+  canbit = getreg32(canbase + TIVA_CAN_OFFSET_BIT);
+  brpe   = getreg32(canbase + TIVA_CAN_OFFSET_BRPE);
+
+  timing.prescaler = (canbit & TIVA_CAN_BIT_BRP_MASK)
+      >> TIVA_CAN_BIT_BRP_SHIFT;
+  timing.prescaler |= ((brpe & TIVA_CAN_BRPE_BRPE_MASK)
+      >> TIVA_CAN_BRPE_BRPE_SHIFT)
+          << TIVA_CAN_BIT_BRP_LENGTH;
+
+  timing.tseg1 = (canbit & TIVA_CAN_BIT_TSEG1_MASK)
+      >> TIVA_CAN_BIT_TSEG1_SHIFT;
+  timing.tseg2 = (canbit & TIVA_CAN_BIT_TSEG2_MASK)
+      >> TIVA_CAN_BIT_TSEG2_SHIFT;
+  timing.sjw   = (canbit & TIVA_CAN_BIT_SJW_MASK)
+      >> TIVA_CAN_BIT_SJW_SHIFT;
+
+  /* Values stored in registers are 1 less than the values used */
+
+  ++timing.prescaler;
+  ++timing.tseg1;
+  ++timing.tseg2;
+  ++timing.sjw;
+
+  return timing;
+}
+
+/****************************************************************************
+ * Name: tivacan_alloc_fifo
+ *
+ * Description:
+ *   Try to allocate a FIFO of the specified depth in the CAN module SRAM.
+ *   (FIFOs in the Tiva CAN modules don't need to be contiguous)
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *   depth - The desired FIFO depth to allocate
+ *
+ * Return value: The index of a tiva_can_fifo_t in dev->cd_priv which
+ *               contains a bit-field of the mailboxes that may be
+ *               used, -ENOSPC if there is not enough room in the CAN module
+ *               SRAM (i.e. too many FIFOs already allocated), or -ENOMEM if
+ *               the limit CONFIG_TIVA_CAN_FILTERS_MAX has been reached.
+ ****************************************************************************/
+
+static int tivacan_alloc_fifo(struct net_driver_s *dev, int depth)
+{
+  tiva_can_fifo_t claimed = 0;
+  int i;
+  int numclaimed = 0;
+  int free_fifo_idx = -1;
+  struct tiva_can_s *canmod = dev->d_private;
+
+  nxmutex_lock(&canmod->fifo_lock);
+
+  /* Mailboxes allocated other RX FIFOs or the TX FIFO */
+
+  for (i = 0; i < CONFIG_TIVA_CAN_FILTERS_MAX + 1; ++i)
+    {
+      if (canmod->fifos[i] == 0)
+        {
+          free_fifo_idx = i;
+        }
+
+      claimed |= canmod->fifos[i];
+    }
+
+  if (free_fifo_idx < 0)
+    {
+      canwarn("Max number of filters allocated.\n");
+      return -ENOMEM;
+    }
+
+  /* Mailboxes that are available to be allocated to this FIFO */
+
+  claimed = ~claimed;
+
+  for (i = 0; i < (TIVA_CAN_NUM_MBOXES - CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+  && numclaimed < depth; ++i)
+    {
+      if (claimed & 1 << i)
+        {
+          ++numclaimed;
+        }
+    }
+
+  if (numclaimed != depth)
+    {
+      nxmutex_unlock(&canmod->fifo_lock);
+      return -ENOSPC;
+    }
+  else
+    {
+      /* Clear the bits for mailboxes we aren't claiming right now. */
+
+      claimed &= 0xffffffff >> (32 - i);
+      canmod->fifos[free_fifo_idx] = claimed;
+
+      nxmutex_unlock(&canmod->fifo_lock);
+      return free_fifo_idx;
+    }
+}
+
+/****************************************************************************
+ * Name: tivacan_free_fifo
+ *
+ * Description:
+ *   Disable all the mailboxes in the specified FIFO and make them
+ *   available for use by another filter. This function waits for pending
+ *   transmissions to complete (for the TX FIFO) or new messages to be
+ *   read (for RX filter FIFOs) before disabling a mailbox.
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *   fifo - The FIFO to free
+ *
+ * Return value: None
+ ****************************************************************************/
+
+static void tivacan_free_fifo(struct net_driver_s *dev,
+                              tiva_can_fifo_t *fifo)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  nxmutex_lock(&canmod->thd_iface_lock);
+
+  for (int i = 0; i < TIVA_CAN_NUM_MBOXES; ++i)
+    {
+      if (*fifo & 1 << i)
+        {
+          tivacan_disable_mbox(dev, canmod->thd_iface_base, i);
+
+          *fifo &= ~(1 << i);
+        }
+    }
+
+  nxmutex_unlock(&canmod->thd_iface_lock);
+}
+
+/****************************************************************************
+ * Name: tivacan_disable_mbox
+ *
+ * Description:
+ *   Disable a mailbox by clearing the MSGVAL bit. This function
+ *   doesn't do any checks before disabling it. Don't call it without
+ *   checking whether there's a pending message in the object! It takes the
+ *   raw base interface register base address, so it can be called from
+ *   thread or ISR context. Zeros out the entire ARB2 register.
+ *
+ *   This function guarantees that the mailbox is actually disabled by
+ *   reading back the value of the register. As briefly mentioned in the
+ *   datasheet and further explained on the TI forums [1], writes to the
+ *   message RAM can be lost if the CAN peripheral is performing a
+ *   read-modify-write operation (i.e. actively receiving a CAN message).
+ *
+ *   Yes, the thread is for the C2000 microcontrollers but the CAN peripheral
+ *   appears to be very similar. However, this approach accomplishes the same
+ *   thing and doesn't take the risk of missing a CAN message by setting the
+ *   INIT bit as suggested in the forum.
+ *
+ *   This is a driver-internal utility function.
+ *
+ *   [1] https://e2e.ti.com/support/microcontrollers/c2000/f/171/t/916169
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *   iface_base - The interface registers base address to use
+ *   num - The mailbox number to disable (zero-indexed)
+ *
+ * Return value: none
+ ****************************************************************************/
+
+static void tivacan_disable_mbox(struct net_driver_s *dev,
+                                 uint32_t iface_base,
+                                 int num)
+{
+  uint32_t reg;
+  struct tiva_can_s *canmod = dev->d_private;
+
+  while (true)
+    {
+      /* Read (back) the ARB2 register. Our write could have been lost if we
+       * caught the peripheral in the middle of a read-modify-write operation
+       * with a received message.
+       */
+
+      reg = tivacan_readsplitreg32(canmod->base, TIVA_CAN_OFFSET_MSG1VAL,
+                                   TIVA_CAN_OFFSET_MSG2VAL);
+      if (!(reg & 1 << num))
+        {
+          break;
+        }
+
+      /* Same as before, but select a write (WRNRD) */
+
+      putreg32(TIVA_CANIF_CMSK_WRNRD | TIVA_CANIF_CMSK_ARB,
+               iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+      /* Mark mailbox as invalid */
+
+      reg &= ~TIVA_CANIF_ARB2_MSGVAL;
+      putreg32(0, iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+      /* Submit request & wait for completion */
+
+      putreg32(num + 1, iface_base + TIVA_CANIF_OFFSET_CRQ);
+      while (getreg32(iface_base + TIVA_CANIF_OFFSET_CRQ)
+          & TIVA_CANIF_CRQ_BUSY);
+    }
+}
+
+/****************************************************************************
+ * Name: tivacan_initfilter
+ *
+ * Description:
+ *   Initialize the mailboxes in the specified FIFO to store messages
+ *   matching the given filter. On return, the FIFO will generate interrupts
+ *   on matching messages (if the CAN hardware is initialized).
+ *
+ *   Note that standard filters only match standard messages, but extended
+ *   filters will match both standard and extended message IDs.
+ *
+ *   REVISIT: it might be nice to have an ioctl to control this behavior.
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev    - An instance of the network driver
+ *   fifo   - A FIFO in the obtained by calling tivacan_alloc_fifo()
+ *   filter - A canioc_stdfilter_s or canioc_extfilter_s
+ *            of type CAN_FILTER_MASK
+ *   type   - One of TIVA_CAN_FILTER_TYPE_STD or TIVA_CAN_FILTER_TYPE_EXT
+ *
+ * Return value: 0 on success, a negated errno on failure.
+ ****************************************************************************/
+
+static int  tivacan_initfilter(struct net_driver_s *dev,
+                               tiva_can_fifo_t  *fifo,
+                               void             *filter,
+                               int               type)
+{
+  uint32_t  reg;
+
+  /* Whether the End of Buffer bit has been set - the Tiva hardware
+   * overwrites the mailbox with this bit even if there is new data in it
+   * when the FIFO is full.
+   */
+
+  bool      eob_set     = false;
+
+  struct tiva_can_s *canmod = dev->d_private;
+  uint32_t  iface_base  = canmod->thd_iface_base;
+
+  struct canioc_stdfilter_s *sfilter = NULL;
+#ifdef CONFIG_NET_CAN_EXTID
+  struct canioc_extfilter_s *xfilter = NULL;
+#endif /* CONFIG_NET_CAN_EXTID */
+
+  if (type == TIVA_CAN_FILTER_TYPE_STD)
+    {
+      sfilter = filter;
+
+      if (sfilter->sf_type != CAN_FILTER_MASK)
+        {
+          return -EINVAL;
+        }
+    }
+#ifdef CONFIG_NET_CAN_EXTID
+  else if (type == TIVA_CAN_FILTER_TYPE_EXT)
+    {
+      xfilter = filter;
+
+      if (xfilter->xf_type != CAN_FILTER_MASK)
+        {
+          return -EINVAL;
+        }
+    }
+#endif /* CONFIG_NET_CAN_EXTID */
+  else
+    {
+      canerr("Invalid filter type parameter.\n");
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&canmod->thd_iface_lock);
+
+  for (int i = TIVA_CAN_NUM_MBOXES - 1; i >= 0; --i)
+    {
+      if (*fifo & (1 << i))
+        {
+          /* Disable the message object to prevent message SRAM conflicts */
+
+          tivacan_disable_mbox(dev, canmod->thd_iface_base, i);
+
+          /* Command Mask Register
+           *
+           * Write to message SRAM and access mask, control, and arbitration
+           * bits. Clear any interrupts (which might be spurious due to
+           * the hardware being uninitialized
+           */
+
+          reg =   TIVA_CANIF_CMSK_WRNRD     | TIVA_CANIF_CMSK_MASK
+              | TIVA_CANIF_CMSK_CONTROL   | TIVA_CANIF_CMSK_ARB
+              | TIVA_CANIF_CMSK_CLRINTPND;
+          putreg32(reg, iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+#ifdef CONFIG_NET_CAN_EXTID
+          /* Mask 1 Register - lower 16 bits of extended ID mask */
+
+          if (type == TIVA_CAN_FILTER_TYPE_EXT)
+            {
+              reg = xfilter->xf_id2 & TIVA_CANIF_MSK1_IDMSK_EXT_MASK;
+              putreg32(reg, iface_base + TIVA_CANIF_OFFSET_MSK1);
+            }
+#endif /* CONFIG_NET_CAN_EXTID */
+
+          /* Mask 2 Register
+           * Allow remote request frames through. This is done by disabling
+           * filter-on-direction (MDIR = 0)
+           */
+
+#ifdef CONFIG_NET_CAN_EXTID
+          if (type == TIVA_CAN_FILTER_TYPE_EXT)
+            {
+              reg = (xfilter->xf_id2 >> TIVA_CANIF_MSK2_IDMSK_EXT_PRESHIFT)
+                  & TIVA_CANIF_MSK2_IDMSK_EXT_MASK;
+            }
+          else
+            {
+#endif /* CONFIG_NET_CAN_EXTID */
+              /* Filter out extended ID messages by default with standard
+               * filters.
+               */
+
+              reg = TIVA_CANIF_MSK2_MXTD;
+              reg |= sfilter->sf_id2 << TIVA_CANIF_MSK2_IDMSK_STD_SHIFT;
+#ifdef CONFIG_NET_CAN_EXTID
+            }
+#endif /* CONFIG_NET_CAN_EXTID */
+
+          putreg32(reg, iface_base + TIVA_CANIF_OFFSET_MSK2);
+
+#ifdef CONFIG_NET_CAN_EXTID
+          /* Arbitration 1 Register - lower 16 bits of extended ID */
+
+          if (type == TIVA_CAN_FILTER_TYPE_EXT)
+            {
+              reg = xfilter->xf_id1 & TIVA_CANIF_ARB1_ID_EXT_MASK;
+              putreg32(reg, iface_base + TIVA_CANIF_OFFSET_ARB1);
+            }
+#endif /* CONFIG_NET_CAN_EXTID */
+
+          /* Arbitration 2 Register - upper extended & all standard ID bits
+           * Also mark the mailbox as valid.
+           */
+
+          reg = TIVA_CANIF_ARB2_MSGVAL;
+
+#ifdef CONFIG_NET_CAN_EXTID
+          if (type == TIVA_CAN_FILTER_TYPE_EXT)
+            {
+              reg |= xfilter->xf_id1 & TIVA_CANIF_ARB2_ID_EXT_MASK;
+            }
+          else
+            {
+#endif /* CONFIG_NET_CAN_EXTID */
+              /* Leave the XTD bit clear to indicate that only standard
+               * messages are allowed through the filter (MXTD is set).
+               */
+
+              reg |= sfilter->sf_id1 << TIVA_CANIF_ARB2_ID_STD_SHIFT;
+#ifdef CONFIG_NET_CAN_EXTID
+            }
+#endif /* CONFIG_NET_CAN_EXTID */
+
+          putreg32(reg, iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+          /* Message Control Register
+           * Use acceptance filter masks, enable RX interrupts for this
+           * mailbox. DLC is initialized to zero but updated by received
+           * frames.
+           */
+
+          reg = TIVA_CANIF_MCTL_UMASK | TIVA_CANIF_MCTL_RXIE;
+          if (!eob_set)
+            {
+              reg |= TIVA_CANIF_MCTL_EOB;
+              eob_set = true;
+            }
+
+          putreg32(reg, iface_base + TIVA_CANIF_OFFSET_MCTL);
+
+          /* Request the registers be transferred to the message RAM and wait
+           * for the transfer to complete. Mailboxes are 1-indexed in
+           * hardware.
+           */
+
+          putreg32(i + 1, iface_base + TIVA_CANIF_OFFSET_CRQ);
+          while (getreg32(iface_base + TIVA_CANIF_OFFSET_CRQ)
+              & TIVA_CANIF_CRQ_BUSY);
+        }
+    }
+
+  nxmutex_unlock(&canmod->thd_iface_lock);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: tivacan_reset
+ *
+ * Description:
+ *   Software-reset the CAN module using the SYSCON registers.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value: None
+ ****************************************************************************/
+
+static void tivacan_reset(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = (struct tiva_can_s *)dev->d_private;
+  int modnum = canmod->modnum;
+#ifndef CONFIG_TIVA_CAN0
+  if (modnum == 0)
+    {
+      canerr("ERROR: tried to reset disabled module CAN0\n");
+      return;
+    }
+
+#endif /* CONFIG_TIVA_CAN0 */
+#ifndef CONFIG_TIVA_CAN1
+  if (modnum == 1)
+    {
+      canerr("ERROR: tried to reset disabled module CAN1\n");
+    }
+
+#endif /* CONFIG_TIVA_CAN1 */
+  if (modnum > 1)
+    {
+      canerr("ERROR: tried to reset nonexistant module CAN%d\n",
+             canmod->modnum);
+    }
+
+  modifyreg32(TIVA_SYSCON_SRCAN, 0, SYSCON_SRCAN(modnum));
+
+  /* Spin for a few clock cycles */
+
+  for (volatile int i = 0; i < 16; ++i);
+
+  modifyreg32(TIVA_SYSCON_SRCAN, SYSCON_SRCAN(modnum), 0);
+
+  /* Wait for peripheral-ready signal */
+
+  while (!(getreg32(TIVA_SYSCON_PRCAN) & (1 << modnum)));
+}
+
+/****************************************************************************
+ * Name: tivacan_shutdown
+ *
+ * Description:
+ *   Shut down the CAN module. This function does the reverse of
+ *   tivacan_setup. The ISR is disabled in the NVIC, unregistered from the
+ *   NuttX dispatcher, and the CAN module is reset.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value: None
+ ****************************************************************************/
+
+static void tivacan_shutdown(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = (struct tiva_can_s *)dev->d_private;
+
+  int irq;
+
+  switch (canmod->modnum)
+  {
+    case 0:
+      {
+        irq = TIVA_IRQ_CAN0;
+      }
+      break;
+    case 1:
+      {
+        irq = TIVA_IRQ_CAN1;
+      }
+      break;
+    default:
+      canerr("ERROR: No such CAN module to disable interrupt\n");
+  }
+
+  /* Free all allocated RX filter FIFOs */
+
+  for (int i = 0; i < CONFIG_TIVA_CAN_FILTERS_MAX; ++i)
+    {
+      if (canmod->fifos[i])
+        {
+          tivacan_free_fifo(dev, &canmod->fifos[i]);
+        }
+    }
+
+  /* Free the TX FIFO */
+
+  if (canmod->fifos[CONFIG_TIVA_CAN_FILTERS_MAX])
+    {
+      tivacan_free_fifo(dev, &canmod->fifos[CONFIG_TIVA_CAN_FILTERS_MAX]);
+    }
+
+  /* Disable interrupts from the CAN module */
+
+  modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+              TIVA_CAN_CTL_EIE | TIVA_CAN_CTL_SIE | TIVA_CAN_CTL_IE, 0);
+
+  up_disable_irq(irq);
+  irq_detach(irq);
+}
+
+/****************************************************************************
+ * Name: tivacan_setup
+ *
+ * Description:
+ *   Set up the CAN module. Register the ISR and enable the interrupt in the
+ *   NVIC. Set default bit-timing and set filters to a consistent state. On
+ *   return, the INIT bit is cleared in the CANCTL register, and interrupts
+ *   are enabled on the side of the CAN module. (can_ops_s.txint and
+ *   can_ops_s.rxint are unsuitable because the Tiva CAN modules do not allow
+ *   disabling TX and RX interrupts separately).
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   Zero on success, or a negated errno on failure.
+ ****************************************************************************/
+
+static int tivacan_setup(struct net_driver_s *dev)
+{
+  uint32_t irq;
+  int      ret;
+  uint32_t reg;
+  struct tiva_can_s *canmod = dev->d_private;
+
+#ifdef CONFIG_NET_CAN_EXTID
+  struct canioc_extfilter_s default_filter =
+      {
+          .xf_id1   = 0x123,
+          .xf_id2   = 0,
+          .xf_type  = CAN_FILTER_MASK,
+          .xf_prio  = CAN_MSGPRIO_HIGH,
+      };
+#else
+  struct canioc_stdfilter_s default_filter =
+      {
+          .sf_id1   = 0x123,
+          .sf_id2   = 0,
+          .sf_type  = CAN_FILTER_MASK,
+          .sf_prio  = CAN_MSGPRIO_HIGH,
+      };
+#endif /* CONFIG_NET_CAN_EXTID */
+
+  /* Clear the status interrupt, just in case */
+
+  getreg32(canmod->base + TIVA_CAN_OFFSET_STS);
+
+  /* Set default bit timing */
+
+  switch (canmod->modnum)
+  {
+#ifdef CONFIG_TIVA_CAN0
+    case 0:
+      tivacan_bittiming_set(dev, CONFIG_TIVA_CAN0_BAUD * 1000);
+      break;
+# endif /* CONFIG_TIVA_CAN0 */
+#ifdef CONFIG_TIVA_CAN1
+    case 1:
+      tivacan_bittiming_set(dev, CONFIG_TIVA_CAN1_BAUD * 1000);
+      break;
+# endif /* CONFIG_TIVA_CAN1 */
+  }
+
+  nxmutex_lock(&canmod->thd_iface_lock);
+
+  /* Ensure a consistent state */
+
+  for (int i = 0; i < TIVA_CAN_NUM_MBOXES; ++i)
+    {
+      tivacan_disable_mbox(dev, canmod->thd_iface_base, i);
+    }
+
+  /* Ensure the TX mailboxes have filtering enabled. Since higher mailbox
+   * numbers are lower-priority for receiving messages, this is might not be
+   * necessary - if the message matches an existing filter FIFO, the hardware
+   * _shouldn't_ let it "fall through" to the next mailbox if the
+   * End of Buffer bit is set (which tivacan_initfilter guarantees). However,
+   * the datasheet does not explicitly guarantee this.
+   *
+   * The other case to be considered is if the incoming message doesn't match
+   * any of the mailboxes in the existing filter FIFOs. In this case it could
+   * fall through to the TX mailbox. The datasheet doesn't specify what
+   * happens if an incoming message matches a mailbox with TXRQST set. It
+   * is possible that the message would overwrite the contents of the TX
+   * mailbox, which would be very bad.
+   *
+   * There are two parts to the filtering rules:
+   *  - MDIR: filter-on-direction. This prevents received messages (of any
+   *          type - RTR or normal data messages) from matching the TX
+   *          mailbox whenever we're transmitting a data frame
+   *          (i.e. ARB2.DIR = 1)
+   *
+   *  - ID filtering: Since MDIR does not protect the TX mailbox when a
+   *                  remote request frame is being transmitted
+   *                  (ARB2.DIR = 0), we require an exact match of the ID
+   *                  and ID type (i.e. standard/extended) to limit the
+   *                  likelihood of the remote request being corrupted before
+   *                  transmission. Since remote requests contain no data,
+   *                  the only thing that would be changed by a received
+   *                  message would be the DLC.
+   *
+   * The MCTL.UMASK bit is set in tivacan_send.
+   */
+
+  for (int i = TIVA_CAN_TX_FIFO_START; i < TIVA_CAN_NUM_MBOXES; ++i)
+    {
+      reg = TIVA_CANIF_MSK1_IDMSK_EXT_MASK;
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_MSK1);
+      reg = TIVA_CANIF_MSK2_IDMSK_EXT_MASK
+          | TIVA_CANIF_MSK2_MDIR
+          | TIVA_CANIF_MSK2_MXTD;
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_MSK2);
+
+      putreg32(TIVA_CANIF_CMSK_MASK,
+               canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+      /* One-indexed */
+
+      putreg32(i + 1, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+      /* Wait for request to process */
+
+      while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+          & TIVA_CANIF_CRQ_BUSY);
+    }
+
+  nxmutex_unlock(&canmod->thd_iface_lock);
+
+  /* Register the ISR */
+
+  switch (canmod->modnum)
+  {
+    case 0:
+      {
+        irq = TIVA_IRQ_CAN0;
+      }
+      break;
+    case 1:
+      {
+        irq = TIVA_IRQ_CAN1;
+      }
+      break;
+    default:
+      {
+        canerr("ERROR: no such CAN module\n");
+        return -ENODEV;
+      }
+  }
+
+  ret = irq_attach(irq, tivacan_isr, dev);
+  if (ret < 0)
+    {
+      canerr("ERROR: Failed to register CAN ISR.\n");
+      return ret;
+    }
+
+  up_enable_irq(irq);
+
+  /* Exit init mode and enable interrupts from the CAN module */
+
+  modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL, TIVA_CAN_CTL_INIT,
+              TIVA_CAN_CTL_EIE | TIVA_CAN_CTL_IE);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  /* Status interrupts are used for reporting lost arbitration, etc. */
+
+  modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL, 0, TIVA_CAN_CTL_SIE);
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* The TX FIFO is manually allocated first because it must be located at
+   * the end of the series of mailboxes to make sure responses to remote
+   * request frames go to the correct place.
+   */
+
+  canmod->fifos[CONFIG_TIVA_CAN_FILTERS_MAX] =
+      0xffffffff << (32 - CONFIG_TIVA_CAN_TX_FIFO_DEPTH);
+  canmod->tx_fifo = &canmod->fifos[CONFIG_TIVA_CAN_FILTERS_MAX];
+
+  ret = tivacan_alloc_fifo(dev, CONFIG_TIVA_CAN_DEFAULT_FIFO_DEPTH);
+  if (ret < 0)
+    {
+      canerr("ERROR: Failed to allocate default RX FIFO.\n");
+      return ret;
+    }
+  else
+    {
+      canmod->rxdefault_fifo = &canmod->fifos[ret];
+    }
+
+#ifdef CONFIG_NET_CAN_EXTID
+  tivacan_initfilter(dev,
+                     canmod->rxdefault_fifo,
+                     &default_filter,
+                     TIVA_CAN_FILTER_TYPE_EXT);
+#else
+  tivacan_initfilter(dev,
+                     canmod->rxdefault_fifo,
+                     &default_filter,
+                     TIVA_CAN_FILTER_TYPE_STD);
+#endif /* CONFIG_NET_CAN_EXTID */
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: tivacan_bittiming_set
+ *
+ * Description:
+ *   Set the CAN bit timing parameters to the hardware registers. The values
+ *   used should be the actual values in time quanta, not the values stored
+ *   in the registers (which are off by one).
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *   desired_baud_rate - Desired baud rate value b/s
+ *
+ * Returned value: None
+ ****************************************************************************/
+
+static void tivacan_bittiming_set(struct net_driver_s *dev,
+                                  uint32_t desired_baud_rate)
+{
+  irqstate_t flags;
+  uint32_t    canbit;
+  uint32_t    brpe;
+  uint32_t    ctl;
+  bool        init_mode;
+  uint32_t    canbase = ((struct tiva_can_s *)dev->d_private)->base;
+
+  /* Calculate the timing parameters */
+
+  struct tiva_can_timing_s timing;
+  uint8_t i = 0;
+  uint32_t bit_quanta = CAN_BIT_QUANTA;
+
+  /* Iterate to find appropriate number of bit quanta. According to
+   * the datasheet the phase 1 and phase 2 should be approximately the same
+   * length. The Prescaler value is determined based on the SYSCLK frequency
+   * and the desired baud rate multiplied by required count of bit quanta.
+   * The TSEG1 = 1 + propagation delay (n_prop) + phase 1 (in time quanta).
+   * The TSEG2 = phase2 (in time quanta)
+   */
+
+  while (true)
+    {
+      timing.prescaler = SYSCLK_FREQUENCY /
+          (desired_baud_rate * bit_quanta);
+      uint32_t can_freq = SYSCLK_FREQUENCY / timing.prescaler;
+
+      /* Calculate CAN time quantum duration */
+
+      float tq = 1.0 / (can_freq);
+
+      /*  Propagation time delay is approximately 400 ns */
+
+      uint8_t n_prop = CAN_TRANSMISSION_DELAY / tq;
+      n_prop = n_prop > 0 ? n_prop : 1;
+
+      /* Calculate both segments length */
+
+      uint8_t phase1 = floor((bit_quanta - 1 - n_prop) / 2);
+      int phase2 = bit_quanta - 1 - n_prop - phase1;
+
+      /* Set SJW to minimum of 4, phase1 and phase2 according to datasheet */
+
+      if (phase1 < 4)
+        {
+          if (phase2 < phase1)
+            timing.sjw = phase2;
+          else
+            timing.sjw = phase1;
+        }
+      else
+        timing.sjw = 4;
+      timing.tseg2 = phase2 > TIVA_CAN_TSEG2_MAX ?
+          TIVA_CAN_TSEG2_MAX : phase2;
+      timing.tseg1 = bit_quanta - 1 - timing.tseg2;
+
+      /* If seg1 has not admissible value lower down the bit_quanta
+       * and rerun the calculation.
+       * Otherwise we are finished with the calculation.
+       */
+
+      if (timing.tseg1 > TIVA_CAN_TSEG1_MAX)
+        {
+          bit_quanta = bit_quanta - 1;
+        }
+      else
+        break;
+
+      i = i + 1;
+      if (i >= TIVA_CAN_SET_TIMING_MAX_TER)
+        {
+          nerr("ERROR: Cannot set up desired baud rate %d for CAN %d\n",
+               desired_baud_rate,
+               ((struct tiva_can_s *)dev->d_private)->modnum);
+          return;
+        }
+    }
+
+  DEBUGASSERT(timing.prescaler > TIVA_CAN_PRESCALER_MIN &&
+              timing.prescaler < TIVA_CAN_PRESCALER_MAX);
+  DEBUGASSERT(timing.tseg1 > TIVA_CAN_TSEG1_MIN &&
+              timing.tseg1 < TIVA_CAN_TSEG1_MAX);
+  DEBUGASSERT(timing.tseg2 > TIVA_CAN_TSEG2_MIN &&
+              timing.tseg2 < TIVA_CAN_TSEG2_MAX);
+  DEBUGASSERT(timing.sjw > TIVA_CAN_SJW_MIN &&
+              timing.sjw < TIVA_CAN_SJW_MAX);
+
+  flags = enter_critical_section();
+
+  ctl = getreg32(canbase + TIVA_CAN_OFFSET_CTL);
+
+  /* Save the current state of the init bit */
+
+  init_mode = ctl & TIVA_CAN_CTL_INIT;
+
+  /* Enable writes to CANBIT and BRPE - set INIT and Configuration Change
+   * Enable (CCE)
+   */
+
+  ctl |= TIVA_CAN_CTL_INIT | TIVA_CAN_CTL_CCE;
+  putreg32(ctl, canbase + TIVA_CAN_OFFSET_CTL);
+
+  canbit = getreg32(canbase + TIVA_CAN_OFFSET_BIT);
+  brpe   = getreg32(canbase + TIVA_CAN_OFFSET_BRPE);
+
+  canbit &= ~(TIVA_CAN_BIT_BRP_MASK | TIVA_CAN_BIT_TSEG1_MASK
+      | TIVA_CAN_BIT_TSEG2_MASK | TIVA_CAN_BIT_SJW_MASK);
+
+  /* Masking bits that belong in the baud rate prescaler extension register.
+   * Values stored in registers are 1 less than the value used.
+   */
+
+  canbit |= ((timing.prescaler - 1) << TIVA_CAN_BIT_BRP_SHIFT)
+      & TIVA_CAN_BIT_BRP_MASK;
+  brpe   |= ((timing.prescaler - 1) >> TIVA_CAN_BIT_BRP_LENGTH)
+      << TIVA_CAN_BRPE_BRPE_SHIFT;
+
+  canbit |= (timing.tseg1 - 1) << TIVA_CAN_BIT_TSEG1_SHIFT;
+  canbit |= (timing.tseg2 - 1) << TIVA_CAN_BIT_TSEG2_SHIFT;
+  canbit |= (timing.sjw - 1) << TIVA_CAN_BIT_SJW_SHIFT;
+
+  putreg32(canbit, canbase + TIVA_CAN_OFFSET_BIT);
+  putreg32(brpe, canbase + TIVA_CAN_OFFSET_BRPE);
+
+  /* Lock changes to CANBIT and BRPE (clear Configuration Change Enable) */
+
+  ctl &= ~TIVA_CAN_CTL_CCE;
+
+  /* Exit init mode if the baud rate was changed on-the-fly */
+
+  if (!init_mode)
+    {
+      ctl &= ~TIVA_CAN_CTL_INIT;
+    }
+
+  putreg32(ctl, canbase + TIVA_CAN_OFFSET_CTL);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * NuttX interface Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: tivacan_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  - An instance of the network driver
+ *
+ * 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 tivacan_txpoll(struct net_driver_s *dev)
+{
+  /* 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 (dev->d_len > 0)
+    {
+      /* Send the packet */
+
+      tivacan_send(dev);
+
+      /* Check if there is room in the device to hold another packet. If
+       * not, return a non-zero value to terminate the poll.
+       */
+
+      if (tivacan_txready(dev) == false)
+        {
+          return -EBUSY;
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: tivacan_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - An instance of the private can struct tiva_can_s (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void tivacan_txavail_work(void *arg)
+{
+  struct tiva_can_s *priv = (struct tiva_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 (tivacan_txready(&priv->dev))
+        {
+          /* No, there is space for another transfer.  Poll the network for
+           * new XMIT data.
+           */
+
+          devif_poll(&priv->dev, tivacan_txpoll);
+        }
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: tivacan_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  - An instance of the network driver
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int tivacan_txavail(struct net_driver_s *dev)
+{
+  struct tiva_can_s *priv = (struct tiva_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. */
+
+      tivacan_txavail_work(priv);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: tivacan_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the CAN interface
+ *
+ * Input Parameters:
+ *   dev  - An instance of the network driver
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int tivacan_ifup(struct net_driver_s *dev)
+{
+  struct tiva_can_s *priv = (struct tiva_can_s *)dev->d_private;
+
+  /* Setup CAN */
+
+  tivacan_setup(dev);
+
+  priv->bifup = true;
+
+  priv->dev.d_buf = (uint8_t *)priv->tx_pool;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: tivacan_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Bring down the CAN interface
+ *
+ * Input Parameters:
+ *   dev  - An instance of the network driver
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int tivacan_ifdown(struct net_driver_s *dev)
+{
+  /* Disable the interface */
+
+  tivacan_shutdown(dev);
+
+  /* Stop processing messages */
+
+  tivacan_reset(dev);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tiva_cansockinitialize
+ *
+ * Description:
+ *   Initialize the selected CAN module using socket net interface
+ *
+ * Input Parameters:
+ *   Module number, for chips with multiple modules (typically 0 or 1)
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int tiva_cansockinitialize(int modnum)
+{
+  struct tiva_can_s *canmod;
+  int                 ret  = OK;
+  caninfo("tiva_cansockinitialize module %d\n", modnum);
+  if (modnum > 1 || modnum < 0)
+    {
+      nerr("ERROR: Unsupported CAN modnum %d\n", modnum);
+      return -EINVAL;
+    }
+
+#ifdef CONFIG_TIVA_CAN0
+  if (modnum == 0)
+    {
+      canmod = &g_tivacan0priv;
+    }
+#else
+  if (modnum == 0)
+    {
+      return -ENODEV;
+    }
+#endif /* CONFIG_TIVA_CAN0 */
+
+#ifdef CONFIG_TIVA_CAN1
+  if (modnum == 1)
+    {
+      canmod = &g_tivacan1priv;
+    }
+#else
+  if (modnum == 1)
+    {
+      return -ENODEV;
+    }
+#endif /* CONFIG_TIVA_CAN1 */
+
+  /* Initialize the driver structure */
+
+  canmod->dev.d_ifup    = tivacan_ifup;
+  canmod->dev.d_ifdown  = tivacan_ifdown;
+  canmod->dev.d_txavail = tivacan_txavail;
+#ifdef CONFIG_NETDEV_IOCTL
+  canmod->dev.d_ioctl   = tivacan_netdev_ioctl;
+#endif /* CONFIG_NETDEV_IOCTL */
+  canmod->dev.d_private = canmod;
+
+  /* Put the interface in the down state.  This usually amounts to resetting
+   * the device and/or calling stm32can_ifdown().

Review Comment:
   ```suggestion
      * the device and/or calling tivacan_ifdown().
   ```



##########
arch/arm/src/tiva/common/tiva_sock_can.c:
##########
@@ -0,0 +1,2371 @@
+/****************************************************************************
+ * arch/arm/src/tiva/common/tiva_sock_can.c
+ * SocketCAN driver implementation for Tiva. Based on the chardev driver.
+ *
+ * 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 <math.h>
+#include <nuttx/mutex.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 "tiva_can.h"
+#include "hardware/tiva_can.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only the last few mailboxes are used for TX to help ensure that responses
+ * to remote request frames we send are always collected by an RX FIFO and
+ * not by the mailbox used to send the remote frame. (The Tiva hardware uses
+ * the mailbox with the lowest number first.)
+ *
+ * This number is zero-indexed, althought the Command Request Register isn't.
+ */
+#define TIVA_CAN_TX_FIFO_START (TIVA_CAN_NUM_MBOXES - \
+        CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+
+/* Frequency of async error message reporting when rate-limited */
+#define TIVA_CAN_ERR_HANDLER_TICKS MSEC2TICK(CONFIG_TIVA_CAN_ERR_HANDLER_PER)
+
+/* For tivacan_initfilter */
+#define TIVA_CAN_FILTER_TYPE_STD 0
+#define TIVA_CAN_FILTER_TYPE_EXT 1
+
+/* Delays *******************************************************************/
+
+/* Defines for bit timing */
+#define CAN_BIT_QUANTA 20
+#define CAN_TRANSMISSION_DELAY 400E-9
+
+#if defined(CONFIG_TIVA_CAN) && (defined(CONFIG_TIVA_CAN0) || \
+        defined(CONFIG_TIVA_CAN1)) && defined(CONFIG_TIVA_SOCKET_CAN)
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#endif /* CONFIG_SCHED_WORKQUEUE */
+
+/* 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
+
+/* Combines two 16-bit registers into a single 32 bit value */
+#define tivacan_readsplitreg32(base, r1, r2) \
+        ((getreg32((base) + (r1)) & 0xffff) | (getreg32((base) + (r2)) << 16))
+
+#define tivacan_get_nwda32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_NWDA1,\
+                               TIVA_CAN_OFFSET_NWDA2)
+#define tivacan_get_txrq32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_TXRQ1,\
+                               TIVA_CAN_OFFSET_TXRQ2)
+#define tivacan_get_msgval32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_MSG1VAL,\
+                               TIVA_CAN_OFFSET_MSG2VAL)
+
+#define TIVA_CAN_AUTRECOVER
+
+/* Set the maximum number of iterations for the tivacan_bittiming_set timing
+ * parameters calculation
+ */
+#define TIVA_CAN_SET_TIMING_MAX_TER 5
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef uint32_t tiva_can_fifo_t;
+struct tiva_can_s
+{
+  /* Module number */
+
+  int       modnum;
+
+  /* Registers base address */
+
+  uint32_t  base;
+
+  /* Contents of the CANSTS reg the last time errors were processed. */
+
+  uint32_t  status;
+
+  /* Contents of the CANERR reg the last time errors were processed. */
+
+  uint32_t  errors;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  struct work_s error_work; /* For deferring error work to the work wq */
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* Configured baud rate */
+
+  uint32_t baud;
+
+  /* Interface registers base address for threads threads */
+
+  uint32_t  thd_iface_base;
+
+  /* Interface registers base address reserved exclusively for the ISR */
+
+  uint32_t  isr_iface_base;
+
+  /* Mutex for threads accessing the interface registers */
+
+  mutex_t   thd_iface_lock;
+
+  /* Mutex for claiming, freeing, and potentially resizing RX FIFOs.
+   * The TX FIFO should never be resized at runtime.
+   */
+
+  mutex_t   fifo_lock;
+
+  /* true:ifup false:ifdown */
+
+  bool                bifup;
+
+  /* Interface understood by the network */
+
+  struct net_driver_s dev;
+
+  struct work_s irqwork;  /* For deferring interrupt work to the wq */
+  struct work_s pollwork; /* For deferring poll work to the work wq */
+  struct work_s rxwork;   /* For deferring receive work to the work wq */
+
+  /* flag to repeat the loop getting data from all receive mailboxes */
+
+  uint8_t rx_work_repeat;
+
+  /* index of mailbox which is currently processed for data reception */
+
+  uint8_t rx_mbox_index;
+
+  /* List of all FIFOs. Contains the indices of used mailboxes for the
+   * appropriate FIFO.
+   */
+
+  tiva_can_fifo_t fifos[CONFIG_TIVA_CAN_FILTERS_MAX + 1];
+
+  /* Pointer to default catch-all RX FIFO initialized by the driver. */
+
+  tiva_can_fifo_t *rxdefault_fifo;
+
+  /* Pointer to the permanent, fixed-size TX FIFO. This is always located at
+   * the end of the series of mailboxes to help ensure that responses to
+   * remote request frames go to an RX (filter) FIFO and don't interfere.
+   */
+
+  tiva_can_fifo_t *tx_fifo;
+
+  /* Index in the TX FIFO where new messages should be added. The Tiva CAN
+   * module doesn't support a ring buffer, so new messages are only added
+   * when the TX FIFO is empty (tx_fifo_tail == 0).
+   */
+
+  int tx_fifo_tail;
+
+  /* NuttX interface TX/RX pool */
+
+  uint8_t tx_pool[sizeof(struct can_frame)];
+  uint8_t rx_pool[sizeof(struct can_frame)];
+};
+
+/* This structure represents the CAN bit timing settings as seen by the hw. */
+
+struct tiva_can_timing_s
+{
+  int       prescaler;
+  int       tseg1;
+  int       tseg2;
+  int       sjw;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* CAN error interrupts and ISR */
+
+static int  tivacan_isr(int irq, void *context, void *dev);
+
+/* Internal utility functions */
+
+static int tivacan_txpoll(struct net_driver_s *dev);
+static struct tiva_can_timing_s
+tivacan_bittiming_get(struct net_driver_s *dev);
+static void tivacan_bittiming_set(struct net_driver_s *dev,
+                                  uint32_t desired_baud_rate);
+
+static int tivacan_setup(struct net_driver_s *dev);
+static void tivacan_shutdown(struct net_driver_s *dev);
+static void tivacan_reset(struct net_driver_s *dev);
+static int tivacan_send(struct net_driver_s *dev);
+static int tivacan_alloc_fifo(struct net_driver_s *dev, int depth);
+static void tivacan_free_fifo(struct net_driver_s *dev,
+                              tiva_can_fifo_t *fifo);
+static void tivacan_disable_mbox(struct net_driver_s *dev,
+                                 uint32_t iface_base,
+                                 int num);
+static int  tivacan_initfilter(struct net_driver_s *dev,
+                               tiva_can_fifo_t  *fifo,
+                               void             *filter,
+                               int               type);
+static void tivacan_receive_work(void *priv);
+int tivacan_handle_errors(struct net_driver_s *dev);
+
+static bool tivacan_txready(struct net_driver_s *dev);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+void tivacan_handle_errors_wqueue(void *dev);
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+/* NuttX callback functions */
+
+static int tivacan_ifup(struct net_driver_s *dev);
+static int tivacan_ifdown(struct net_driver_s *dev);
+
+static void tivacan_txavail_work(void *arg);
+static int tivacan_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int tivacan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                unsigned long arg);
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_TIVA_CAN0
+static struct tiva_can_s g_tivacan0priv =
+    {
+        .modnum          = 0,
+        .base            = TIVA_CAN_BASE(0),
+        .thd_iface_base  = TIVA_CAN_IFACE_BASE(0, 0),
+        .isr_iface_base  = TIVA_CAN_IFACE_BASE(0, 1),
+        .bifup           = false,  /* true:ifup false:ifdown */
+        .baud            = CONFIG_TIVA_CAN0_BAUD,
+        .rx_work_repeat  = 0,
+        .rx_mbox_index   = 0,
+    };
+
+#endif /* CONFIG_TIVA_CAN0 */
+
+#ifdef CONFIG_TIVA_CAN1
+static struct tiva_can_s g_tivacan1priv =
+    {
+        .modnum           = 1,
+        .base             = TIVA_CAN_BASE(1),
+        .thd_iface_base   = TIVA_CAN_IFACE_BASE(1, 0),
+        .isr_iface_base   = TIVA_CAN_IFACE_BASE(1, 1),
+        .bifup            = false,  /* true:ifup false:ifdown */
+        .baud             = CONFIG_TIVA_CAN1_BAUD,
+        .rx_work_repeat   = 0,
+        .rx_mbox_index    = 0,
+    };
+#endif /* CONFIG_TIVA_CAN1 */
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tivacan_receive_work
+ *
+ * Description: Worker callback for processing received messages
+ ****************************************************************************/
+
+static void tivacan_receive_work(void *priv)
+{
+  struct net_driver_s *dev    = (struct net_driver_s *)priv;
+  struct tiva_can_s   *canmod = dev->d_private;
+  struct can_frame    *frame  = (struct can_frame *)canmod->rx_pool;
+  uint8_t             *mbox   = &canmod->rx_mbox_index;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  int ret;
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* The while loop iteration looks for new data in all RX mailboxes.
+   * If a message has arrived while in the loop the canmod->rx_work_repeat
+   * should be set so that the reception loop is repeated.
+   */
+
+  while (canmod->rx_work_repeat)
+    {
+      canmod->rx_work_repeat = 0;
+      nxmutex_lock(&canmod->thd_iface_lock);
+
+      /* Process the received message(s). Since hardware RX FIFOS are used
+       * and new messages are received into the mailbox with the lowest
+       * number, we can't use CANINT since a new message might arrive in a
+       * mailbox we'd already read from before the higher-address mailboxes
+       * in the FIFO were emptied. (Besides, the ISR clears CANINT).
+       */
+
+      for (*mbox = 0;
+          *mbox < TIVA_CAN_NUM_MBOXES - CONFIG_TIVA_CAN_TX_FIFO_DEPTH;
+          ++*mbox)
+        {
+          uint32_t reg;
+
+          /* New Data registers */
+
+          reg = tivacan_get_nwda32(canmod->base);
+
+          if (!(reg & ~*(canmod->tx_fifo)))
+            {
+              /* No new messages to process in the RX FIFOs */
+
+              break;
+            }
+          else if (! (reg & 1 << *mbox))
+            {
+              /* No new message in this mailbox */
+
+              continue;
+            }
+
+          /* Command Mask register
+           * Pull arbitration bits, control bits, and data bits from the
+           * message SRAM. Clear the new data bit; the ISR cleared the IRQ.
+           */
+
+          reg =   TIVA_CANIF_CMSK_ARB       | TIVA_CANIF_CMSK_CONTROL
+              | TIVA_CANIF_CMSK_NEWDAT    | TIVA_CANIF_CMSK_DATAA
+              | TIVA_CANIF_CMSK_DATAB;
+          putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+          /* Submit request (1-indexed) */
+
+          putreg32(*mbox + 1,
+                   canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+          /* Wait for response */
+
+          while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+              & TIVA_CANIF_CRQ_BUSY);
+
+          /* ARB2 - upper chunk of the message ID, "direction," and extended
+           * message indication. (NOTE: sadly, DIR is always 0 (receive) even
+           * for RTR messages. There is no way to determine whether the RTR
+           * bit was set on a received frame.
+           */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+#ifdef CONFIG_NET_CAN_EXTID
+          uint8_t ext_id = (0 != (reg & TIVA_CANIF_ARB2_XTD));
+
+          if (ext_id)
+            {
+              frame->can_id = (reg & TIVA_CANIF_ARB2_ID_EXT_MASK)
+                      << TIVA_CANIF_ARB2_ID_EXT_PRESHIFT;
+
+              /* ARB1 - lower chunk of the message ID for extended IDs */
+
+              reg = getreg32(
+                  canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+              frame->can_id |= reg & TIVA_CANIF_ARB1_ID_EXT_MASK;
+            }
+          else
+            {
+              frame->can_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                      >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+            }
+#else
+          frame->can_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                  >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+#endif /* CONFIG_NET_CAN_EXTID */
+
+          /* Message Control - remote enable (RMTEN) & data length code */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+          frame->can_dlc = reg & TIVA_CANIF_MCTL_DLC_MASK;
+
+          /* Data registers - these are little endian but the uint8_t
+           * array in can_msg_s is big endian.
+           */
+
+          for (int j = 0; j < (frame->can_dlc + 1) / 2; ++j)
+            {
+              reg = getreg32(canmod->thd_iface_base
+                             + TIVA_CANIF_OFFSET_DATA(j));
+
+              frame->data[j * 2]      = (reg & TIVA_CANIF_DATA_HBYTE_MASK)
+                      >> TIVA_CANIF_DATA_HBYTE_SHIFT;
+              frame->data[j * 2 + 1]  = (reg & TIVA_CANIF_DATA_LBYTE_MASK)
+                      >> TIVA_CANIF_DATA_LBYTE_SHIFT;
+            }
+
+          /* Point the d_buf buffer to the received can frame */
+
+          dev->d_len = sizeof(struct can_frame);
+          dev->d_buf = (uint8_t *)frame;
+
+          NETDEV_RXPACKETS(dev);
+
+          /* Process the received frame */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+          ret = can_input(dev);
+#else
+          can_input(dev);
+#endif /* CONFIG_NET_CAN_ERRORS */
+        }
+
+      /* 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.
+       */
+
+      dev->d_buf = (uint8_t *)canmod->tx_pool;
+      nxmutex_unlock(&canmod->thd_iface_lock);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+      if (ret == OK)
+        {
+          /* Switch to processing errors by interrupt when the application
+           * is able to keep up with the message volume and messages are
+           * being received successfully.
+           */
+
+          work_cancel(CANWORK, &canmod->error_work);
+          modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+                      0, TIVA_CAN_CTL_SIE);
+        }
+#endif /* CONFIG_NET_CAN_ERRORS */
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+/****************************************************************************
+ * Name: tivacan_ioctl
+ *
+ * Description:
+ *   Perform the action requested by an ioctl syscall that was not handled by
+ *   the "upper half" CAN driver.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   cmd - One of the CANIOC_* ioctls
+ *   arg - Second argument to the ioctl call.
+ *
+ * Returned value:
+ *   Zero on success; a negated errno on failure
+ ****************************************************************************/
+
+static int tivacan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                unsigned long arg)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  int ret;
+
+  /* TODO: The IOCTL commands are not implemented yet. */
+
+#warning Missing logic
+
+  switch (cmd)
+  {
+    default:
+      canerr("ERROR: Unrecognized ioctl\n");
+      ret = -ENOSYS;
+      break;
+  }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Name: tivacan_send
+ *
+ * Description:
+ *   Enqueue a message to transmit into the hardware FIFO. Returns
+ *   immediately if the FIFO is full. This function must not fail when
+ *   tivacan_txready returns true, or the message will be lost.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   Zero on success; a negated errorno on failure.
+ ****************************************************************************/
+
+static int tivacan_send(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  struct can_frame *frame = (struct can_frame *)dev->d_buf;
+  uint32_t reg = 0;
+
+  /* REVISIT: Should can_txdone be called to advance the FIFO buffer
+   * head when dev_send is called when it shouldn't be?
+   */
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      NETDEV_TXDONE(canmod->dev);
+      return -ENETDOWN;
+    }
+
+  if (canmod->tx_fifo_tail >= CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+    {
+      NETDEV_TXDONE(canmod->dev);
+      return -EBUSY;
+    }
+
+  nxmutex_lock(&canmod->thd_iface_lock);
+
+  /* Protect the message object due the minute chance that the mailbox was
+   * previously used for a remote frame and could receive messages, causing
+   * an SRAM conflict.
+   */
+
+  tivacan_disable_mbox(dev, canmod->thd_iface_base,
+                       TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail);
+
+  /* Command mask register */
+
+  reg = TIVA_CANIF_CMSK_WRNRD | TIVA_CANIF_CMSK_ARB | TIVA_CANIF_CMSK_CONTROL
+      | TIVA_CANIF_CMSK_CLRINTPND
+      | TIVA_CANIF_CMSK_DATAA | TIVA_CANIF_CMSK_DATAB;
+  putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+  /* Arbitration registers (ARB1 and ARB2)
+   * Check if the frame is RTR
+   */
+
+  uint8_t rtr = frame->can_id & CAN_RTR_FLAG;
+  frame->can_id &= ~CAN_RTR_FLAG;
+
+#ifdef CONFIG_NET_CAN_EXTID
+  if (frame->can_id & CAN_EFF_FLAG)
+    {
+      frame->can_id &= ~CAN_EFF_FLAG;
+
+      reg = frame->can_id & TIVA_CANIF_ARB1_ID_EXT_MASK;
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+      reg = (frame->can_id >> TIVA_CANIF_ARB2_ID_EXT_PRESHIFT)
+              & TIVA_CANIF_ARB2_ID_EXT_MASK;
+      reg |= TIVA_CANIF_ARB2_MSGVAL | TIVA_CANIF_ARB2_XTD;
+
+#ifdef CONFIG_CAN_USE_RTR
+      reg |= (rtr ? 0 : TIVA_CANIF_ARB2_DIR);
+#else
+      reg |= TIVA_CANIF_ARB2_DIR;
+#endif /* CONFIG_CAN_USE_RTR */
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+    }
+  else
+    {
+#endif /* CONFIG_NET_CAN_EXTID */
+      reg = (frame->can_id << TIVA_CANIF_ARB2_ID_STD_SHIFT)
+          & TIVA_CANIF_ARB2_ID_STD_MASK;
+
+      reg |= TIVA_CANIF_ARB2_MSGVAL;
+
+#ifdef CONFIG_CAN_USE_RTR
+      reg |= (rtr ? 0 : TIVA_CANIF_ARB2_DIR);
+#else
+      reg |= TIVA_CANIF_ARB2_DIR;
+#endif /* CONFIG_CAN_USE_RTR */
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+#ifdef CONFIG_NET_CAN_EXTID
+    }
+#endif /* CONFIG_NET_CAN_EXTID */
+
+  /* Message Control (MCTL) register */
+
+  reg = frame->can_dlc & TIVA_CANIF_MCTL_DLC_MASK;
+
+  /* Protect the mailbox from rx messages; see comment in tivacan_setup */
+
+  reg |= TIVA_CANIF_MCTL_UMASK;
+
+  /* NOTE: Not sure whether the End Of Buffer bit means anything here */
+
+  reg |= TIVA_CANIF_MCTL_NEWDAT | TIVA_CANIF_MCTL_TXIE
+      | TIVA_CANIF_MCTL_TXRQST | TIVA_CANIF_MCTL_EOB;
+  putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+
+  /* Data registers, there are four of them. */
+
+  for (int i = 0; i < (frame->can_dlc + 1) / 2; ++i)
+    {
+      reg = (uint32_t)frame->data[i * 2]
+                                  << TIVA_CANIF_DATA_HBYTE_SHIFT;
+      reg |= (uint32_t)frame->data[i * 2 + 1]
+                                   << TIVA_CANIF_DATA_LBYTE_SHIFT;
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_DATA(i));
+    }
+
+  /* Submit request; this register is one-indexed. */
+
+  putreg32(TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail + 1,
+           canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+  /* Wait for completion */
+
+  while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+      & TIVA_CANIF_CRQ_BUSY);
+
+  nxmutex_unlock(&canmod->thd_iface_lock);
+
+  /* Move to the next message in the h/w TX FIFO.
+   * Tell the upper-half the message has been submitted... this recurses
+   * back on us until the software TX FIFO is empty.
+   */
+
+  ++canmod->tx_fifo_tail;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: tivacan_txready
+ *
+ * Description:
+ *   Determine whether the hardware is ready to transmit another TX message.
+ *   This checks for bus-off conditions as well as if the FIFO is full.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   True if the hardware can accept another TX message, false otherwise.
+ ****************************************************************************/
+
+static bool tivacan_txready(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      return false;
+    }
+
+  return (canmod->tx_fifo_tail < CONFIG_TIVA_CAN_TX_FIFO_DEPTH);
+}
+
+/****************************************************************************
+ * Name: tivacan_handle_errors_wqueue
+ *
+ * Description:
+ *   Periodically handle errors when status interrupts are disabled due to
+ *   overload. Only applicable when CAN error reporting is enabled.
+ *
+ *   Since error messages are processed directly in the ISR, it is easy to
+ *   overwhelm the application with error messages. If tivacan_handle_errors
+ *   fails to send an error message in the ISR, status interrupts are
+ *   disabled and the ISR adds this function to the work queue.
+ *
+ *   On the other hand, when a message is successfully sent, or successfully
+ *   passes through the filters and is received by the application, the ISR
+ *   or the RX kthread will call work_cancel and re-enable status interrupts
+ *   if they are disabled.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver (typed as
+ *         void* for compatibility with worker_t.
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_CAN_ERRORS
+void tivacan_handle_errors_wqueue(void *dev)
+{
+  irqstate_t flags;
+  struct tiva_can_s *canmod = ((struct net_driver_s *)dev)->d_private;
+
+  flags = enter_critical_section();
+  tivacan_handle_errors((struct net_driver_s *)dev);
+  work_queue(LPWORK,
+             &canmod->error_work,
+             tivacan_handle_errors_wqueue,
+             dev,
+             TIVA_CAN_ERR_HANDLER_TICKS);
+  leave_critical_section(flags);
+}
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+/****************************************************************************
+ * Name: tivacan_handle_errors
+ *
+ * Description:
+ *   Process everything in the status register; i.e. initiate bus-off
+ *   recovery and send error messages if desired.  This function may be
+ *   called from either the main ISR or (when error messages are desired but
+ *   the application is unable to handle the volume of errors) from the
+ *   high-priority work queue. However, interrupts must be disabled when
+ *   called from the work queue context to ensure that error messages are
+ *   sent to the application in the correct order.
+ *
+ *   An IRQ is always raised for bus-off and error counter "warning" level,
+ *   so this function may be executed from the ISR while still waiting in
+ *   the work queue. In this case, the call to work_queue in the ISR
+ *   will just reset the timer, which is fine.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   1 if an error message was successfully sent to the application, the
+ *   return value of can_receive() (a negated errno) if it failed, and
+ *   zero if no error message was needed.
+ ****************************************************************************/
+
+int tivacan_handle_errors(struct net_driver_s *dev)
+{
+  uint32_t cansts;
+  uint32_t canerr;
+  struct tiva_can_s *canmod = dev->d_private;
+  struct can_frame    *frame  = (struct can_frame *)canmod->rx_pool;
+
+  int      ret = 0;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  int      errcnt;
+  int      prev_errcnt;
+
+  memset(frame, 0, sizeof(struct can_frame));

Review Comment:
   ```suggestion
     memset(frame, 0, sizeof(*frame));
   ```
   
   Just to future-proof the code against structural changes...



##########
arch/arm/src/tiva/common/tiva_sock_can.c:
##########
@@ -0,0 +1,2371 @@
+/****************************************************************************
+ * arch/arm/src/tiva/common/tiva_sock_can.c
+ * SocketCAN driver implementation for Tiva. Based on the chardev driver.
+ *
+ * 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 <math.h>
+#include <nuttx/mutex.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 "tiva_can.h"
+#include "hardware/tiva_can.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only the last few mailboxes are used for TX to help ensure that responses
+ * to remote request frames we send are always collected by an RX FIFO and
+ * not by the mailbox used to send the remote frame. (The Tiva hardware uses
+ * the mailbox with the lowest number first.)
+ *
+ * This number is zero-indexed, althought the Command Request Register isn't.
+ */
+#define TIVA_CAN_TX_FIFO_START (TIVA_CAN_NUM_MBOXES - \
+        CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+
+/* Frequency of async error message reporting when rate-limited */
+#define TIVA_CAN_ERR_HANDLER_TICKS MSEC2TICK(CONFIG_TIVA_CAN_ERR_HANDLER_PER)
+
+/* For tivacan_initfilter */
+#define TIVA_CAN_FILTER_TYPE_STD 0
+#define TIVA_CAN_FILTER_TYPE_EXT 1
+
+/* Delays *******************************************************************/
+
+/* Defines for bit timing */
+#define CAN_BIT_QUANTA 20
+#define CAN_TRANSMISSION_DELAY 400E-9
+
+#if defined(CONFIG_TIVA_CAN) && (defined(CONFIG_TIVA_CAN0) || \
+        defined(CONFIG_TIVA_CAN1)) && defined(CONFIG_TIVA_SOCKET_CAN)
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#endif /* CONFIG_SCHED_WORKQUEUE */
+
+/* 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
+
+/* Combines two 16-bit registers into a single 32 bit value */
+#define tivacan_readsplitreg32(base, r1, r2) \
+        ((getreg32((base) + (r1)) & 0xffff) | (getreg32((base) + (r2)) << 16))
+
+#define tivacan_get_nwda32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_NWDA1,\
+                               TIVA_CAN_OFFSET_NWDA2)
+#define tivacan_get_txrq32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_TXRQ1,\
+                               TIVA_CAN_OFFSET_TXRQ2)
+#define tivacan_get_msgval32(base) \
+        tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_MSG1VAL,\
+                               TIVA_CAN_OFFSET_MSG2VAL)
+
+#define TIVA_CAN_AUTRECOVER
+
+/* Set the maximum number of iterations for the tivacan_bittiming_set timing
+ * parameters calculation
+ */
+#define TIVA_CAN_SET_TIMING_MAX_TER 5
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef uint32_t tiva_can_fifo_t;
+struct tiva_can_s
+{
+  /* Module number */
+
+  int       modnum;
+
+  /* Registers base address */
+
+  uint32_t  base;
+
+  /* Contents of the CANSTS reg the last time errors were processed. */
+
+  uint32_t  status;
+
+  /* Contents of the CANERR reg the last time errors were processed. */
+
+  uint32_t  errors;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  struct work_s error_work; /* For deferring error work to the work wq */
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* Configured baud rate */
+
+  uint32_t baud;
+
+  /* Interface registers base address for threads threads */
+
+  uint32_t  thd_iface_base;
+
+  /* Interface registers base address reserved exclusively for the ISR */
+
+  uint32_t  isr_iface_base;
+
+  /* Mutex for threads accessing the interface registers */
+
+  mutex_t   thd_iface_lock;
+
+  /* Mutex for claiming, freeing, and potentially resizing RX FIFOs.
+   * The TX FIFO should never be resized at runtime.
+   */
+
+  mutex_t   fifo_lock;
+
+  /* true:ifup false:ifdown */
+
+  bool                bifup;
+
+  /* Interface understood by the network */
+
+  struct net_driver_s dev;
+
+  struct work_s irqwork;  /* For deferring interrupt work to the wq */
+  struct work_s pollwork; /* For deferring poll work to the work wq */
+  struct work_s rxwork;   /* For deferring receive work to the work wq */
+
+  /* flag to repeat the loop getting data from all receive mailboxes */
+
+  uint8_t rx_work_repeat;
+
+  /* index of mailbox which is currently processed for data reception */
+
+  uint8_t rx_mbox_index;
+
+  /* List of all FIFOs. Contains the indices of used mailboxes for the
+   * appropriate FIFO.
+   */
+
+  tiva_can_fifo_t fifos[CONFIG_TIVA_CAN_FILTERS_MAX + 1];
+
+  /* Pointer to default catch-all RX FIFO initialized by the driver. */
+
+  tiva_can_fifo_t *rxdefault_fifo;
+
+  /* Pointer to the permanent, fixed-size TX FIFO. This is always located at
+   * the end of the series of mailboxes to help ensure that responses to
+   * remote request frames go to an RX (filter) FIFO and don't interfere.
+   */
+
+  tiva_can_fifo_t *tx_fifo;
+
+  /* Index in the TX FIFO where new messages should be added. The Tiva CAN
+   * module doesn't support a ring buffer, so new messages are only added
+   * when the TX FIFO is empty (tx_fifo_tail == 0).
+   */
+
+  int tx_fifo_tail;
+
+  /* NuttX interface TX/RX pool */
+
+  uint8_t tx_pool[sizeof(struct can_frame)];
+  uint8_t rx_pool[sizeof(struct can_frame)];
+};
+
+/* This structure represents the CAN bit timing settings as seen by the hw. */
+
+struct tiva_can_timing_s
+{
+  int       prescaler;
+  int       tseg1;
+  int       tseg2;
+  int       sjw;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* CAN error interrupts and ISR */
+
+static int  tivacan_isr(int irq, void *context, void *dev);
+
+/* Internal utility functions */
+
+static int tivacan_txpoll(struct net_driver_s *dev);
+static struct tiva_can_timing_s
+tivacan_bittiming_get(struct net_driver_s *dev);
+static void tivacan_bittiming_set(struct net_driver_s *dev,
+                                  uint32_t desired_baud_rate);
+
+static int tivacan_setup(struct net_driver_s *dev);
+static void tivacan_shutdown(struct net_driver_s *dev);
+static void tivacan_reset(struct net_driver_s *dev);
+static int tivacan_send(struct net_driver_s *dev);
+static int tivacan_alloc_fifo(struct net_driver_s *dev, int depth);
+static void tivacan_free_fifo(struct net_driver_s *dev,
+                              tiva_can_fifo_t *fifo);
+static void tivacan_disable_mbox(struct net_driver_s *dev,
+                                 uint32_t iface_base,
+                                 int num);
+static int  tivacan_initfilter(struct net_driver_s *dev,
+                               tiva_can_fifo_t  *fifo,
+                               void             *filter,
+                               int               type);
+static void tivacan_receive_work(void *priv);
+int tivacan_handle_errors(struct net_driver_s *dev);
+
+static bool tivacan_txready(struct net_driver_s *dev);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+void tivacan_handle_errors_wqueue(void *dev);
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+/* NuttX callback functions */
+
+static int tivacan_ifup(struct net_driver_s *dev);
+static int tivacan_ifdown(struct net_driver_s *dev);
+
+static void tivacan_txavail_work(void *arg);
+static int tivacan_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int tivacan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                unsigned long arg);
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_TIVA_CAN0
+static struct tiva_can_s g_tivacan0priv =
+    {
+        .modnum          = 0,
+        .base            = TIVA_CAN_BASE(0),
+        .thd_iface_base  = TIVA_CAN_IFACE_BASE(0, 0),
+        .isr_iface_base  = TIVA_CAN_IFACE_BASE(0, 1),
+        .bifup           = false,  /* true:ifup false:ifdown */
+        .baud            = CONFIG_TIVA_CAN0_BAUD,
+        .rx_work_repeat  = 0,
+        .rx_mbox_index   = 0,
+    };
+
+#endif /* CONFIG_TIVA_CAN0 */
+
+#ifdef CONFIG_TIVA_CAN1
+static struct tiva_can_s g_tivacan1priv =
+    {
+        .modnum           = 1,
+        .base             = TIVA_CAN_BASE(1),
+        .thd_iface_base   = TIVA_CAN_IFACE_BASE(1, 0),
+        .isr_iface_base   = TIVA_CAN_IFACE_BASE(1, 1),
+        .bifup            = false,  /* true:ifup false:ifdown */
+        .baud             = CONFIG_TIVA_CAN1_BAUD,
+        .rx_work_repeat   = 0,
+        .rx_mbox_index    = 0,
+    };
+#endif /* CONFIG_TIVA_CAN1 */
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tivacan_receive_work
+ *
+ * Description: Worker callback for processing received messages
+ ****************************************************************************/
+
+static void tivacan_receive_work(void *priv)
+{
+  struct net_driver_s *dev    = (struct net_driver_s *)priv;
+  struct tiva_can_s   *canmod = dev->d_private;
+  struct can_frame    *frame  = (struct can_frame *)canmod->rx_pool;
+  uint8_t             *mbox   = &canmod->rx_mbox_index;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  int ret;
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* The while loop iteration looks for new data in all RX mailboxes.
+   * If a message has arrived while in the loop the canmod->rx_work_repeat
+   * should be set so that the reception loop is repeated.
+   */
+
+  while (canmod->rx_work_repeat)
+    {
+      canmod->rx_work_repeat = 0;
+      nxmutex_lock(&canmod->thd_iface_lock);
+
+      /* Process the received message(s). Since hardware RX FIFOS are used
+       * and new messages are received into the mailbox with the lowest
+       * number, we can't use CANINT since a new message might arrive in a
+       * mailbox we'd already read from before the higher-address mailboxes
+       * in the FIFO were emptied. (Besides, the ISR clears CANINT).
+       */
+
+      for (*mbox = 0;
+          *mbox < TIVA_CAN_NUM_MBOXES - CONFIG_TIVA_CAN_TX_FIFO_DEPTH;
+          ++*mbox)
+        {
+          uint32_t reg;
+
+          /* New Data registers */
+
+          reg = tivacan_get_nwda32(canmod->base);
+
+          if (!(reg & ~*(canmod->tx_fifo)))
+            {
+              /* No new messages to process in the RX FIFOs */
+
+              break;
+            }
+          else if (! (reg & 1 << *mbox))
+            {
+              /* No new message in this mailbox */
+
+              continue;
+            }
+
+          /* Command Mask register
+           * Pull arbitration bits, control bits, and data bits from the
+           * message SRAM. Clear the new data bit; the ISR cleared the IRQ.
+           */
+
+          reg =   TIVA_CANIF_CMSK_ARB       | TIVA_CANIF_CMSK_CONTROL
+              | TIVA_CANIF_CMSK_NEWDAT    | TIVA_CANIF_CMSK_DATAA
+              | TIVA_CANIF_CMSK_DATAB;
+          putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+          /* Submit request (1-indexed) */
+
+          putreg32(*mbox + 1,
+                   canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+          /* Wait for response */
+
+          while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+              & TIVA_CANIF_CRQ_BUSY);
+
+          /* ARB2 - upper chunk of the message ID, "direction," and extended
+           * message indication. (NOTE: sadly, DIR is always 0 (receive) even
+           * for RTR messages. There is no way to determine whether the RTR
+           * bit was set on a received frame.
+           */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+#ifdef CONFIG_NET_CAN_EXTID
+          uint8_t ext_id = (0 != (reg & TIVA_CANIF_ARB2_XTD));
+
+          if (ext_id)
+            {
+              frame->can_id = (reg & TIVA_CANIF_ARB2_ID_EXT_MASK)
+                      << TIVA_CANIF_ARB2_ID_EXT_PRESHIFT;
+
+              /* ARB1 - lower chunk of the message ID for extended IDs */
+
+              reg = getreg32(
+                  canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+              frame->can_id |= reg & TIVA_CANIF_ARB1_ID_EXT_MASK;
+            }
+          else
+            {
+              frame->can_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                      >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+            }
+#else
+          frame->can_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                  >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+#endif /* CONFIG_NET_CAN_EXTID */
+
+          /* Message Control - remote enable (RMTEN) & data length code */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+          frame->can_dlc = reg & TIVA_CANIF_MCTL_DLC_MASK;
+
+          /* Data registers - these are little endian but the uint8_t
+           * array in can_msg_s is big endian.
+           */
+
+          for (int j = 0; j < (frame->can_dlc + 1) / 2; ++j)
+            {
+              reg = getreg32(canmod->thd_iface_base
+                             + TIVA_CANIF_OFFSET_DATA(j));
+
+              frame->data[j * 2]      = (reg & TIVA_CANIF_DATA_HBYTE_MASK)
+                      >> TIVA_CANIF_DATA_HBYTE_SHIFT;
+              frame->data[j * 2 + 1]  = (reg & TIVA_CANIF_DATA_LBYTE_MASK)
+                      >> TIVA_CANIF_DATA_LBYTE_SHIFT;
+            }
+
+          /* Point the d_buf buffer to the received can frame */
+
+          dev->d_len = sizeof(struct can_frame);
+          dev->d_buf = (uint8_t *)frame;
+
+          NETDEV_RXPACKETS(dev);
+
+          /* Process the received frame */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+          ret = can_input(dev);
+#else
+          can_input(dev);
+#endif /* CONFIG_NET_CAN_ERRORS */
+        }
+
+      /* 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.
+       */
+
+      dev->d_buf = (uint8_t *)canmod->tx_pool;
+      nxmutex_unlock(&canmod->thd_iface_lock);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+      if (ret == OK)
+        {
+          /* Switch to processing errors by interrupt when the application
+           * is able to keep up with the message volume and messages are
+           * being received successfully.
+           */
+
+          work_cancel(CANWORK, &canmod->error_work);
+          modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+                      0, TIVA_CAN_CTL_SIE);
+        }
+#endif /* CONFIG_NET_CAN_ERRORS */
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+/****************************************************************************
+ * Name: tivacan_ioctl
+ *
+ * Description:
+ *   Perform the action requested by an ioctl syscall that was not handled by
+ *   the "upper half" CAN driver.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   cmd - One of the CANIOC_* ioctls
+ *   arg - Second argument to the ioctl call.
+ *
+ * Returned value:
+ *   Zero on success; a negated errno on failure
+ ****************************************************************************/
+
+static int tivacan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                                unsigned long arg)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  int ret;
+
+  /* TODO: The IOCTL commands are not implemented yet. */
+
+#warning Missing logic
+
+  switch (cmd)
+  {
+    default:
+      canerr("ERROR: Unrecognized ioctl\n");
+      ret = -ENOSYS;
+      break;
+  }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Name: tivacan_send
+ *
+ * Description:
+ *   Enqueue a message to transmit into the hardware FIFO. Returns
+ *   immediately if the FIFO is full. This function must not fail when
+ *   tivacan_txready returns true, or the message will be lost.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   Zero on success; a negated errorno on failure.
+ ****************************************************************************/
+
+static int tivacan_send(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+  struct can_frame *frame = (struct can_frame *)dev->d_buf;
+  uint32_t reg = 0;
+
+  /* REVISIT: Should can_txdone be called to advance the FIFO buffer
+   * head when dev_send is called when it shouldn't be?
+   */
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      NETDEV_TXDONE(canmod->dev);
+      return -ENETDOWN;
+    }
+
+  if (canmod->tx_fifo_tail >= CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+    {
+      NETDEV_TXDONE(canmod->dev);
+      return -EBUSY;
+    }
+
+  nxmutex_lock(&canmod->thd_iface_lock);
+
+  /* Protect the message object due the minute chance that the mailbox was
+   * previously used for a remote frame and could receive messages, causing
+   * an SRAM conflict.
+   */
+
+  tivacan_disable_mbox(dev, canmod->thd_iface_base,
+                       TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail);
+
+  /* Command mask register */
+
+  reg = TIVA_CANIF_CMSK_WRNRD | TIVA_CANIF_CMSK_ARB | TIVA_CANIF_CMSK_CONTROL
+      | TIVA_CANIF_CMSK_CLRINTPND
+      | TIVA_CANIF_CMSK_DATAA | TIVA_CANIF_CMSK_DATAB;
+  putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+  /* Arbitration registers (ARB1 and ARB2)
+   * Check if the frame is RTR
+   */
+
+  uint8_t rtr = frame->can_id & CAN_RTR_FLAG;
+  frame->can_id &= ~CAN_RTR_FLAG;
+
+#ifdef CONFIG_NET_CAN_EXTID
+  if (frame->can_id & CAN_EFF_FLAG)
+    {
+      frame->can_id &= ~CAN_EFF_FLAG;
+
+      reg = frame->can_id & TIVA_CANIF_ARB1_ID_EXT_MASK;
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+      reg = (frame->can_id >> TIVA_CANIF_ARB2_ID_EXT_PRESHIFT)
+              & TIVA_CANIF_ARB2_ID_EXT_MASK;
+      reg |= TIVA_CANIF_ARB2_MSGVAL | TIVA_CANIF_ARB2_XTD;
+
+#ifdef CONFIG_CAN_USE_RTR
+      reg |= (rtr ? 0 : TIVA_CANIF_ARB2_DIR);
+#else
+      reg |= TIVA_CANIF_ARB2_DIR;
+#endif /* CONFIG_CAN_USE_RTR */
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+    }
+  else
+    {
+#endif /* CONFIG_NET_CAN_EXTID */
+      reg = (frame->can_id << TIVA_CANIF_ARB2_ID_STD_SHIFT)
+          & TIVA_CANIF_ARB2_ID_STD_MASK;
+
+      reg |= TIVA_CANIF_ARB2_MSGVAL;
+
+#ifdef CONFIG_CAN_USE_RTR
+      reg |= (rtr ? 0 : TIVA_CANIF_ARB2_DIR);
+#else
+      reg |= TIVA_CANIF_ARB2_DIR;
+#endif /* CONFIG_CAN_USE_RTR */
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+#ifdef CONFIG_NET_CAN_EXTID
+    }
+#endif /* CONFIG_NET_CAN_EXTID */
+
+  /* Message Control (MCTL) register */
+
+  reg = frame->can_dlc & TIVA_CANIF_MCTL_DLC_MASK;
+
+  /* Protect the mailbox from rx messages; see comment in tivacan_setup */
+
+  reg |= TIVA_CANIF_MCTL_UMASK;
+
+  /* NOTE: Not sure whether the End Of Buffer bit means anything here */
+
+  reg |= TIVA_CANIF_MCTL_NEWDAT | TIVA_CANIF_MCTL_TXIE
+      | TIVA_CANIF_MCTL_TXRQST | TIVA_CANIF_MCTL_EOB;
+  putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+
+  /* Data registers, there are four of them. */
+
+  for (int i = 0; i < (frame->can_dlc + 1) / 2; ++i)
+    {
+      reg = (uint32_t)frame->data[i * 2]
+                                  << TIVA_CANIF_DATA_HBYTE_SHIFT;
+      reg |= (uint32_t)frame->data[i * 2 + 1]
+                                   << TIVA_CANIF_DATA_LBYTE_SHIFT;
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_DATA(i));
+    }
+
+  /* Submit request; this register is one-indexed. */
+
+  putreg32(TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail + 1,
+           canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+  /* Wait for completion */
+
+  while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+      & TIVA_CANIF_CRQ_BUSY);
+
+  nxmutex_unlock(&canmod->thd_iface_lock);
+
+  /* Move to the next message in the h/w TX FIFO.
+   * Tell the upper-half the message has been submitted... this recurses
+   * back on us until the software TX FIFO is empty.
+   */
+
+  ++canmod->tx_fifo_tail;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: tivacan_txready
+ *
+ * Description:
+ *   Determine whether the hardware is ready to transmit another TX message.
+ *   This checks for bus-off conditions as well as if the FIFO is full.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   True if the hardware can accept another TX message, false otherwise.
+ ****************************************************************************/
+
+static bool tivacan_txready(struct net_driver_s *dev)
+{
+  struct tiva_can_s *canmod = dev->d_private;
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      return false;
+    }
+
+  return (canmod->tx_fifo_tail < CONFIG_TIVA_CAN_TX_FIFO_DEPTH);
+}
+
+/****************************************************************************
+ * Name: tivacan_handle_errors_wqueue
+ *
+ * Description:
+ *   Periodically handle errors when status interrupts are disabled due to
+ *   overload. Only applicable when CAN error reporting is enabled.
+ *
+ *   Since error messages are processed directly in the ISR, it is easy to
+ *   overwhelm the application with error messages. If tivacan_handle_errors
+ *   fails to send an error message in the ISR, status interrupts are
+ *   disabled and the ISR adds this function to the work queue.
+ *
+ *   On the other hand, when a message is successfully sent, or successfully
+ *   passes through the filters and is received by the application, the ISR
+ *   or the RX kthread will call work_cancel and re-enable status interrupts
+ *   if they are disabled.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver (typed as
+ *         void* for compatibility with worker_t.
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_CAN_ERRORS
+void tivacan_handle_errors_wqueue(void *dev)
+{
+  irqstate_t flags;
+  struct tiva_can_s *canmod = ((struct net_driver_s *)dev)->d_private;
+
+  flags = enter_critical_section();
+  tivacan_handle_errors((struct net_driver_s *)dev);
+  work_queue(LPWORK,
+             &canmod->error_work,
+             tivacan_handle_errors_wqueue,
+             dev,
+             TIVA_CAN_ERR_HANDLER_TICKS);
+  leave_critical_section(flags);
+}
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+/****************************************************************************
+ * Name: tivacan_handle_errors
+ *
+ * Description:
+ *   Process everything in the status register; i.e. initiate bus-off
+ *   recovery and send error messages if desired.  This function may be
+ *   called from either the main ISR or (when error messages are desired but
+ *   the application is unable to handle the volume of errors) from the
+ *   high-priority work queue. However, interrupts must be disabled when
+ *   called from the work queue context to ensure that error messages are
+ *   sent to the application in the correct order.
+ *
+ *   An IRQ is always raised for bus-off and error counter "warning" level,
+ *   so this function may be executed from the ISR while still waiting in
+ *   the work queue. In this case, the call to work_queue in the ISR
+ *   will just reset the timer, which is fine.
+ *
+ * Input parameters:
+ *   dev - An instance of the network driver
+ *
+ * Returned value:
+ *   1 if an error message was successfully sent to the application, the
+ *   return value of can_receive() (a negated errno) if it failed, and
+ *   zero if no error message was needed.
+ ****************************************************************************/
+
+int tivacan_handle_errors(struct net_driver_s *dev)
+{
+  uint32_t cansts;
+  uint32_t canerr;
+  struct tiva_can_s *canmod = dev->d_private;
+  struct can_frame    *frame  = (struct can_frame *)canmod->rx_pool;
+
+  int      ret = 0;
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  int      errcnt;
+  int      prev_errcnt;
+
+  memset(frame, 0, sizeof(struct can_frame));
+  frame->can_dlc = CAN_ERR_DLC;
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+  /* Clear the status interrupt by reading CANSTS */
+
+  cansts = getreg32(canmod->base + TIVA_CAN_OFFSET_STS);
+
+  /* Get the contents of the error counter register */
+
+  canerr = getreg32(canmod->base + TIVA_CAN_OFFSET_ERR);
+
+  /* Bus-off handling
+   * This is a new bus-off event. Proceed with error processing (if error
+   * reporting is enabled) so that the cause of the "last straw" error is
+   * included with the bus-off error message sent to the app.
+   * Clear the INIT bit if autorecovery is enabled.
+   */
+
+  if (cansts & TIVA_CAN_STS_BOFF
+      && ! (canmod->status & TIVA_CAN_STS_BOFF))
+    {
+#ifdef TIVA_CAN_AUTRECOVER
+      modreg32(0, TIVA_CAN_CTL_INIT,
+               canmod->base + TIVA_CAN_OFFSET_CTL);
+#endif /* TIVA_CAN_AUTRECOVER */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+      frame->can_id = CAN_ERR_BUSOFF;
+#endif /* CONFIG_NET_CAN_ERRORS */
+    }
+#ifdef CONFIG_NET_CAN_ERRORS
+
+  /* If this an existing bus-off event and the module is recovering, don't do
+   * anything. The interrupt is caused by the Last Error Code being updated
+   * with a bit-0 error to indicate that the module is recovering.
+   */
+
+  else if (cansts & TIVA_CAN_STS_BOFF
+      && canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      goto save_regs_and_return;
+    }
+
+  /* The module has just finished recovering from bus-off; indicate this to
+   * the app. If the LEC is a BIT0 error it must be ignored (see below).
+   * We still check for other errors though because if we're running from the
+   * work queue, the module could have recovered and then immediately
+   * encountered another error without us being notified in between.
+   */
+
+  else if (! (cansts & TIVA_CAN_STS_BOFF)
+      && canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      frame->can_id = CAN_ERR_RESTARTED;
+    }
+
+  /* Lost arbitration detection */
+
+  if (tivacan_get_txrq32(canmod->base) & ~tivacan_get_nwda32(canmod->base)
+  && cansts & TIVA_CAN_STS_RXOK)
+    {
+      /* Received a message successfually but sending a message failed */

Review Comment:
   ```suggestion
         /* Received a message successfully but sending a message failed */
   ```



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