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