You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/01/31 06:07:29 UTC

[nuttx] branch master updated: Added SocketCAN driver implementation to the tiva chip, modified the EK-TC1294XL launchpad board to use the new SocketCAN API

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 372fee9412 Added SocketCAN driver implementation to the tiva chip, modified the EK-TC1294XL launchpad board to use the new SocketCAN API
372fee9412 is described below

commit 372fee9412bbdffb13cb8cb14916b8220f6c03fe
Author: David Vosahlik <dv...@gmail.com>
AuthorDate: Wed Jan 25 15:38:02 2023 +0100

    Added SocketCAN driver implementation to the tiva chip, modified the EK-TC1294XL launchpad board to use the new SocketCAN API
---
 arch/arm/src/tiva/Kconfig                         |   71 +-
 arch/arm/src/tiva/Make.defs                       |    6 +-
 arch/arm/src/tiva/common/tiva_sock_can.c          | 2373 +++++++++++++++++++++
 arch/arm/src/tiva/tiva_can.h                      |   19 +-
 boards/arm/tiva/tm4c1294-launchpad/src/tm4c_can.c |   30 +
 5 files changed, 2473 insertions(+), 26 deletions(-)

diff --git a/arch/arm/src/tiva/Kconfig b/arch/arm/src/tiva/Kconfig
index c5935ad070..0a3dd5b180 100644
--- a/arch/arm/src/tiva/Kconfig
+++ b/arch/arm/src/tiva/Kconfig
@@ -436,7 +436,6 @@ config TIVA_CAN
 	bool
 	default n
 	select CAN
-	select ARCH_HAVE_CAN_ERRORS
 	select CAN_TXREADY
 	select CAN_USE_RTR
 
@@ -612,33 +611,12 @@ config TIVA_CAN0
 	default n
 	depends on TIVA_HAVE_CAN0
 	select TIVA_CAN
-
-config TIVA_CAN0_PRIO
-	int "CAN0 kthread priority"
-	default 300
-	depends on TIVA_CAN0
-	---help---
-		The Tiva CAN driver retrieves messages using a kthread rather
-		than in the ISR or using a work queue. The ISR signals the
-		kthread, but the kthread can be preempted if needed. This
-		option sets the thread priority for CAN module 0.
-
 config TIVA_CAN1
 	bool "CAN1"
 	default n
 	depends on TIVA_HAVE_CAN1
 	select TIVA_CAN
 
-config TIVA_CAN1_PRIO
-	int "CAN1 kthread priority"
-	default 300
-	depends on TIVA_CAN1
-	---help---
-		The Tiva CAN driver retrieves messages using a kthread rather
-		than in the ISR or using a work queue. The ISR signals the
-		kthread, but the kthread can be preempted if needed. This
-		option sets the thread priority for CAN module 1.
-
 config TIVA_QEI0
 	bool "QEI0"
 	default n
@@ -1520,10 +1498,55 @@ endif # TIVA_ADC
 
 menu "CAN Driver Configuration"
 	depends on TIVA_CAN
+choice
+	prompt "CAN bus driver selection"
+	default TIVA_SOCKET_CAN
+	
+config TIVA_SOCKET_CAN
+	bool "Use SocketCAN driver"
+	depends on (TIVA_CAN0 || TIVA_CAN1) && NET_CAN
+	select NET_CAN_HAVE_ERRORS
+	
+config TIVA_CHAR_DEV_CAN
+	bool "Character device driver"
+	depends on (TIVA_CAN0 || TIVA_CAN1) && !NET_CAN
+	select ARCH_HAVE_CAN_ERRORS
+	
+endchoice # CAN driver selection
+
+config TIVA_CAN0_PRIO
+	int "CAN0 kthread priority"
+	default 300
+	depends on TIVA_CAN0 && TIVA_CHAR_DEV_CAN
+	---help---
+		The Tiva CAN driver retrieves messages using a kthread rather
+		than in the ISR or using a work queue. The ISR signals the
+		kthread, but the kthread can be preempted if needed. This
+		option sets the thread priority for CAN module 0.
+		
+config TIVA_CAN0_BAUD
+	int "CAN0 baud rate kb/s"
+	default 125
+	depends on TIVA_CAN0 && TIVA_SOCKET_CAN
+	
+config TIVA_CAN1_BAUD
+	int "CAN1 baud rate kb/s"
+	default 125
+	depends on TIVA_CAN1 && TIVA_SOCKET_CAN
+
+config TIVA_CAN1_PRIO
+	int "CAN1 kthread priority"
+	default 300
+	depends on TIVA_CAN1 && TIVA_CHAR_DEV_CAN
+	---help---
+		The Tiva CAN driver retrieves messages using a kthread rather
+		than in the ISR or using a work queue. The ISR signals the
+		kthread, but the kthread can be preempted if needed. This
+		option sets the thread priority for CAN module 1.
 
 config TIVA_CAN_REGDEBUG
 	bool "CAN register level debug"
-	depends on  DEBUG_CAN_INFO
+	depends on  DEBUG_CAN_INFO && TIVA_CHAR_DEV_CAN
 	default n
 	---help---
 		Output detailed register-level CAN device debug information.
@@ -1589,7 +1612,7 @@ config TIVA_CAN_ERR_HANDLER_PER
 	int "Rate-limited error handling period (milliseconds)"
 	range 10 1000
 	default 100
-	depends on CAN_ERRORS
+	depends on CAN_ERRORS || NET_CAN_ERRORS
 	---help---
 		When error messages (CAN_ERRORS) are enabled, the Tiva
 		CAN driver will disable interrupts for individual errors
diff --git a/arch/arm/src/tiva/Make.defs b/arch/arm/src/tiva/Make.defs
index 3c4b66324d..4b73d350d6 100644
--- a/arch/arm/src/tiva/Make.defs
+++ b/arch/arm/src/tiva/Make.defs
@@ -91,10 +91,14 @@ ifeq ($(CONFIG_TIVA_ADC),y)
   CHIP_CSRCS += tiva_adclib.c
 endif
 
-ifeq ($(CONFIG_TIVA_CAN),y)
+ifeq ($(CONFIG_TIVA_CHAR_DEV_CAN),y)
   CHIP_CSRCS += tiva_can.c
 endif
 
+ifeq ($(CONFIG_TIVA_SOCKET_CAN),y)
+  CHIP_CSRCS += tiva_sock_can.c
+endif
+
 ifeq ($(CONFIG_TIVA_ETHERNET),y)
 ifeq ($(CONFIG_ARCH_CHIP_LM3S),y)
   CHIP_CSRCS += lm3s_ethernet.c
diff --git a/arch/arm/src/tiva/common/tiva_sock_can.c b/arch/arm/src/tiva/common/tiva_sock_can.c
new file mode 100644
index 0000000000..033a5dfa4f
--- /dev/null
+++ b/arch/arm/src/tiva/common/tiva_sock_can.c
@@ -0,0 +1,2373 @@
+/****************************************************************************
+ * 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, although 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!
+ */
+#if !defined(CONFIG_SCHED_LPWORK)
+#  warning "SocketCAN will run on HPWORK queue (not recommended). Consider enabling LPWORK queue."
+#endif /* !CONFIG_SCHED_LPWORK */
+#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 to 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(*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 successfully 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 tivacan_ifdown().
+   */
+
+  ninfo("callbacks done\n");
+
+  nxmutex_init(&canmod->thd_iface_lock);
+  nxmutex_init(&canmod->fifo_lock);
+
+  tivacan_ifdown(&canmod->dev);
+
+  /* Register the device with the OS so that socket IOCTLs can be performed */
+
+  ret = netdev_register(&canmod->dev, NET_LL_CAN);
+
+  if (ret < 0)
+    {
+      canerr("ERROR: can_register failed: %d\n", ret);
+    }
+
+  return ret;
+}
+
+#endif /* defined(CONFIG_TIVA_CAN) && (defined(CONFIG_TIVA_CAN0) || \
+        * defined(CONFIG_TIVA_CAN1)) && defined(CONFIG_TIVA_SOCKET_CAN)
+        */
diff --git a/arch/arm/src/tiva/tiva_can.h b/arch/arm/src/tiva/tiva_can.h
index 1ac547c254..d1fa85a581 100644
--- a/arch/arm/src/tiva/tiva_can.h
+++ b/arch/arm/src/tiva/tiva_can.h
@@ -61,7 +61,7 @@ extern "C"
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/
-
+#ifdef CONFIG_TIVA_CHAR_DEV_CAN
 /****************************************************************************
  * Name: tiva_can_initialize
  *
@@ -78,6 +78,23 @@ extern "C"
  ****************************************************************************/
 
 int tiva_can_initialize(char *devpath, int modnum);
+#elif CONFIG_TIVA_SOCKET_CAN
+/****************************************************************************
+ * Name: tiva_cansockinitialize
+ *
+ * Description:
+ *   Initialize the selected CAN module using socket net API
+ *
+ * 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);
+#endif
 
 #undef EXTERN
 #if defined(__cplusplus)
diff --git a/boards/arm/tiva/tm4c1294-launchpad/src/tm4c_can.c b/boards/arm/tiva/tm4c1294-launchpad/src/tm4c_can.c
index da9dc8bed1..5c5fa43fd7 100644
--- a/boards/arm/tiva/tm4c1294-launchpad/src/tm4c_can.c
+++ b/boards/arm/tiva/tm4c1294-launchpad/src/tm4c_can.c
@@ -78,6 +78,7 @@ int tm4c_can_setup(void)
       goto configgpio_error;
     }
 
+#    ifdef CONFIG_TIVA_CHAR_DEV_CAN
   /* Call tiva_can_initialize() to get an instance of CAN interface 0
    * and register it.
    */
@@ -88,6 +89,20 @@ int tm4c_can_setup(void)
       canerr("ERROR:  Failed to get/register CAN interface 0\n");
       return ret;
     }
+#    endif /* CONFIG_TIVA_CHAR_DEV_CAN */
+
+#    if defined(CONFIG_TIVA_SOCKET_CAN)
+  /* Call tiva_cansockinitialize() to get an instance of CAN interface 0
+   * and register it.
+   */
+
+  ret = tiva_cansockinitialize(0);
+  if (ret < 0)
+    {
+      canerr("ERROR:  Failed to get/register CAN interface 0\n");
+      return ret;
+    }
+#    endif /* CONFIG_TIVA_SOCKET_CAN */
 #  endif /* CONFIG_TIVA_CAN0 */
 
 #  ifdef CONFIG_TIVA_CAN1
@@ -105,6 +120,7 @@ int tm4c_can_setup(void)
       goto configgpio_error;
     }
 
+#    ifdef CONFIG_TIVA_CHAR_DEV_CAN
   /* Call tiva_can_initialize() to get an instance of CAN interface 1
    * and register it.
    */
@@ -115,6 +131,20 @@ int tm4c_can_setup(void)
       canerr("ERROR:  Failed to get/register CAN interface 1\n");
       return ret;
     }
+
+#    endif /* CONFIG_TIVA_CHAR_DEV_CAN */
+#    if defined(CONFIG_TIVA_SOCKET_CAN)
+  /* Call tiva_cansockinitialize() to get an instance of CAN interface 0
+   * and register it.
+   */
+
+  ret = tiva_cansockinitialize(0);
+  if (ret < 0)
+    {
+      canerr("ERROR:  Failed to get/register CAN interface 0\n");
+      return ret;
+    }
+#    endif /* CONFIG_TIVA_SOCKET_CAN */
 #  endif /* CONFIG_TIVA_CAN1 */
 
   return OK;