You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/06/02 14:09:45 UTC

[incubator-nuttx] 27/31: Implement NET_CAN_RAW_TX_DEADLINE in SocketCAN and S32K1XX FlexCAN driver

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

gnutt pushed a commit to branch SocketCAN
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit f08151cc60ddc05b952de091aba53c68ef18c89c
Author: Peter van der Perk <pe...@nxp.com>
AuthorDate: Tue Mar 17 13:57:22 2020 +0100

    Implement NET_CAN_RAW_TX_DEADLINE in SocketCAN and S32K1XX FlexCAN driver
---
 arch/arm/src/s32k1xx/s32k1xx_flexcan.c | 195 ++++++++++++++++++++++++++++-----
 net/can/Kconfig                        |  19 ++++
 net/can/can.h                          |  24 ++++
 net/can/can_send.c                     | 158 +++++++++++++++++++++++++-
 net/can/can_sockif.c                   |   4 +-
 net/devif/devif_cansend.c              |   4 +-
 6 files changed, 372 insertions(+), 32 deletions(-)

diff --git a/arch/arm/src/s32k1xx/s32k1xx_flexcan.c b/arch/arm/src/s32k1xx/s32k1xx_flexcan.c
index 5c02aa0..0325033 100644
--- a/arch/arm/src/s32k1xx/s32k1xx_flexcan.c
+++ b/arch/arm/src/s32k1xx/s32k1xx_flexcan.c
@@ -49,7 +49,7 @@
 #include "s32k1xx_pin.h"
 #include "s32k1xx_flexcan.h"
 
-#ifdef CONFIG_NET_TIMESTAMP
+#ifdef CONFIG_NET_CMSG
 #include <sys/time.h>
 #endif
 
@@ -117,24 +117,32 @@
 
 #define POOL_SIZE                   1
 
-#ifdef CONFIG_NET_TIMESTAMP
+#ifdef CONFIG_NET_CMSG
 #define MSG_DATA                    sizeof(struct timeval)
 #else
 #define MSG_DATA                    0
 #endif
 
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+
+#  if !defined(CONFIG_SCHED_WORKQUEUE)
+#    error Work queue support is required
+#  endif
+
+#define TX_TIMEOUT_WQ
+#endif
+
 /* Interrupt flags for RX fifo */
 #define IFLAG1_RXFIFO               (CAN_FIFO_NE | CAN_FIFO_WARN | CAN_FIFO_OV)
 
 static int peak_tx_mailbox_index_ = 0;
 
-#ifdef WORK_QUEUE
 /* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per
  * second.
  */
 
 #define S32K1XX_WDDELAY     (1*CLK_TCK)
-#endif
+#define S32K1XX_TXTIMEOUT   ((CONFIG_NET_CAN_RAW_TX_POLL/1000)*CLK_TCK)
 
 /****************************************************************************
  * Private Types
@@ -198,6 +206,18 @@ struct mb_s
 #endif
 };
 
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+#define TX_ABORT -1
+#define TX_FREE 0
+#define TX_BUSY 1
+
+struct txmbstats
+{
+  struct timeval deadline;
+  uint32_t pending; /* -1 = abort, 0 = free, 1 = busy  */
+};
+#endif
+
 /* The s32k1xx_driver_s encapsulates all state information for a single
  * hardware interface
  */
@@ -211,6 +231,8 @@ struct s32k1xx_driver_s
   uint8_t phyaddr;             /* Selected PHY address */
 #ifdef WORK_QUEUE
   WDOG_ID txpoll;              /* TX poll timer */
+#endif
+#ifdef TX_TIMEOUT_WQ
   WDOG_ID txtimeout;           /* TX timeout timer */
 #endif
   struct work_s irqwork;       /* For deferring interrupt work to the work queue */
@@ -229,6 +251,10 @@ struct s32k1xx_driver_s
 
   struct mb_s *rx;
   struct mb_s *tx;
+
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+  struct txmbstats txmb[TXMBCOUNT];
+#endif
 };
 
 /****************************************************************************
@@ -285,6 +311,10 @@ static void s32k1xx_setenable(uint32_t enable);
 static void s32k1xx_setfreeze(uint32_t freeze);
 static uint32_t s32k1xx_waitmcr_change(uint32_t mask,
                                        uint32_t target_state);
+#ifdef TX_TIMEOUT_WQ
+static void s32k1xx_checkandaborttx(struct s32k1xx_driver_s *priv,
+        uint32_t mbi, struct timeval *now);
+#endif
 
 /* Interrupt handling */
 
@@ -301,6 +331,10 @@ static int  s32k1xx_flexcan_interrupt(int irq, FAR void *context,
 static void s32k1xx_poll_work(FAR void *arg);
 static void s32k1xx_polltimer_expiry(int argc, uint32_t arg, ...);
 #endif
+#ifdef TX_TIMEOUT_WQ
+static void s32k1xx_txtimeout_work(FAR void *arg);
+static void s32k1xx_txtimeout_expiry(int argc, uint32_t arg, ...);
+#endif
 
 /* NuttX callback functions */
 
@@ -411,6 +445,34 @@ static int s32k1xx_transmit(FAR struct s32k1xx_driver_s *priv)
       return 0;       /* No transmission for you! */
     }
 
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+      if (priv->dev.d_sndlen > priv->dev.d_len)
+        {
+          struct timeval *tv =
+                 (struct timeval *)(priv->dev.d_buf + priv->dev.d_len);
+          priv->txmb[mbi].deadline = *tv;
+        }
+      else
+        {
+          /* Default TX deadline defined in NET_CAN_RAW_DEFAULT_TX_DEADLINE */
+
+          if (CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE > 0)
+            {
+              struct timespec ts;
+              clock_systimespec(&ts);
+              priv->txmb[mbi].deadline.tv_sec = ts.tv_sec +
+                      CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000;
+              priv->txmb[mbi].deadline.tv_usec = (ts.tv_nsec / 1000) +
+                      CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE % 1000000;
+            }
+          else
+            {
+              priv->txmb[mbi].deadline.tv_sec = 0;
+              priv->txmb[mbi].deadline.tv_usec = 0;
+            }
+        }
+#endif
+
   peak_tx_mailbox_index_ =
     (peak_tx_mailbox_index_ > mbi ? peak_tx_mailbox_index_ : mbi);
 
@@ -514,6 +576,16 @@ static int s32k1xx_transmit(FAR struct s32k1xx_driver_s *priv)
   regval |= mb_bit;
   putreg32(regval, S32K1XX_CAN0_IMASK1);
 
+  /* Increment statistics */
+
+  NETDEV_TXPACKETS(&priv->dev);
+
+  /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+#ifdef TX_TIMEOUT_WQ
+  wd_start(priv->txtimeout, S32K1XX_TXTIMEOUT, s32k1xx_txtimeout_expiry, 1,
+           (wdparm_t)priv);
+#endif
+
   return OK;
 }
 
@@ -544,8 +616,6 @@ static int s32k1xx_transmit(FAR struct s32k1xx_driver_s *priv)
 
 static int s32k1xx_txpoll(struct net_driver_s *dev)
 {
-  #warning Missing logic
-
   FAR struct s32k1xx_driver_s *priv =
     (FAR struct s32k1xx_driver_s *)dev->d_private;
 
@@ -560,15 +630,6 @@ static int s32k1xx_txpoll(struct net_driver_s *dev)
           /* Send the packet */
 
           s32k1xx_transmit(priv);
-#if 0
-          /* FIXME implement ring buffer and increment pointer just like the
-           * enet driver??
-           */
-
-          priv->dev.d_buf =
-            (uint8_t *)s32k1xx_swap32(
-              (uint32_t)priv->txdesc[priv->txhead].data);
-#endif
 
           /* Check if there is room in the device to hold another packet. If
            * not, return a non-zero value to terminate the poll.
@@ -777,15 +838,20 @@ static void s32k1xx_txdone(FAR struct s32k1xx_driver_s *priv, uint32_t flags)
 {
   #warning Missing logic
 
+#ifdef TX_TIMEOUT_WQ
   /* We are here because a transmission completed, so the watchdog can be
    * canceled.
    */
 
-#ifdef WORK_QUEUE
   wd_cancel(priv->txtimeout);
+
+  struct timespec ts;
+  struct timeval *now = (struct timeval *)&ts;
+  clock_systimespec(&ts);
+  now->tv_usec = ts.tv_nsec / 1000; /* timespec to timeval conversion */
 #endif
 
-  /* FIXME process aborts */
+  /* FIXME First Process Error aborts */
 
   /* Process TX completions */
 
@@ -796,16 +862,17 @@ static void s32k1xx_txdone(FAR struct s32k1xx_driver_s *priv, uint32_t flags)
         {
           putreg32(mb_bit, S32K1XX_CAN0_IFLAG1);
           flags &= ~mb_bit;
-#if 0 
-          /* FIXME TB ABORT SUPPORT */
+          NETDEV_TXDONE(&priv->dev);
+        }
 
-          /* const bool txok = priv->tx[mbi].cs.code != CAN_TXMB_ABORT;
-           * handleTxMailboxInterrupt(mbi, txok, utc_usec);
-           */
-#endif
+#ifdef TX_TIMEOUT_WQ
+      /* MB is not done, check for timeout */
 
-          NETDEV_TXDONE(&priv->dev);
+      else
+        {
+       s32k1xx_checkandaborttx(priv, mbi, now);
         }
+#endif
 
       mb_bit <<= 1;
     }
@@ -904,7 +971,6 @@ static void s32k1xx_poll_work(FAR void *arg)
            1, (wdparm_t)priv);
   net_unlock();
 }
-#endif
 
 /****************************************************************************
  * Function: s32k1xx_polltimer_expiry
@@ -924,7 +990,6 @@ static void s32k1xx_poll_work(FAR void *arg)
  *
  ****************************************************************************/
 
-#ifdef WORK_QUEUE
 static void s32k1xx_polltimer_expiry(int argc, uint32_t arg, ...)
 {
   #warning Missing logic
@@ -936,6 +1001,82 @@ static void s32k1xx_polltimer_expiry(int argc, uint32_t arg, ...)
 }
 #endif
 
+/****************************************************************************
+ * Function: s32k1xx_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+#ifdef TX_TIMEOUT_WQ
+static void s32k1xx_checkandaborttx(struct s32k1xx_driver_s *priv,
+  uint32_t mbi, struct timeval *now)
+{
+  if (priv->txmb[mbi].deadline.tv_sec != 0
+      && (now->tv_sec > priv->txmb[mbi].deadline.tv_sec
+      || now->tv_usec > priv->txmb[mbi].deadline.tv_usec))
+    {
+      NETDEV_TXTIMEOUTS(&priv->dev);
+      struct mb_s *mb = &priv->tx[mbi];
+      mb->cs.code = CAN_TXMB_ABORT;
+      priv->txmb[mbi].pending = TX_ABORT;
+    }
+}
+
+static void s32k1xx_txtimeout_work(FAR void *arg)
+{
+  FAR struct s32k1xx_driver_s *priv = (FAR struct s32k1xx_driver_s *)arg;
+
+  struct timespec ts;
+  struct timeval *now = (struct timeval *)&ts;
+  clock_systimespec(&ts);
+  now->tv_usec = ts.tv_nsec / 1000; /* timespec to timeval conversion */
+
+  for (int i = 0; i < TXMBCOUNT; i++)
+    {
+      s32k1xx_checkandaborttx(priv, i, now);
+    }
+}
+
+/****************************************************************************
+ * Function: s32k1xx_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   argc - The number of available arguments
+ *   arg  - The first argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void s32k1xx_txtimeout_expiry(int argc, uint32_t arg, ...)
+{
+  FAR struct s32k1xx_driver_s *priv = (FAR struct s32k1xx_driver_s *)arg;
+
+  /* Schedule to perform the TX timeout processing on the worker thread
+   */
+
+  work_queue(CANWORK, &priv->irqwork, s32k1xx_txtimeout_work, priv, 0);
+}
+
+#endif
+
 static void s32k1xx_setenable(uint32_t enable)
 {
   uint32_t regval;
@@ -1494,6 +1635,8 @@ int s32k1xx_netinitialize(int intf)
   /* Create a watchdog for timing polling for and timing of transmissions */
 
   priv->txpoll        = wd_create();       /* Create periodic poll timer */
+#endif
+#ifdef TX_TIMEOUT_WQ
   priv->txtimeout     = wd_create();       /* Create TX timeout timer */
 #endif
   priv->rx            = (struct mb_s *)(S32K1XX_CAN0_MB);
diff --git a/net/can/Kconfig b/net/can/Kconfig
index ac94021..00bfabd 100644
--- a/net/can/Kconfig
+++ b/net/can/Kconfig
@@ -51,6 +51,25 @@ config NET_CAN_RAW_TX_DEADLINE
 		CAN frame is still in the HW TX mailbox then the CAN driver will 
 		discard the CAN frame automatically.
 		
+config NET_CAN_RAW_TX_POLL
+	int "TX deadline polling rate (ms) "
+	default 500
+	depends on NET_CAN_RAW_TX_DEADLINE
+	---help---
+		The polling rate on which the CAN driver checks whenever a TX deadline occurs
+		
+config NET_CAN_RAW_DEFAULT_TX_DEADLINE
+	int "Default TX deadline when no deadline is given (us)"
+	default 0
+	depends on NET_CAN_RAW_TX_DEADLINE
+	---help---
+		Some applications may not use the NET_CAN_RAW_TX_DEADLINE flag.
+		By default their deadline becomes 0 which means it becomes infinite.
+		This would mean that packets from applications without the 
+		NET_CAN_RAW_TX_DEADLINE flag, can block the TX mailboxes forever.
+		This config can set the default deadline when no deadline has been 
+		given.
+		
 config NET_CAN_RAW_FILTER_MAX
 	int "CAN_RAW_FILTER max filter count"
 	default 32
diff --git a/net/can/can.h b/net/can/can.h
index 00c297d..a727612 100644
--- a/net/can/can.h
+++ b/net/can/can.h
@@ -331,6 +331,30 @@ ssize_t psock_can_send(FAR struct socket *psock, FAR const void *buf,
                        size_t len);
 
 /****************************************************************************
+ * Name: psock_can_sendmsg
+ *
+ * Description:
+ *   The psock_can_sendmsg() call may be used only when the packet socket is
+ *   in a connected state (so that the intended recipient is known).
+ *
+ * Input Parameters:
+ *   psock    An instance of the internal socket structure.
+ *   msg      msg to send
+ *   len      Length of msg to send
+ *
+ * Returned Value:
+ *   On success, returns the number of characters sent.  On  error,
+ *   a negated errno value is returned.  See send() for the complete list
+ *   of return values.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_CMSG
+ssize_t psock_can_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
+                       size_t len);
+#endif
+
+/****************************************************************************
  * Name: can_readahead_signal
  *
  * Description:
diff --git a/net/can/can_send.c b/net/can/can_send.c
index dfb03d0..ddfa83f 100644
--- a/net/can/can_send.c
+++ b/net/can/can_send.c
@@ -61,6 +61,10 @@
 #include "socket/socket.h"
 #include "can/can.h"
 
+#ifdef CONFIG_NET_CMSG
+#include <sys/time.h>
+#endif
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -123,10 +127,15 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
         {
           /* Copy the packet data into the device packet buffer and send it */
 
-          /* FIXME potentialy wrong function do we have a header?? */
-
           devif_can_send(dev, pstate->snd_buffer, pstate->snd_buflen);
           pstate->snd_sent = pstate->snd_buflen;
+
+          if (pstate->pr_msglen > 0) /* concat cmsg data after packet */
+            {
+              memcpy(dev->d_buf + pstate->snd_buflen, pstate->pr_msgbuf,
+                      pstate->pr_msglen);
+              dev->d_sndlen = pstate->snd_buflen + pstate->pr_msglen;
+            }
         }
 
       /* Don't allow any further call backs. */
@@ -279,4 +288,149 @@ ssize_t psock_can_send(FAR struct socket *psock, FAR const void *buf,
   return state.snd_sent;
 }
 
+/****************************************************************************
+ * Name: psock_can_sendmsg
+ *
+ * Description:
+ *   The psock_can_sendmsg() call may be used only when the packet socket is
+ *   in a connected state (so that the intended recipient is known).
+ *
+ * Input Parameters:
+ *   psock    An instance of the internal socket structure.
+ *   msg      msg to send
+ *   len      Length of msg to send
+ *
+ * Returned Value:
+ *   On success, returns the number of characters sent.  On  error,
+ *   a negated errno value is retruend.  See send() for the complete list
+ *   of return values.
+ *
+ ****************************************************************************/
+
+ssize_t psock_can_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
+                       size_t len)
+{
+  FAR struct net_driver_s *dev;
+  FAR struct can_conn_s *conn;
+  struct send_s state;
+  int ret = OK;
+
+  conn = (FAR struct can_conn_s *)psock->s_conn;
+
+  /* Verify that the sockfd corresponds to valid, allocated socket */
+
+  if (!psock || psock->s_crefs <= 0)
+    {
+      return -EBADF;
+    }
+
+  /* Get the device driver that will service this transfer */
+
+  dev = conn->dev;
+  if (dev == NULL)
+    {
+      return -ENODEV;
+    }
+
+  if (conn->fd_frames)
+    {
+      if (msg->msg_iov->iov_len != CANFD_MTU
+              && msg->msg_iov->iov_len != CAN_MTU)
+        {
+          return -EINVAL;
+        }
+    }
+  else
+    {
+      if (msg->msg_iov->iov_len != CAN_MTU)
+        {
+          return -EINVAL;
+        }
+    }
+
+  /* Perform the send operation */
+
+  /* Initialize the state structure. This is done with the network locked
+   * because we don't want anything to happen until we are ready.
+   */
+
+  net_lock();
+  memset(&state, 0, sizeof(struct send_s));
+
+  /* This semaphore is used for signaling and, hence, should not have
+   * priority inheritance enabled.
+   */
+
+  nxsem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */
+  nxsem_setprotocol(&state.snd_sem, SEM_PRIO_NONE);
+
+  state.snd_sock      = psock;                  /* Socket descriptor */
+  state.snd_buflen    = msg->msg_iov->iov_len;  /* bytes to send */
+  state.snd_buffer    = msg->msg_iov->iov_base; /* Buffer to send from */
+
+  if (msg->msg_controllen > sizeof(struct cmsghdr))
+    {
+      struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
+      if (conn->tx_deadline && cmsg->cmsg_level == SOL_CAN_RAW
+              && cmsg->cmsg_type == CAN_RAW_TX_DEADLINE
+              && cmsg->cmsg_len == sizeof(struct timeval))
+        {
+          state.pr_msgbuf     = CMSG_DATA(cmsg); /* Buffer to cmsg data */
+          state.pr_msglen     = cmsg->cmsg_len;  /* len of cmsg data */
+        }
+    }
+
+  /* Allocate resource to receive a callback */
+
+  state.snd_cb = can_callback_alloc(dev, conn);
+  if (state.snd_cb)
+    {
+      /* Set up the callback in the connection */
+
+      state.snd_cb->flags = CAN_POLL;
+      state.snd_cb->priv  = (FAR void *)&state;
+      state.snd_cb->event = psock_send_eventhandler;
+
+      /* Notify the device driver that new TX data is available. */
+
+      netdev_txnotify_dev(dev);
+
+      /* Wait for the send to complete or an error to occur.
+       * net_lockedwait will also terminate if a signal is received.
+       */
+
+      ret = net_lockedwait(&state.snd_sem);
+
+      /* Make sure that no further events are processed */
+
+      can_callback_free(dev, conn, state.snd_cb);
+    }
+
+  nxsem_destroy(&state.snd_sem);
+  net_unlock();
+
+  /* Check for a errors, Errors are signalled by negative errno values
+   * for the send length
+   */
+
+  if (state.snd_sent < 0)
+    {
+      return state.snd_sent;
+    }
+
+  /* If net_lockedwait failed, then we were probably reawakened by a signal.
+   * In this case, net_lockedwait will have returned negated errno
+   * appropriately.
+   */
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Return the number of bytes actually sent */
+
+  return state.snd_sent;
+}
+
 #endif /* CONFIG_NET && CONFIG_NET_CAN */
diff --git a/net/can/can_sockif.c b/net/can/can_sockif.c
index 09a61d0..497f74b 100644
--- a/net/can/can_sockif.c
+++ b/net/can/can_sockif.c
@@ -797,7 +797,7 @@ static ssize_t can_sendto(FAR struct socket *psock, FAR const void *buf,
  ****************************************************************************/
 #ifdef CONFIG_NET_CMSG
 static ssize_t can_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
-                    size_t len, int flags);
+                    size_t len, int flags)
 {
   ssize_t ret;
 
@@ -807,7 +807,7 @@ static ssize_t can_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
     {
       /* Raw packet send */
 
-      ret = psock_can_send(psock, buf, len);
+      ret = psock_can_sendmsg(psock, msg, len);
     }
   else
     {
diff --git a/net/devif/devif_cansend.c b/net/devif/devif_cansend.c
index 60fdf38..43570f2 100644
--- a/net/devif/devif_cansend.c
+++ b/net/devif/devif_cansend.c
@@ -80,10 +80,10 @@
  ****************************************************************************/
 
 /****************************************************************************
- * Name: devif_pkt_send
+ * Name: devif_can_send
  *
  * Description:
- *   Called from socket logic in order to send a raw packet in response to
+ *   Called from socket logic in order to send a can packet in response to
  *   an xmit or poll request from the network interface driver.
  *
  *   This is almost identical to calling devif_send() except that the data to