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/03/18 13:35:28 UTC
[incubator-nuttx] 11/23: Added support for SO_TIMESTAMP in
socketlayer and SocketCAN Cleanup FlexCAN driver driver Disabled workqueue
based TX in FlexCAN
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 b6c052f702e5122e84d926b76d46fcda51cd8502
Author: Peter van der Perk <pe...@nxp.com>
AuthorDate: Wed Mar 11 16:13:18 2020 +0100
Added support for SO_TIMESTAMP in socketlayer and SocketCAN
Cleanup FlexCAN driver driver
Disabled workqueue based TX in FlexCAN
---
arch/arm/src/s32k1xx/s32k1xx_flexcan.c | 228 +++++------------------------
include/nuttx/net/net.h | 7 +
libs/libc/net/lib_recvmsg.c | 4 +-
net/bluetooth/bluetooth_sockif.c | 3 +
net/can/can.h | 28 ++++
net/can/can_callback.c | 16 +++
net/can/can_recvfrom.c | 252 +++++++++++++++++++++++++++++++++
net/can/can_sockif.c | 10 +-
net/icmp/icmp_sockif.c | 3 +
net/icmpv6/icmpv6_sockif.c | 3 +
net/ieee802154/ieee802154_sockif.c | 3 +
net/inet/inet_sockif.c | 3 +
net/local/local_sockif.c | 3 +
net/netlink/netlink_sockif.c | 3 +
net/pkt/pkt_sockif.c | 3 +
net/socket/Kconfig | 16 +++
net/socket/Make.defs | 5 +
net/socket/getsockopt.c | 13 ++
net/socket/setsockopt.c | 24 ++++
net/socket/socket.h | 2 +-
20 files changed, 430 insertions(+), 199 deletions(-)
diff --git a/arch/arm/src/s32k1xx/s32k1xx_flexcan.c b/arch/arm/src/s32k1xx/s32k1xx_flexcan.c
index 2e73dc9..8e79833 100644
--- a/arch/arm/src/s32k1xx/s32k1xx_flexcan.c
+++ b/arch/arm/src/s32k1xx/s32k1xx_flexcan.c
@@ -59,9 +59,14 @@
* is required.
*/
+
+/* FIXME A workqueue is required for enet but for FLEXCAN it increased the
+ * transmit latency by ~ 40us from 24 to 67us
+ * Therefore for now its configurable by the WORK_QUEUE define
+ * If we know for sure that a workqueue isn't required
+ * Then all WORK_QUEUE related code will be removed */
#if !defined(CONFIG_SCHED_WORKQUEUE)
-# error Work queue support is required
- //FIXME maybe for enet not sure for FLEXCAN
+//# error Work queue support is required
#else
/* Select work queue. Always use the LP work queue if available. If not,
@@ -72,28 +77,14 @@
* processing that never suspends. Suspending the high priority work queue
* may bring the system to its knees!
*/
-
-# define ETHWORK LPWORK
+//# define WORK_QUEUE
+# define CANWORK LPWORK
#endif
/* CONFIG_S32K1XX_FLEXCAN_NETHIFS determines the number of physical interfaces
* that will be supported.
*/
-#if 0
-#if CONFIG_S32K1XX_FLEXCAN_NETHIFS != 1
-# error "CONFIG_S32K1XX_FLEXCAN_NETHIFS must be one for now"
-#endif
-
-#if CONFIG_S32K1XX_FLEXCAN_NTXBUFFERS < 1
-# error "Need at least one TX buffer"
-#endif
-
-#if CONFIG_S32K1XX_FLEXCAN_NRXBUFFERS < 1
-# error "Need at least one RX buffer"
-#endif
-#endif
-
#define MASKSTDID 0x000007ff
#define MASKEXTID 0x1fffffff
#define FLAGEFF (1 << 31) /* Extended frame format */
@@ -122,45 +113,13 @@
static int peak_tx_mailbox_index_ = 0;
-/* Normally you would clean the cache after writing new values to the DMA
- * memory so assure that the dirty cache lines are flushed to memory
- * before the DMA occurs. And you would invalid the cache after a data is
- * received via DMA so that you fetch the actual content of the data from
- * the cache.
- *
- * These conditions are not fully supported here. If the write-throuch
- * D-Cache is enabled, however, then many of these issues go away: The
- * cache clean operation does nothing (because there are not dirty cache
- * lines) and the cache invalid operation is innocuous (because there are
- * never dirty cache lines to be lost; valid data will always be reloaded).
- *
- * At present, we simply insist that write through cache be enabled.
- */
-
-#if defined(CONFIG_ARMV7M_DCACHE) && !defined(CONFIG_ARMV7M_DCACHE_WRITETHROUGH)
-# error Write back D-Cache not yet supported
-#endif
-
+#ifdef WORK_QUEUE
/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per
* second.
*/
#define S32K1XX_WDDELAY (1*CLK_TCK)
-
-/* Align assuming that the D-Cache is enabled (probably 32-bytes).
- *
- * REVISIT: The size of descriptors and buffers must also be in even units
- * of the cache line size That is because the operations to clean and
- * invalidate the cache will operate on a full 32-byte cache line. If
- * CONFIG_FLEXCAN_ENHANCEDBD is selected, then the size of the descriptor is
- * 32-bytes (and probably already the correct size for the cache line);
- * otherwise, the size of the descriptors much smaller, only 8 bytes.
- */
-
-#define FLEXCAN_ALIGN ARMV7M_DCACHE_LINESIZE
-#define FLEXCAN_ALIGN_MASK (FLEXCAN_ALIGN - 1)
-#define FLEXCAN_ALIGN_UP(n) (((n) + FLEXCAN_ALIGN_MASK) & ~FLEXCAN_ALIGN_MASK)
-
+#endif
/****************************************************************************
* Private Types
@@ -235,8 +194,10 @@ struct s32k1xx_driver_s
uint8_t txhead; /* The next TX descriptor to use */
uint8_t rxtail; /* The next RX descriptor to use */
uint8_t phyaddr; /* Selected PHY address */
+#ifdef WORK_QUEUE
WDOG_ID txpoll; /* TX poll timer */
WDOG_ID txtimeout; /* TX timeout timer */
+#endif
struct work_s irqwork; /* For deferring interrupt work to the work queue */
struct work_s pollwork; /* For deferring poll work to the work queue */
#ifdef CAN_FD
@@ -276,21 +237,6 @@ static uint8_t g_rx_pool[sizeof(struct can_frame)*POOL_SIZE]
* Private Function Prototypes
****************************************************************************/
-/* Utility functions */
-
-#ifndef S32K1XX_BUFFERS_SWAP
-# define s32k1xx_swap32(value) (value)
-# define s32k1xx_swap16(value) (value)
-#else
-#if 0 /* Use builtins if the compiler supports them */
-static inline uint32_t s32k1xx_swap32(uint32_t value);
-static inline uint16_t s32k1xx_swap16(uint16_t value);
-#else
-# define s32k1xx_swap32 __builtin_bswap32
-# define s32k1xx_swap16 __builtin_bswap16
-#endif
-#endif
-
/****************************************************************************
* Name: arm_clz
*
@@ -328,21 +274,17 @@ static uint32_t s32k1xx_waitmcr_change(uint32_t mask,
/* Interrupt handling */
-static void s32k1xx_dispatch(FAR struct s32k1xx_driver_s *priv);
static void s32k1xx_receive(FAR struct s32k1xx_driver_s *priv, uint32_t flags);
static void s32k1xx_txdone(FAR struct s32k1xx_driver_s *priv, uint32_t flags);
-static void s32k1xx_flexcan_interrupt_work(FAR void *arg);
static int s32k1xx_flexcan_interrupt(int irq, FAR void *context,
FAR void *arg);
/* Watchdog timer expirations */
-
-static void s32k1xx_txtimeout_work(FAR void *arg);
-static void s32k1xx_txtimeout_expiry(int argc, uint32_t arg, ...);
-
+#ifdef WORK_QUEUE
static void s32k1xx_poll_work(FAR void *arg);
static void s32k1xx_polltimer_expiry(int argc, uint32_t arg, ...);
+#endif
/* NuttX callback functions */
@@ -359,7 +301,6 @@ static int s32k1xx_ioctl(struct net_driver_s *dev, int cmd,
/* Initialization */
-static void s32k1xx_initbuffers(struct s32k1xx_driver_s *priv);
static int s32k1xx_initialize(struct s32k1xx_driver_s *priv);
static void s32k1xx_reset(struct s32k1xx_driver_s *priv);
@@ -632,28 +573,6 @@ static int s32k1xx_txpoll(struct net_driver_s *dev)
return 0;
}
-/****************************************************************************
- * Function: s32k1xx_dispatch
- *
- * Description:
- * A new Rx packet was received; dispatch that packet to the network layer
- * as necessary.
- *
- * Input Parameters:
- * priv - Reference to the driver state structure
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- * Global interrupts are disabled by interrupt handling logic.
- *
- ****************************************************************************/
-
-static inline void s32k1xx_dispatch(FAR struct s32k1xx_driver_s *priv)
-{
- #warning Missing logic
-}
/****************************************************************************
* Function: s32k1xx_receive
@@ -841,7 +760,9 @@ static void s32k1xx_txdone(FAR struct s32k1xx_driver_s *priv, uint32_t flags)
* canceled.
*/
+#ifdef WORK_QUEUE
wd_cancel(priv->txtimeout);
+#endif
/* FIXME process aborts */
@@ -854,7 +775,7 @@ static void s32k1xx_txdone(FAR struct s32k1xx_driver_s *priv, uint32_t flags)
{
putreg32(mb_bit, S32K1XX_CAN0_IFLAG1);
flags &= ~mb_bit;
-#if 0
+#if 0 //FIXME TB ABORT SUPPORT
const bool txok = priv->tx[mbi].cs.code != CAN_TXMB_ABORT;
handleTxMailboxInterrupt(mbi, txok, utc_usec);
#endif
@@ -872,27 +793,6 @@ static void s32k1xx_txdone(FAR struct s32k1xx_driver_s *priv, uint32_t flags)
devif_poll(&priv->dev, s32k1xx_txpoll);
}
-/****************************************************************************
- * Function: s32k1xx_flexcan_interrupt_work
- *
- * Description:
- * Perform interrupt related work from the worker thread
- *
- * Input Parameters:
- * arg - The argument passed when work_queue() was called.
- *
- * Returned Value:
- * OK on success
- *
- * Assumptions:
- * The network is locked.
- *
- ****************************************************************************/
-
-static void s32k1xx_flexcan_interrupt_work(FAR void *arg)
-{
- #warning Missing logic
-}
/****************************************************************************
* Function: s32k1xx_flexcan_interrupt
@@ -937,53 +837,6 @@ static int s32k1xx_flexcan_interrupt(int irq, FAR void *context, FAR void *arg)
}
/****************************************************************************
- * 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:
- *
- ****************************************************************************/
-
-static void s32k1xx_txtimeout_work(FAR void *arg)
-{
- #warning Missing logic
- ninfo("FLEXCAN: tx timeout work\r\n");
-}
-
-/****************************************************************************
- * 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, ...)
-{
- #warning Missing logic
- ninfo("FLEXCAN: tx timeout expiry\r\n");
-}
-
-/****************************************************************************
* Function: s32k1xx_poll_work
*
* Description:
@@ -999,7 +852,7 @@ static void s32k1xx_txtimeout_expiry(int argc, uint32_t arg, ...)
* The network is locked.
*
****************************************************************************/
-
+#ifdef WORK_QUEUE
static void s32k1xx_poll_work(FAR void *arg)
{
#warning Missing logic
@@ -1027,6 +880,7 @@ static void s32k1xx_poll_work(FAR void *arg)
1, (wdparm_t)priv);
net_unlock();
}
+#endif
/****************************************************************************
* Function: s32k1xx_polltimer_expiry
@@ -1046,6 +900,7 @@ static void s32k1xx_poll_work(FAR void *arg)
*
****************************************************************************/
+#ifdef WORK_QUEUE
static void s32k1xx_polltimer_expiry(int argc, uint32_t arg, ...)
{
#warning Missing logic
@@ -1053,8 +908,9 @@ static void s32k1xx_polltimer_expiry(int argc, uint32_t arg, ...)
/* Schedule to perform the poll processing on the worker thread. */
- work_queue(ETHWORK, &priv->pollwork, s32k1xx_poll_work, priv, 0);
+ work_queue(CANWORK, &priv->pollwork, s32k1xx_poll_work, priv, 0);
}
+#endif
static void s32k1xx_setenable(uint32_t enable)
{
@@ -1140,7 +996,6 @@ static int s32k1xx_ifup(struct net_driver_s *dev)
{
FAR struct s32k1xx_driver_s *priv =
(FAR struct s32k1xx_driver_s *)dev->d_private;
- uint32_t regval;
#warning Missing logic
@@ -1150,10 +1005,12 @@ static int s32k1xx_ifup(struct net_driver_s *dev)
return -1;
}
- /* Set and activate a timer process */
+#ifdef WORK_QUEUE
+ /* Set and activate a timer process */
wd_start(priv->txpoll, S32K1XX_WDDELAY, s32k1xx_polltimer_expiry, 1,
(wdparm_t)priv);
+#endif
priv->bifup = true;
@@ -1270,10 +1127,10 @@ static int s32k1xx_txavail(struct net_driver_s *dev)
{
/* Schedule to serialize the poll on the worker thread. */
-#ifdef WORK_QUEUE_BYPASS
- s32k1xx_txavail_work(priv);
+#ifdef WORK_QUEUE
+ work_queue(CANWORK, &priv->pollwork, s32k1xx_txavail_work, priv, 0);
#else
- work_queue(ETHWORK, &priv->pollwork, s32k1xx_txavail_work, priv, 0);
+ s32k1xx_txavail_work(priv);
#endif
}
@@ -1413,6 +1270,7 @@ static int s32k1xx_initialize(struct s32k1xx_driver_s *priv)
for (i = TXMBCOUNT; i < TOTALMBCOUNT; i++)
{
priv->rx[i].id.w = 0x0;
+ //FIXME sometimes we get a hard fault here
}
putreg32(0x0, S32K1XX_CAN0_RXFGMASK);
@@ -1450,27 +1308,6 @@ static int s32k1xx_initialize(struct s32k1xx_driver_s *priv)
}
/****************************************************************************
- * Function: s32k1xx_initbuffers
- *
- * Description:
- * Initialize FLEXCAN buffers and descriptors
- *
- * Input Parameters:
- * priv - Reference to the private FLEXCAN driver state structure
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- *
- ****************************************************************************/
-
-static void s32k1xx_initbuffers(struct s32k1xx_driver_s *priv)
-{
- #warning Missing logic
-}
-
-/****************************************************************************
* Function: s32k1xx_reset
*
* Description:
@@ -1621,14 +1458,15 @@ int s32k1xx_netinitialize(int intf)
priv->dev.d_ifdown = s32k1xx_ifdown; /* I/F down callback */
priv->dev.d_txavail = s32k1xx_txavail; /* New TX data callback */
#ifdef CONFIG_NETDEV_IOCTL
- priv->dev.d_ioctl = s32k1xx_ioctl; /* Support PHY ioctl() calls */
+ priv->dev.d_ioctl = s32k1xx_ioctl; /* Support CAN ioctl() calls */
#endif
priv->dev.d_private = (void *)g_flexcan; /* Used to recover private state from dev */
+#ifdef WORK_QUEUE
/* Create a watchdog for timing polling for and timing of transmissions */
-
priv->txpoll = wd_create(); /* Create periodic poll timer */
priv->txtimeout = wd_create(); /* Create TX timeout timer */
+#endif
priv->rx = (struct mb_s *)(S32K1XX_CAN0_MB);
priv->tx = (struct mb_s *)(S32K1XX_CAN0_MB +
(sizeof(struct mb_s) * RXMBCOUNT));
diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h
index 02f7ac2..20da67a 100644
--- a/include/nuttx/net/net.h
+++ b/include/nuttx/net/net.h
@@ -212,6 +212,10 @@ struct sock_intf_s
CODE ssize_t (*si_recvfrom)(FAR struct socket *psock, FAR void *buf,
size_t len, int flags, FAR struct sockaddr *from,
FAR socklen_t *fromlen);
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ CODE ssize_t (*si_recvmsg)(FAR struct socket *psock,
+ FAR struct msghdr *msg, int flags);
+#endif
CODE int (*si_close)(FAR struct socket *psock);
#ifdef CONFIG_NET_USRSOCK
CODE int (*si_ioctl)(FAR struct socket *psock, int cmd,
@@ -268,6 +272,9 @@ struct socket
#ifdef CONFIG_NET_SOLINGER
socktimeo_t s_linger; /* Linger timeout value (in deciseconds) */
#endif
+#ifdef CONFIG_NET_TIMESTAMP
+ int32_t s_timestamp; /* Socket timestamp enabled/disabled */
+#endif
#endif
FAR void *s_conn; /* Connection inherits from struct socket_conn_s */
diff --git a/libs/libc/net/lib_recvmsg.c b/libs/libc/net/lib_recvmsg.c
index 984a16f..2513dfa 100644
--- a/libs/libc/net/lib_recvmsg.c
+++ b/libs/libc/net/lib_recvmsg.c
@@ -39,7 +39,7 @@
#include <nuttx/config.h>
-#ifdef CONFIG_NET
+#if defined(CONFIG_NET) && !defined(CONFIG_NET_RECVMSG_CMSG)
#include <sys/types.h>
#include <sys/socket.h>
@@ -86,4 +86,4 @@ ssize_t recvmsg(int sockfd, FAR struct msghdr *msg, int flags)
}
}
-#endif /* CONFIG_NET */
+#endif /* CONFIG_NET && !CONFIG_NET_RECVMSG_CMSG */
diff --git a/net/bluetooth/bluetooth_sockif.c b/net/bluetooth/bluetooth_sockif.c
index 41bcafa..4f4b936 100644
--- a/net/bluetooth/bluetooth_sockif.c
+++ b/net/bluetooth/bluetooth_sockif.c
@@ -109,6 +109,9 @@ const struct sock_intf_s g_bluetooth_sockif =
NULL, /* si_sendfile */
#endif
bluetooth_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ NULL, /* si_recvmsg */
+#endif
bluetooth_close /* si_close */
};
diff --git a/net/can/can.h b/net/can/can.h
index e5d45f0..c74fca4 100644
--- a/net/can/can.h
+++ b/net/can/can.h
@@ -115,6 +115,10 @@ struct can_conn_s
/* TODO add filter support */
#endif
+#ifdef CONFIG_NET_TIMESTAMP
+ FAR struct socket *psock; /* Needed to get SO_TIMESTAMP value */
+#endif
+
};
@@ -254,11 +258,35 @@ uint16_t can_datahandler(FAR struct can_conn_s *conn, FAR uint8_t *buffer,
*
****************************************************************************/
+
ssize_t can_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len,
int flags, FAR struct sockaddr *from,
FAR socklen_t *fromlen);
/****************************************************************************
+ * Name: can_recvmsg
+ *
+ * Description:
+ * recvmsg() receives messages from a socket, and may be used to receive
+ * data on a socket whether or not it is connection-oriented.
+ *
+ * If from is not NULL, and the underlying protocol provides the source
+ * address, this source address is filled in. The argument 'fromlen'
+ * initialized to the size of the buffer associated with from, and modified
+ * on return to indicate the actual size of the address stored there.
+ *
+ * Input Parameters:
+ * psock A pointer to a NuttX-specific, internal socket structure
+ * msg Buffer to receive msg
+ * flags Receive flags (ignored)
+ *
+ ****************************************************************************/
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ssize_t can_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
+ size_t len, int flags);
+#endif
+
+/****************************************************************************
* Name: can_poll
*
* Description:
diff --git a/net/can/can_callback.c b/net/can/can_callback.c
index 7b923d3..f8bff86 100644
--- a/net/can/can_callback.c
+++ b/net/can/can_callback.c
@@ -35,6 +35,10 @@
#include "devif/devif.h"
#include "can/can.h"
+#ifdef CONFIG_NET_TIMESTAMP
+#include <sys/time.h>
+#endif
+
/****************************************************************************
* Private Functions
****************************************************************************/
@@ -114,6 +118,18 @@ uint16_t can_callback(FAR struct net_driver_s *dev,
if (conn)
{
+#ifdef CONFIG_NET_TIMESTAMP
+ /* TIMESTAMP sockopt is activated, create timestamp and copy to iob */
+ if(conn->psock->s_timestamp)
+ {
+ struct timespec *ts = (struct timespec*)&dev->d_appdata[dev->d_len];
+ struct timeval *tv = (struct timeval*)&dev->d_appdata[dev->d_len];
+ dev->d_len += sizeof(struct timeval);
+ clock_systimespec(ts);
+ tv->tv_usec = ts->tv_nsec / 1000;
+ }
+#endif
+
/* Perform the callback */
flags = devif_conn_event(dev, conn, flags, conn->list);
diff --git a/net/can/can_recvfrom.c b/net/can/can_recvfrom.c
index 06b133c..98a0b2a 100644
--- a/net/can/can_recvfrom.c
+++ b/net/can/can_recvfrom.c
@@ -46,6 +46,11 @@
#include "socket/socket.h"
#include <netpacket/packet.h>
+#ifdef CONFIG_NET_TIMESTAMP
+#include <sys/time.h>
+#endif
+
+
/****************************************************************************
* Private Types
****************************************************************************/
@@ -58,6 +63,10 @@ struct can_recvfrom_s
size_t pr_buflen; /* Length of receive buffer */
FAR uint8_t *pr_buffer; /* Pointer to receive buffer */
ssize_t pr_recvlen; /* The received length */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ size_t pr_msglen; /* Length of msg buffer */
+ FAR uint8_t *pr_msgbuf; /* Pointer to msg buffer */
+#endif
int pr_result; /* Success:OK, failure:negated errno */
};
@@ -294,6 +303,79 @@ static inline int can_readahead(struct can_recvfrom_s *pstate)
return 0;
}
+/****************************************************************************
+ * Name: can_readahead
+ *
+ * Description:
+ * Copy the read-ahead data from the packet
+ *
+ * Input Parameters:
+ * pstate recvfrom state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+#ifdef CONFIG_NET_TIMESTAMP
+static inline int can_readahead_timestamp(struct can_conn_s *conn, FAR uint8_t *buffer)
+{
+ FAR struct iob_s *iob;
+ int recvlen;
+
+
+ if ((iob = iob_peek_queue(&conn->readahead)) != NULL)
+ {
+ DEBUGASSERT(iob->io_pktlen > 0);
+
+ /* Transfer that buffered data from the I/O buffer chain into
+ * the user buffer.
+ */
+
+ recvlen = iob_copyout(buffer, iob, sizeof(struct timeval), 0);
+
+ /* If we took all of the data from the I/O buffer chain is empty, then
+ * release it. If there is still data available in the I/O buffer
+ * chain, then just trim the data that we have taken from the
+ * beginning of the I/O buffer chain.
+ */
+
+ if (recvlen >= iob->io_pktlen)
+ {
+ FAR struct iob_s *tmp;
+
+ /* Remove the I/O buffer chain from the head of the read-ahead
+ * buffer queue.
+ */
+
+ tmp = iob_remove_queue(&conn->readahead);
+ DEBUGASSERT(tmp == iob);
+ UNUSED(tmp);
+
+ /* And free the I/O buffer chain */
+
+ iob_free_chain(iob, IOBUSER_NET_CAN_READAHEAD);
+ }
+ else
+ {
+ /* The bytes that we have received from the head of the I/O
+ * buffer chain (probably changing the head of the I/O
+ * buffer queue).
+ */
+
+ iob_trimhead_queue(&conn->readahead, recvlen,
+ IOBUSER_NET_CAN_READAHEAD);
+ }
+
+ return recvlen;
+ }
+
+ return 0;
+}
+#endif
+
#ifdef CONFIG_NET_CANPROTO_OPTIONS
static int can_recv_filter(struct can_conn_s *conn, canid_t id)
{
@@ -360,6 +442,22 @@ static uint16_t can_recvfrom_eventhandler(FAR struct net_driver_s *dev,
can_newdata(dev, pstate);
+#ifdef CONFIG_NET_TIMESTAMP
+ if(pstate->pr_sock->s_timestamp)
+ {
+ if(pstate->pr_msglen == sizeof(struct timeval))
+ {
+ can_readahead_timestamp(conn, pstate->pr_msgbuf);
+ }
+ else
+ {
+ /* We still have to consume the data otherwise IOB gets full */
+ uint8_t dummy_buf[sizeof(struct timeval)];
+ can_readahead_timestamp(conn, &dummy_buf);
+ }
+ }
+#endif
+
/* We are finished. */
/* Don't allow any further call backs. */
@@ -559,4 +657,158 @@ errout_with_state:
return ret;
}
+/****************************************************************************
+ * Name: can_recvmsg
+ *
+ * Description:
+ * recvmsg() receives messages from a socket, and may be used to receive
+ * data on a socket whether or not it is connection-oriented.
+ *
+ * If from is not NULL, and the underlying protocol provides the source
+ * address, this source address is filled in. The argument 'fromlen'
+ * initialized to the size of the buffer associated with from, and modified
+ * on return to indicate the actual size of the address stored there.
+ *
+ * Input Parameters:
+ * psock A pointer to a NuttX-specific, internal socket structure
+ * msg Buffer to receive msg
+ * flags Receive flags (ignored)
+ *
+ ****************************************************************************/
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ssize_t can_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
+ size_t len, int flags)
+{
+ FAR struct can_conn_s *conn;
+ FAR struct net_driver_s *dev;
+ struct can_recvfrom_s state;
+ int ret;
+
+ DEBUGASSERT(psock != NULL && psock->s_conn != NULL && msg != NULL);
+
+ conn = (FAR struct can_conn_s *)psock->s_conn;
+
+ if (psock->s_type != SOCK_RAW)
+ {
+ nerr("ERROR: Unsupported socket type: %d\n", psock->s_type);
+ ret = -ENOSYS;
+ }
+
+ net_lock();
+
+ /* Initialize the state structure. */
+
+ memset(&state, 0, sizeof(struct can_recvfrom_s));
+
+ /* This semaphore is used for signaling and, hence, should not have
+ * priority inheritance enabled.
+ */
+
+ nxsem_init(&state.pr_sem, 0, 0); /* Doesn't really fail */
+ nxsem_setprotocol(&state.pr_sem, SEM_PRIO_NONE);
+
+
+ state.pr_buflen = msg->msg_iov->iov_len;
+ state.pr_buffer = msg->msg_iov->iov_base;
+#ifdef CONFIG_NET_TIMESTAMP
+ if(psock->s_timestamp && msg->msg_controllen == (sizeof(struct cmsghdr) + sizeof(struct timeval)))
+ {
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(msg);
+ state.pr_msglen = sizeof(struct timeval);
+ state.pr_msgbuf = CMSG_DATA(cmsg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SO_TIMESTAMP;
+ cmsg->cmsg_len = state.pr_msglen;
+ }
+#endif
+ state.pr_sock = psock;
+
+ /* Handle any any CAN data already buffered in a read-ahead buffer. NOTE
+ * that there may be read-ahead data to be retrieved even after the
+ * socket has been disconnected.
+ */
+
+ ret = can_readahead(&state);
+ if (ret > 0)
+ {
+#ifdef CONFIG_NET_TIMESTAMP
+ if(psock->s_timestamp)
+ {
+ if(state.pr_msglen == sizeof(struct timeval))
+ {
+ can_readahead_timestamp(conn, state.pr_msgbuf);
+ }
+ else
+ {
+ /* We still have to consume the data otherwise IOB gets full */
+ uint8_t dummy_buf[sizeof(struct timeval)];
+ can_readahead_timestamp(conn, &dummy_buf);
+ }
+ }
+#endif
+ goto errout_with_state;
+ }
+
+ ret = state.pr_recvlen;
+
+ /* Handle non-blocking CAN sockets */
+
+ if (_SS_ISNONBLOCK(psock->s_flags) || (flags & MSG_DONTWAIT) != 0)
+ {
+ /* Return the number of bytes read from the read-ahead buffer if
+ * something was received (already in 'ret'); EAGAIN if not.
+ */
+
+ if (ret < 0)
+ {
+ /* Nothing was received */
+
+ ret = -EAGAIN;
+ goto errout_with_state;
+ }
+ }
+
+ /* Get the device driver that will service this transfer */
+
+ dev = conn->dev;
+ if (dev == NULL)
+ {
+ ret = -ENODEV;
+ goto errout_with_state;
+ }
+
+ /* Set up the callback in the connection */
+
+ state.pr_cb = can_callback_alloc(dev, conn);
+ if (state.pr_cb)
+ {
+ state.pr_cb->flags = (CAN_NEWDATA | CAN_POLL);
+ state.pr_cb->priv = (FAR void *)&state;
+ state.pr_cb->event = can_recvfrom_eventhandler;
+
+ /* Wait for either the receive to complete or for an error/timeout to
+ * occur. NOTES: (1) net_lockedwait will also terminate if a signal
+ * is received, (2) the network is locked! It will be un-locked while
+ * the task sleeps and automatically re-locked when the task restarts.
+ */
+
+ ret = net_lockedwait(&state.pr_sem);
+
+ /* Make sure that no further events are processed */
+
+ can_callback_free(dev, conn, state.pr_cb);
+ ret = can_recvfrom_result(ret, &state);
+ }
+ else
+ {
+ ret = -EBUSY;
+ }
+
+errout_with_state:
+ net_unlock();
+ nxsem_destroy(&state.pr_sem);
+ return ret;
+}
+#endif
+
#endif /* CONFIG_NET_CAN */
diff --git a/net/can/can_sockif.c b/net/can/can_sockif.c
index 4deef14..844ac3b 100644
--- a/net/can/can_sockif.c
+++ b/net/can/can_sockif.c
@@ -93,6 +93,9 @@ const struct sock_intf_s g_can_sockif =
NULL, /* si_sendfile */
#endif
can_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ can_recvmsg, /* si_recvmsg */
+#endif
can_close /* si_close */
};
@@ -105,7 +108,7 @@ const struct sock_intf_s g_can_sockif =
*
* Description:
* This function is called to perform the actual CAN receive operation
- * via the device interface layer.
+ * via the device interface layer. from can_input()
*
* Input Parameters:
* dev The structure of the network driver that caused the event
@@ -229,6 +232,11 @@ static int can_setup(FAR struct socket *psock, int protocol)
return -ENOMEM;
}
+#ifdef CONFIG_NET_TIMESTAMP
+ /* Store psock in conn se we can read the SO_TIMESTAMP value */
+ conn->psock = psock;
+#endif
+
/* Initialize the connection instance */
conn->protocol = (uint8_t)protocol;
diff --git a/net/icmp/icmp_sockif.c b/net/icmp/icmp_sockif.c
index 983eb71..c81592e 100644
--- a/net/icmp/icmp_sockif.c
+++ b/net/icmp/icmp_sockif.c
@@ -102,6 +102,9 @@ const struct sock_intf_s g_icmp_sockif =
NULL, /* si_sendfile */
#endif
icmp_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ NULL, /* si_recvmsg */
+#endif
icmp_close /* si_close */
};
diff --git a/net/icmpv6/icmpv6_sockif.c b/net/icmpv6/icmpv6_sockif.c
index c99d184..29e5e47 100644
--- a/net/icmpv6/icmpv6_sockif.c
+++ b/net/icmpv6/icmpv6_sockif.c
@@ -102,6 +102,9 @@ const struct sock_intf_s g_icmpv6_sockif =
NULL, /* si_sendfile */
#endif
icmpv6_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ NULL, /* si_recvmsg */
+#endif
icmpv6_close /* si_close */
};
diff --git a/net/ieee802154/ieee802154_sockif.c b/net/ieee802154/ieee802154_sockif.c
index 216264a..d1ac29b 100644
--- a/net/ieee802154/ieee802154_sockif.c
+++ b/net/ieee802154/ieee802154_sockif.c
@@ -107,6 +107,9 @@ const struct sock_intf_s g_ieee802154_sockif =
NULL, /* si_sendfile */
#endif
ieee802154_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ NULL, /* si_recvmsg */
+#endif
ieee802154_close /* si_close */
};
diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c
index 7a71ff9..0980989 100644
--- a/net/inet/inet_sockif.c
+++ b/net/inet/inet_sockif.c
@@ -117,6 +117,9 @@ static const struct sock_intf_s g_inet_sockif =
inet_sendfile, /* si_sendfile */
#endif
inet_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ NULL, /* si_recvmsg */
+#endif
inet_close /* si_close */
};
diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c
index f2bbcb1..62d56b5 100644
--- a/net/local/local_sockif.c
+++ b/net/local/local_sockif.c
@@ -110,6 +110,9 @@ const struct sock_intf_s g_local_sockif =
NULL, /* si_sendfile */
#endif
local_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ NULL, /* si_recvmsg */
+#endif
local_close /* si_close */
};
diff --git a/net/netlink/netlink_sockif.c b/net/netlink/netlink_sockif.c
index cff507a..fff4411 100644
--- a/net/netlink/netlink_sockif.c
+++ b/net/netlink/netlink_sockif.c
@@ -110,6 +110,9 @@ const struct sock_intf_s g_netlink_sockif =
NULL, /* si_sendfile */
#endif
netlink_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ NULL, /* si_recvmsg */
+#endif
netlink_close /* si_close */
};
diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c
index e189ddc..bbabf42 100644
--- a/net/pkt/pkt_sockif.c
+++ b/net/pkt/pkt_sockif.c
@@ -108,6 +108,9 @@ const struct sock_intf_s g_pkt_sockif =
NULL, /* si_sendfile */
#endif
pkt_recvfrom, /* si_recvfrom */
+#ifdef CONFIG_NET_RECVMSG_CMSG
+ NULL, /* si_recvmsg */
+#endif
pkt_close /* si_close */
};
diff --git a/net/socket/Kconfig b/net/socket/Kconfig
index 8b71ade..f4c9408 100644
--- a/net/socket/Kconfig
+++ b/net/socket/Kconfig
@@ -55,5 +55,21 @@ config NET_SOLINGER
Enable or disable support for the SO_LINGER socket option. Requires
write buffer support.
+config NET_TIMESTAMP
+ bool "SO_TIMESTAMP socket option"
+ default n
+ depends on NET_CAN && NET_RECVMSG_CMSG
+ ---help---
+ Enable or disable support for the SO_TIMESTAMP socket option. Currently only tested & implemented in SocketCAN but should work on all sockets
+
endif # NET_SOCKOPTS
+
+config NET_RECVMSG_CMSG
+ bool "recvmsg() control messages (CMSG) support"
+ default n
+ ---help---
+ Enable or disable support for control messages in the recvmsg function.
+ Control messages (also defined in POSIX 1003.1g as ancillary data object information).
+ Includes additional information on the packet received.
+
endmenu # Socket Support
diff --git a/net/socket/Make.defs b/net/socket/Make.defs
index 47fb4d2..2be9dbf 100644
--- a/net/socket/Make.defs
+++ b/net/socket/Make.defs
@@ -74,3 +74,8 @@ endif
DEPPATH += --dep-path socket
VPATH += :socket
+
+# Support for recvmsg() with control messages (CMSG)
+ifeq ($(CONFIG_NET_RECVMSG_CMSG),y)
+SOCK_CSRCS += recvmsg.c
+endif
diff --git a/net/socket/getsockopt.c b/net/socket/getsockopt.c
index 47fd82f..9c2658b 100644
--- a/net/socket/getsockopt.c
+++ b/net/socket/getsockopt.c
@@ -277,6 +277,19 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option,
}
break;
+#ifdef CONFIG_NET_TIMESTAMP
+ case SO_TIMESTAMP:
+ {
+ if (*value_len != sizeof(int))
+ {
+ return -EINVAL;
+ }
+
+ *(FAR int *)value = (int)psock->s_timestamp;
+ }
+ break;
+#endif
+
/* The following are not yet implemented (return values other than {0,1) */
case SO_LINGER: /* Lingers on a close() if data is present */
diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c
index 4c1802c..3608152 100644
--- a/net/socket/setsockopt.c
+++ b/net/socket/setsockopt.c
@@ -276,6 +276,30 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option,
}
break;
#endif
+
+#ifdef CONFIG_NET_TIMESTAMP
+ case SO_TIMESTAMP: /* Generates a timestamp for each incoming packet */
+ {
+
+ /* Verify that option is at least the size of an integer. */
+
+ if (value_len < sizeof(FAR int32_t))
+ {
+ return -EINVAL;
+ }
+
+ /* Lock the network so that we have exclusive access to the socket
+ * options.
+ */
+
+ net_lock();
+
+ psock->s_timestamp = *((FAR int32_t*)value);
+
+ net_unlock();
+ }
+ break;
+#endif
/* The following are not yet implemented */
case SO_RCVBUF: /* Sets receive buffer size */
diff --git a/net/socket/socket.h b/net/socket/socket.h
index 0f809ac..80279da 100644
--- a/net/socket/socket.h
+++ b/net/socket/socket.h
@@ -80,7 +80,7 @@
/* This is the largest option value. REVISIT: belongs in sys/socket.h */
-#define _SO_MAXOPT (15)
+#define _SO_MAXOPT (29)
/* Macros to set, test, clear options */