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 fd4662e34c9eeb0db346b72e90bc40a1c01ba049 Author: Peter van der Perk <peter.vanderp...@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