diff --git a/arch/arm/src/common/stm32/Kconfig.eth b/arch/arm/src/common/stm32/Kconfig.eth index d682b5d345906..7a028c19fd855 100644 --- a/arch/arm/src/common/stm32/Kconfig.eth +++ b/arch/arm/src/common/stm32/Kconfig.eth @@ -188,7 +188,6 @@ config STM32_ETH_PTP_RTC_HIRES config STM32_ETH_TIMESTAMP_RX bool "Hardware timestamping of received packets" depends on STM32_COMMON_LEGACY && STM32_ETH_PTP && NET_TIMESTAMP && STM32_ETH_ENHANCEDDESC - select ARCH_HAVE_NETDEV_TIMESTAMP default n ---help--- Timestamp all received Ethernet packets. diff --git a/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c b/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c index a33b15a6bc791..b151bf6e9ced8 100644 --- a/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c +++ b/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c @@ -4234,6 +4234,10 @@ int stm32_ethinitialize(int intf) return ret; } +#ifdef CONFIG_STM32_ETH_TIMESTAMP_RX + priv->dev.d_features |= NETDEV_RX_STAMP; +#endif + /* Register the device with the OS so that socket IOCTLs can be performed */ netdev_register(&priv->dev, NET_LL_ETHERNET); diff --git a/arch/sim/src/sim/sim_netdriver.c b/arch/sim/src/sim/sim_netdriver.c index b41ae87e7ea2e..ee8310c7f02bb 100644 --- a/arch/sim/src/sim/sim_netdriver.c +++ b/arch/sim/src/sim/sim_netdriver.c @@ -63,6 +63,7 @@ #include #include +#include #include #include @@ -109,6 +110,9 @@ struct sim_netdev_s #endif uint8_t buf[SIM_NETDEV_BUFSIZE]; /* Used when packet buffer is fragmented */ struct work_s work; +#ifdef CONFIG_NET_TIMESTAMP + netpkt_queue_t tstampq; /* TX timestamp loopback queue */ +#endif }; /**************************************************************************** @@ -163,6 +167,25 @@ static int netdriver_send(struct netdev_lowerhalf_s *dev, netpkt_t *pkt) sim_netdev_send(DEVIDX(dev), netpkt_getdata(dev, pkt), len); } +#ifdef CONFIG_NET_TIMESTAMP + /* If the packet is tagged for TX timestamping, generate a + * software timestamp and loop it back through the RX path + * so the protocol layer can deliver it via MSG_ERRQUEUE. + * Reuse the original pkt directly to avoid clone overhead. + */ + + if (pkt->io_conn != NULL) + { + FAR struct sim_netdev_s *priv = (FAR struct sim_netdev_s *)dev; + + clock_gettime(CLOCK_REALTIME, &pkt->io_time); + iob_add_queue(pkt, &priv->tstampq); + atomic_add(&dev->quota_ptr[NETPKT_TX], 1); + netdev_lower_rxready(dev); + return OK; + } +#endif + netpkt_free(dev, pkt, NETPKT_TX); return OK; } @@ -172,6 +195,26 @@ static netpkt_t *netdriver_recv(struct netdev_lowerhalf_s *dev) netpkt_t *pkt = NULL; unsigned int len; +#ifdef CONFIG_NET_TIMESTAMP + /* Return any TX timestamp loopback packets first. + * Fast-path: skip locking when the queue is empty. + */ + + FAR struct sim_netdev_s *priv = (FAR struct sim_netdev_s *)dev; + + if (!IOB_QEMPTY(&priv->tstampq)) + { + netdev_lock(&dev->netdev); + pkt = iob_remove_queue(&priv->tstampq); + netdev_unlock(&dev->netdev); + if (pkt != NULL) + { + atomic_sub(&dev->quota_ptr[NETPKT_RX], 1); + return pkt; + } + } +#endif + if (sim_netdev_avail(DEVIDX(dev))) { pkt = netpkt_alloc(dev, NETPKT_RX); @@ -237,8 +280,23 @@ static int netdriver_ifup(struct netdev_lowerhalf_s *dev) static int netdriver_ifdown(struct netdev_lowerhalf_s *dev) { +#ifdef CONFIG_NET_TIMESTAMP + /* Drain any pending TX timestamp loopback packets. + * Detach the entire queue under lock, then free outside. + */ + + FAR struct sim_netdev_s *priv = (FAR struct sim_netdev_s *)dev; + FAR netpkt_t *pkt; + + while ((pkt = iob_remove_queue(&priv->tstampq)) != NULL) + { + netpkt_free(dev, pkt, NETPKT_TX); + } +#endif + netdev_lower_carrier_off(dev); sim_netdev_ifdown(DEVIDX(dev)); + return OK; } @@ -259,12 +317,14 @@ static int netdriver_rmmac(struct netdev_lowerhalf_s *dev, static void netdriver_txdone_interrupt(void *priv) { struct netdev_lowerhalf_s *dev = (struct netdev_lowerhalf_s *)priv; + netdev_lower_txdone(dev); } static void netdriver_rxready_interrupt(void *priv) { struct netdev_lowerhalf_s *dev = (struct netdev_lowerhalf_s *)priv; + netdev_lower_rxready(dev); } @@ -273,7 +333,11 @@ static void sim_netdev_work(void *arg) struct sim_netdev_s *priv = (struct sim_netdev_s *)arg; struct netdev_lowerhalf_s *dev = (struct netdev_lowerhalf_s *)&priv->dev; - if (sim_netdev_avail(DEVIDX(dev))) + if (sim_netdev_avail(DEVIDX(dev)) +#ifdef CONFIG_NET_TIMESTAMP + || !IOB_QEMPTY(&priv->tstampq) +#endif + ) { netdev_lower_rxready(dev); } @@ -347,15 +411,20 @@ void sim_netdriver_setmacaddr(int devidx, unsigned char *macaddr) void sim_netdriver_setmtu(int devidx, int mtu) { IDXDEV(devidx)->netdev.d_pktsize = MIN(SIM_NETDEV_BUFSIZE, - mtu + ETH_HDRLEN); + mtu + ETH_HDRLEN); } void sim_netdriver_loop(void) { int devidx; + for (devidx = 0; devidx < CONFIG_SIM_NETDEV_NUMBER; devidx++) { - if (sim_netdev_avail(devidx)) + if (sim_netdev_avail(devidx) +#ifdef CONFIG_NET_TIMESTAMP + || !IOB_QEMPTY(&g_sim_dev[devidx].tstampq) +#endif + ) { netdev_lower_rxready(IDXDEV(devidx)); } diff --git a/boards/arm/imx9/imx95-evk/configs/can/defconfig b/boards/arm/imx9/imx95-evk/configs/can/defconfig index fb147108d64f1..6b14202f5b98a 100644 --- a/boards/arm/imx9/imx95-evk/configs/can/defconfig +++ b/boards/arm/imx9/imx95-evk/configs/can/defconfig @@ -62,7 +62,6 @@ CONFIG_NET_CAN_EXTID=y CONFIG_NET_CAN_NOTIFIER=y CONFIG_NET_CAN_RAW_TX_DEADLINE=y CONFIG_NET_CAN_SOCK_OPTS=y -CONFIG_NET_LL_GUARDSIZE=14 CONFIG_NET_TIMESTAMP=y CONFIG_NSH_BUILTIN_APPS=y CONFIG_NSH_FILEIOSIZE=512 diff --git a/include/nuttx/mm/iob.h b/include/nuttx/mm/iob.h index 4c3c2710264ae..125dd2627ea5d 100644 --- a/include/nuttx/mm/iob.h +++ b/include/nuttx/mm/iob.h @@ -37,6 +37,10 @@ # include #endif +#ifdef CONFIG_NET_TIMESTAMP +# include +#endif + #ifdef CONFIG_MM_IOB /**************************************************************************** @@ -126,6 +130,18 @@ struct iob_s #endif unsigned int io_pktlen; /* Total length of the packet */ + FAR struct socket_conn_s *io_conn; + +#ifdef CONFIG_NET_TIMESTAMP + /* timestamp of the packet. + * d_features is the member of net_driver_s struct, if the NETDEV_RX_STAMP + * bit of d_features is set, the timestamp is provided by hardware driver. + * Otherwise it is filled in by kernel when the packet is passed into + * respective protocol layer. The timestamp is in CLOCK_REALTIME. + */ + + struct timespec io_time; +#endif #ifdef CONFIG_IOB_ALLOC iob_free_cb_t io_free; /* Custom free callback */ FAR uint8_t *io_data; diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h index 23a3f2f5e5d8a..c5395ba67cdc9 100644 --- a/include/nuttx/net/netdev.h +++ b/include/nuttx/net/netdev.h @@ -84,6 +84,7 @@ #define NETDEV_TX_CSUM (1 << 1) /* Netdev support hardware tx checksum */ #define NETDEV_RX_CSUM (1 << 2) /* Netdev support hardware rx checksum */ +#define NETDEV_RX_STAMP (1 << 3) /* Netdev support hardware timestamp */ /* Determine the largest possible address */ @@ -527,9 +528,10 @@ struct net_driver_s /* Reception timestamp of packet being currently processed. * If CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP is true, the timestamp is provided * by hardware driver. Otherwise it is filled in by kernel when packet - * enters ipv4_input or ipv6_input. + * enters ipv4_input or ipv6_input. The timestamp is in CLOCK_REALTIME. * - * The timestamp is in CLOCK_REALTIME. + * d_rxtime is serviced for older netdev driver. + * d_rxtime will be replaced by iob->io_tstamp in net stack. */ struct timespec d_rxtime; diff --git a/include/sys/socket.h b/include/sys/socket.h index 3cdb3ce75b819..50424bff924e8 100644 --- a/include/sys/socket.h +++ b/include/sys/socket.h @@ -223,6 +223,22 @@ #define SO_TIMESTAMPNS 20 /* Generates a timestamp in ns for each incoming packet * arg: integer value */ +#define SO_TIMESTAMPING 21 /* Generates timestamp for each output packet + */ + +/* Protocol-level socket options may begin with this value */ + +#define __SO_PROTOCOL 16 + +/* Timestamp generation */ + +#define SOF_TIMESTAMPING_TX_HARDWARE (1 << SO_TIMESTAMPING) +#define SOF_TIMESTAMPING_TX_SOFTWARE SOF_TIMESTAMPING_TX_HARDWARE + +/* Timestamp reporting */ + +#define SOF_TIMESTAMPING_SOFTWARE SOF_TIMESTAMPING_TX_SOFTWARE +#define SOF_TIMESTAMPING_RAW_HARDWARE SOF_TIMESTAMPING_TX_HARDWARE /* The options are unsupported but included for compatibility * and portability @@ -249,10 +265,6 @@ #define SOL_PACKET 19 -/* Protocol-level socket options may begin with this value */ - -#define __SO_PROTOCOL 16 - /* Values for the 'how' argument of shutdown() */ #define SHUT_RD 1 /* Bit 0: Disables further receive operations */ @@ -297,10 +309,12 @@ /* "Socket"-level control message types: */ -#define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */ -#define SCM_CREDENTIALS 0x02 /* rw: struct ucred */ -#define SCM_SECURITY 0x03 /* rw: security label */ -#define SCM_TIMESTAMP SO_TIMESTAMP +#define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */ +#define SCM_CREDENTIALS 0x02 /* rw: struct ucred */ +#define SCM_SECURITY 0x03 /* rw: security label */ +#define SCM_TIMESTAMP SO_TIMESTAMP +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS +#define SCM_TIMESTAMPING SO_TIMESTAMPING /* Desired design of maximum size and alignment (see RFC2553) */ diff --git a/mm/iob/iob_alloc.c b/mm/iob/iob_alloc.c index 27575084c039e..9eb822b986728 100644 --- a/mm/iob/iob_alloc.c +++ b/mm/iob/iob_alloc.c @@ -96,6 +96,9 @@ static FAR struct iob_s *iob_alloc_committed(void) iob->io_len = 0; /* Length of the data in the entry */ iob->io_offset = 0; /* Offset to the beginning of data */ iob->io_pktlen = 0; /* Total length of the packet */ +#ifdef CONFIG_NET_TIMESTAMPING + iob->io_conn = NULL; +#endif } spin_unlock_irqrestore(&g_iob_lock, flags); @@ -135,6 +138,9 @@ static FAR struct iob_s *iob_tryalloc_internal(bool throttled) iob->io_len = 0; /* Length of the data in the entry */ iob->io_offset = 0; /* Offset to the beginning of data */ iob->io_pktlen = 0; /* Total length of the packet */ +#ifdef CONFIG_NET_TIMESTAMPING + iob->io_conn = NULL; +#endif return iob; } } @@ -340,11 +346,8 @@ FAR struct iob_s *iob_alloc_dynamic(uint16_t size) iob = kmm_memalign(IOB_ALIGNMENT, alignsize); if (iob) { - iob->io_flink = NULL; /* Not in a chain */ - iob->io_len = 0; /* Length of the data in the entry */ - iob->io_offset = 0; /* Offset to the beginning of data */ + memset(iob, 0, offsetof(struct iob_s, io_data)); iob->io_bufsize = size; /* Total length of the iob buffer */ - iob->io_pktlen = 0; /* Total length of the packet */ iob->io_free = iob_free_dynamic; /* Customer free callback */ iob->io_data = (FAR uint8_t *)ALIGN_UP((uintptr_t)(iob + 1), IOB_ALIGNMENT); @@ -383,14 +386,10 @@ FAR struct iob_s *iob_alloc_with_data(FAR void *data, uint16_t size, DEBUGASSERT(free_cb != NULL); - iob = kmm_malloc(sizeof(struct iob_s)); + iob = kmm_zalloc(sizeof(struct iob_s)); if (iob) { - iob->io_flink = NULL; /* Not in a chain */ - iob->io_len = 0; /* Length of the data in the entry */ - iob->io_offset = 0; /* Offset to the beginning of data */ iob->io_bufsize = size; /* Total length of the iob buffer */ - iob->io_pktlen = 0; /* Total length of the packet */ iob->io_free = free_cb; /* Customer free callback */ iob->io_data = data; } @@ -426,10 +425,7 @@ FAR struct iob_s *iob_init_with_data(FAR void *data, uint16_t size, { FAR struct iob_s *iob = (FAR struct iob_s *)data; - iob->io_flink = NULL; /* Not in a chain */ - iob->io_len = 0; /* Length of the data in the entry */ - iob->io_offset = 0; /* Offset to the beginning of data */ - iob->io_pktlen = 0; /* Total length of the packet */ + memset(iob, 0, offsetof(struct iob_s, io_data)); iob->io_free = free_cb; /* Customer free callback */ iob->io_data = (FAR uint8_t *)ALIGN_UP((uintptr_t)(iob + 1), IOB_ALIGNMENT); diff --git a/mm/iob/iob_clone.c b/mm/iob/iob_clone.c index 3438a1b1097b5..86316bc4519a6 100644 --- a/mm/iob/iob_clone.c +++ b/mm/iob/iob_clone.c @@ -127,6 +127,10 @@ int iob_clone_partial(FAR struct iob_s *iob1, unsigned int len, iob2->io_pktlen = len + offset2; +#ifdef CONFIG_NET_TIMESTAMP + iob2->io_time = iob1->io_time; +#endif + /* Handle special case where there are empty buffers at the head * the list, Skip I/O buffer containing the data offset. */ diff --git a/net/Kconfig b/net/Kconfig index b561b3f8fcc5b..7ca3c46763778 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -23,10 +23,6 @@ config ARCH_HAVE_NETDEV_STATISTICS bool default n -config ARCH_HAVE_NETDEV_TIMESTAMP - bool - default n - config NET_WRITE_BUFFERS bool default n @@ -139,7 +135,6 @@ config NET_LL_GUARDSIZE int "Data Link Layer(L2) Guard size of Network buffer(IOB)" default 50 if RNDIS default 18 if NET_VLAN - default 16 if NET_CAN && NET_TIMESTAMP default 14 if NET_ETHERNET default 0 ---help--- diff --git a/net/can/can_bufpool.c b/net/can/can_bufpool.c index a6b2b47f909d3..a5140a7d80bff 100644 --- a/net/can/can_bufpool.c +++ b/net/can/can_bufpool.c @@ -36,14 +36,9 @@ * Pre-processor Definitions ****************************************************************************/ -#ifdef CONFIG_NET_TIMESTAMP # define CAN_BUFFER_SIZE ALIGN_UP(sizeof(struct iob_s) + NET_CAN_PKTSIZE + \ CONFIG_NET_LL_GUARDSIZE + IOB_ALIGNMENT - \ 1, IOB_ALIGNMENT) -#else -# define CAN_BUFFER_SIZE ALIGN_UP(sizeof(struct iob_s) + NET_CAN_PKTSIZE + \ - IOB_ALIGNMENT - 1, IOB_ALIGNMENT) -#endif /**************************************************************************** * Private Data @@ -117,10 +112,7 @@ FAR struct iob_s *can_iob_clone(FAR struct net_driver_s *dev) return NULL; } -#ifdef CONFIG_NET_TIMESTAMP iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE); -#endif - /* CAN data length is fixed, So when we use iob_clone_partial to copy * data, we don't have to worry about distributing other iob. */ diff --git a/net/can/can_callback.c b/net/can/can_callback.c index b06302e0e6c14..c5c072d143ab7 100644 --- a/net/can/can_callback.c +++ b/net/can/can_callback.c @@ -70,10 +70,6 @@ can_data_event(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn, int recvlen; uint32_t ret; -#ifdef CONFIG_NET_TIMESTAMP - buflen -= sizeof(struct timeval); -#endif - ret = (flags & ~CAN_NEWDATA); /* Save as the packet data as in the read-ahead buffer. NOTE that @@ -126,31 +122,6 @@ uint32_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 (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) && - (dev->d_iob != NULL)) - { - struct timeval tv; - FAR struct timespec *ts = (FAR struct timespec *)&tv; - int len; - - clock_systime_timespec(ts); - tv.tv_usec = ts->tv_nsec / 1000; - - len = iob_trycopyin(dev->d_iob, (FAR uint8_t *)&tv, - sizeof(struct timeval), - -CONFIG_NET_LL_GUARDSIZE, false); - if (len == sizeof(struct timeval)) - { - dev->d_len += len; - } - } -#endif - conn_lock(&conn->sconn); flags = devif_conn_event(dev, flags, conn->sconn.list); diff --git a/net/can/can_input.c b/net/can/can_input.c index a2c2b497f1760..315186c5852ee 100644 --- a/net/can/can_input.c +++ b/net/can/can_input.c @@ -229,6 +229,17 @@ static int can_in(FAR struct net_driver_s *dev) return OK; } + /* Storing reception timestamp provided by realtime + * if timestamp no provided by hardware. + */ + +#ifdef CONFIG_NET_TIMESTAMP + if ((dev->d_features & NETDEV_RX_STAMP) == 0) + { + clock_gettime(CLOCK_REALTIME, &dev->d_iob->io_time); + } +#endif /* CONFIG_NET_TIMESTAMP */ + can_conn_list_lock(); /* Do we have second connection that can hold this packet? */ @@ -311,7 +322,7 @@ int can_input(FAR struct net_driver_s *dev) if (ret < 0) { #ifdef CONFIG_NET_STATISTICS - g_netstats.can.drop++; + g_netstats.can.drop++; #endif } diff --git a/net/can/can_recvmsg.c b/net/can/can_recvmsg.c index 0a26ea283449d..d69fbbc52d91b 100644 --- a/net/can/can_recvmsg.c +++ b/net/can/can_recvmsg.c @@ -63,12 +63,9 @@ struct can_recvfrom_s { FAR struct can_conn_s *pr_conn; /* Connection associated with the socket */ FAR struct devif_callback_s *pr_cb; /* Reference to callback instance */ + FAR struct msghdr *pr_msg; /* Pointer to receive buffer */ sem_t pr_sem; /* Semaphore signals recv completion */ - size_t pr_buflen; /* Length of receive buffer */ - FAR uint8_t *pr_buffer; /* Pointer to receive buffer */ ssize_t pr_recvlen; /* The received length */ - size_t pr_msglen; /* Length of msg buffer */ - FAR uint8_t *pr_msgbuf; /* Pointer to msg buffer */ int pr_result; /* Success:OK, failure:negated errno */ }; @@ -76,6 +73,37 @@ struct can_recvfrom_s * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: can_recvfrom_initialize + * + * Description: + * Initialize the state structure + * + * Input Parameters: + * conn The CAN connection of interest + * msg Receive info and buffer for receive data + * pstate A pointer to the state structure to be initialized + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void can_recvfrom_initialize(FAR struct can_conn_s *conn, + FAR struct msghdr *msg, + FAR struct can_recvfrom_s *pstate) +{ + /* Initialize the state structure. */ + + memset(pstate, 0, sizeof(struct can_recvfrom_s)); + nxsem_init(&pstate->pr_sem, 0, 0); + + pstate->pr_conn = conn; + pstate->pr_msg = msg; +} + /**************************************************************************** * Name: can_add_recvlen * @@ -102,8 +130,6 @@ static inline void can_add_recvlen(FAR struct can_recvfrom_s *pstate, } pstate->pr_recvlen += recvlen; - pstate->pr_buffer += recvlen; - pstate->pr_buflen -= recvlen; } /**************************************************************************** @@ -129,31 +155,20 @@ static size_t can_recvfrom_newdata(FAR struct net_driver_s *dev, { unsigned int offset; size_t recvlen; -#ifdef CONFIG_NET_TIMESTAMP - FAR struct can_conn_s *conn = pstate->pr_conn; - if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) && - pstate->pr_msglen == sizeof(struct timeval)) - { - iob_copyout(pstate->pr_msgbuf, dev->d_iob, sizeof(struct timeval), - -CONFIG_NET_LL_GUARDSIZE); - } +#ifdef CONFIG_NET_TIMESTAMP + cmsg_store_timestamp(pstate->pr_msg, &dev->d_iob->io_time, + pstate->pr_conn->sconn.s_options); #endif - if (dev->d_len > pstate->pr_buflen) - { - recvlen = pstate->pr_buflen; - } - else - { - recvlen = dev->d_len; - } + recvlen = MIN(pstate->pr_msg->msg_iov->iov_len, dev->d_len); /* Copy the new packet data into the user buffer */ offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset; - recvlen = iob_copyout(pstate->pr_buffer, dev->d_iob, recvlen, offset); + recvlen = iob_copyout(pstate->pr_msg->msg_iov->iov_base, + dev->d_iob, recvlen, offset); /* Trim the copied buffers */ @@ -238,27 +253,28 @@ static inline int can_readahead(struct can_recvfrom_s *pstate) * buffer. */ - pstate->pr_recvlen = -1; + pstate->pr_recvlen = -ENODATA; - if (pstate->pr_buflen > 0 && + if (pstate->pr_msg->msg_iov->iov_len > 0 && (iob = iob_remove_queue(&conn->readahead)) != NULL) { DEBUGASSERT(iob->io_pktlen > 0); #ifdef CONFIG_NET_TIMESTAMP - if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) && - pstate->pr_msglen == sizeof(struct timeval)) - { - iob_copyout(pstate->pr_msgbuf, iob, sizeof(struct timeval), - -CONFIG_NET_LL_GUARDSIZE); - } + cmsg_store_timestamp(pstate->pr_msg, &iob->io_time, + conn->sconn.s_options); #endif /* Transfer that buffered data from the I/O buffer chain into * the user buffer. */ - recvlen = iob_copyout(pstate->pr_buffer, iob, pstate->pr_buflen, 0); + recvlen = iob_copyout(pstate->pr_msg->msg_iov->iov_base, + iob, pstate->pr_msg->msg_iov->iov_len, 0); + + /* Update the accumulated size of the data read */ + + pstate->pr_recvlen = recvlen; /* We should have taken all of the data from the I/O buffer chain, * so release it. There is no trimming needed, since One CAN/CANFD @@ -297,8 +313,7 @@ static uint32_t can_recvfrom_eventhandler(FAR struct net_driver_s *dev, if (pstate) { -#if (defined(CONFIG_NET_CANPROTO_OPTIONS) && defined(CONFIG_NET_CAN_CANFD)) \ - || defined(CONFIG_NET_TIMESTAMP) +#if defined(CONFIG_NET_CANPROTO_OPTIONS) && defined(CONFIG_NET_CAN_CANFD) struct can_conn_s *conn = pstate->pr_conn; #endif @@ -309,15 +324,7 @@ static uint32_t can_recvfrom_eventhandler(FAR struct net_driver_s *dev, if (!_SO_GETOPT(conn->sconn.s_options, CAN_RAW_FD_FRAMES)) #endif { -#ifdef CONFIG_NET_TIMESTAMP - if ((_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) && - dev->d_len > sizeof(struct can_frame) + - sizeof(struct timeval)) || - (!_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) && - dev->d_len > sizeof(struct can_frame))) -#else if (dev->d_len > sizeof(struct can_frame)) -#endif { /* DO WE NEED TO CLEAR FLAGS?? */ @@ -442,25 +449,7 @@ ssize_t can_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, /* Initialize the state structure. */ - memset(&state, 0, sizeof(struct can_recvfrom_s)); - nxsem_init(&state.pr_sem, 0, 0); /* Doesn't really fail */ - - state.pr_buflen = msg->msg_iov->iov_len; - state.pr_buffer = msg->msg_iov->iov_base; - -#ifdef CONFIG_NET_TIMESTAMP - if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP)) - { - state.pr_msgbuf = cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMP, - NULL, sizeof(struct timeval)); - if (state.pr_msgbuf != NULL) - { - state.pr_msglen = sizeof(struct timeval); - } - } -#endif - - state.pr_conn = conn; + can_recvfrom_initialize(conn, msg, &state); /* 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 diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c index eb880ad2e901c..15f1b62a84253 100644 --- a/net/devif/ipv4_input.c +++ b/net/devif/ipv4_input.c @@ -149,6 +149,7 @@ static int ipv4_check_opt(FAR struct ipv4_hdr_s *ipv4) else if (optlen > 1) { int len = opt[1]; + if (len > optlen) { return -EINVAL; @@ -231,6 +232,17 @@ static int ipv4_in(FAR struct net_driver_s *dev) bool isfrag; int ret = OK; + /* Storing reception timestamp provided by realtime + * if timestamp no provided by hardware. + */ + +#ifdef CONFIG_NET_TIMESTAMP + if ((dev->d_features & NETDEV_RX_STAMP) == 0) + { + clock_gettime(CLOCK_REALTIME, &dev->d_iob->io_time); + } +#endif /* CONFIG_NET_TIMESTAMP */ + /* Handle ARP on input then give the IPv4 packet to the network layer */ arp_ipin(dev); @@ -483,7 +495,7 @@ static int ipv4_in(FAR struct net_driver_s *dev) #endif #ifdef NET_ICMP_HAVE_STACK - /* Check for ICMP input */ + /* Check for ICMP input */ case IP_PROTO_ICMP: /* ICMP input */ icmp_input(dev); @@ -491,7 +503,7 @@ static int ipv4_in(FAR struct net_driver_s *dev) #endif #ifdef CONFIG_NET_IGMP - /* Check for IGMP input */ + /* Check for IGMP input */ case IP_PROTO_IGMP: /* IGMP input */ igmp_input(dev); @@ -573,12 +585,6 @@ int ipv4_input(FAR struct net_driver_s *dev) netdev_lock(dev); - /* Store reception timestamp if enabled and not provided by hardware. */ - -#if defined(CONFIG_NET_TIMESTAMP) && !defined(CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP) - clock_gettime(CLOCK_REALTIME, &dev->d_rxtime); -#endif - if (dev->d_iob != NULL) { buf = dev->d_buf; diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c index 3400fdebd7906..87067bcf367fa 100644 --- a/net/devif/ipv6_input.c +++ b/net/devif/ipv6_input.c @@ -229,6 +229,17 @@ static int ipv6_in(FAR struct net_driver_s *dev) bool isfrag = false; #endif + /* Storing reception timestamp provided by realtime + * if timestamp no provided by hardware. + */ + +#ifdef CONFIG_NET_TIMESTAMP + if ((dev->d_features & NETDEV_RX_STAMP) == 0) + { + clock_gettime(CLOCK_REALTIME, &dev->d_iob->io_time); + } +#endif /* CONFIG_NET_TIMESTAMP */ + /* This is where the input processing starts. */ #ifdef CONFIG_NET_STATISTICS @@ -706,12 +717,6 @@ int ipv6_input(FAR struct net_driver_s *dev) netdev_lock(dev); - /* Store reception timestamp if enabled and not provided by hardware. */ - -#if defined(CONFIG_NET_TIMESTAMP) && !defined(CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP) - clock_gettime(CLOCK_REALTIME, &dev->d_rxtime); -#endif - if (dev->d_iob != NULL) { buf = dev->d_buf; diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index b8012b4fc6ca1..db3fccbe4c3fe 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -72,48 +72,62 @@ static void inet_addref(FAR struct socket *psock); static int inet_bind(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen); + static int inet_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen); + static int inet_getpeername(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen); + static int inet_listen(FAR struct socket *psock, int backlog); static int inet_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen); + static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen, FAR struct socket *newsock, int flags); + static int inet_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup); + static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); + static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, int flags, FAR const struct sockaddr *to, socklen_t tolen); + static ssize_t inet_sendmsg(FAR struct socket *psock, FAR const struct msghdr *msg, int flags); + static ssize_t inet_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, int flags); + static int inet_ioctl(FAR struct socket *psock, int cmd, unsigned long arg); + static int inet_socketpair(FAR struct socket *psocks[2]); static int inet_shutdown(FAR struct socket *psock, int how); #ifdef CONFIG_NET_SOCKOPTS static int inet_getsockopt(FAR struct socket *psock, int level, int option, FAR void *value, FAR socklen_t *value_len); + static int inet_setsockopt(FAR struct socket *psock, int level, int option, FAR const void *value, socklen_t value_len); + #endif #ifdef CONFIG_NET_SENDFILE static ssize_t inet_sendfile(FAR struct socket *psock, FAR struct file *infile, FAR off_t *offset, size_t count); + #endif /**************************************************************************** @@ -165,6 +179,7 @@ static int inet_tcp_alloc(FAR struct socket *psock) /* Allocate the TCP connection structure */ FAR struct tcp_conn_s *conn = tcp_alloc(psock->s_domain); + if (conn == NULL) { /* Failed to reserve a connection structure */ @@ -207,6 +222,7 @@ static int inet_udp_alloc(FAR struct socket *psock) /* Allocate the UDP connection structure */ FAR struct udp_conn_s *conn = udp_alloc(psock->s_domain); + if (conn == NULL) { /* Failed to reserve a connection structure */ @@ -362,6 +378,7 @@ static void inet_addref(FAR struct socket *psock) if (psock->s_type == SOCK_STREAM) { FAR struct tcp_conn_s *conn = psock->s_conn; + DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); conn->crefs++; } @@ -371,6 +388,7 @@ static void inet_addref(FAR struct socket *psock) if (psock->s_type == SOCK_DGRAM) { FAR struct udp_conn_s *conn = psock->s_conn; + DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); conn->crefs++; } @@ -412,20 +430,20 @@ static int inet_bind(FAR struct socket *psock, switch (addr->sa_family) { #ifdef CONFIG_NET_IPv4 - case AF_INET: - minlen = sizeof(struct sockaddr_in); - break; + case AF_INET: + minlen = sizeof(struct sockaddr_in); + break; #endif #ifdef CONFIG_NET_IPv6 - case AF_INET6: - minlen = sizeof(struct sockaddr_in6); - break; + case AF_INET6: + minlen = sizeof(struct sockaddr_in6); + break; #endif - default: - nerr("ERROR: Unrecognized address family: %d\n", addr->sa_family); - return -EAFNOSUPPORT; + default: + nerr("ERROR: Unrecognized address family: %d\n", addr->sa_family); + return -EAFNOSUPPORT; } if (addrlen < minlen) @@ -517,17 +535,17 @@ static int inet_getsockname(FAR struct socket *psock, switch (psock->s_domain) { #ifdef CONFIG_NET_IPv4 - case PF_INET: - return ipv4_getsockname(psock, addr, addrlen); + case PF_INET: + return ipv4_getsockname(psock, addr, addrlen); #endif #ifdef CONFIG_NET_IPv6 - case PF_INET6: - return ipv6_getsockname(psock, addr, addrlen); + case PF_INET6: + return ipv6_getsockname(psock, addr, addrlen); #endif - default: - return -EAFNOSUPPORT; + default: + return -EAFNOSUPPORT; } } @@ -568,17 +586,17 @@ static int inet_getpeername(FAR struct socket *psock, switch (psock->s_domain) { #ifdef CONFIG_NET_IPv4 - case PF_INET: - return ipv4_getpeername(psock, addr, addrlen); + case PF_INET: + return ipv4_getpeername(psock, addr, addrlen); #endif #ifdef CONFIG_NET_IPv6 - case PF_INET6: - return ipv6_getpeername(psock, addr, addrlen); + case PF_INET6: + return ipv6_getpeername(psock, addr, addrlen); #endif - default: - return -EAFNOSUPPORT; + default: + return -EAFNOSUPPORT; } } @@ -637,6 +655,7 @@ static int inet_get_socketlevel_option(FAR struct socket *psock, int option, if (psock->s_type == SOCK_STREAM) { FAR struct tcp_conn_s *tcp = psock->s_conn; + *(FAR int *)value = tcp->rcv_bufs; } else @@ -645,6 +664,7 @@ static int inet_get_socketlevel_option(FAR struct socket *psock, int option, if (psock->s_type == SOCK_DGRAM) { FAR struct udp_conn_s *udp = psock->s_conn; + *(FAR int *)value = udp->rcvbufs; } else @@ -668,6 +688,7 @@ static int inet_get_socketlevel_option(FAR struct socket *psock, int option, if (psock->s_type == SOCK_STREAM) { FAR struct tcp_conn_s *tcp = psock->s_conn; + *(FAR int *)value = tcp->snd_bufs; } else @@ -713,29 +734,6 @@ static int inet_get_socketlevel_option(FAR struct socket *psock, int option, } #endif -#ifdef CONFIG_NET_TIMESTAMP - case SO_TIMESTAMP: - { - if (*value_len != sizeof(int)) - { - return -EINVAL; - } - -# ifdef NET_UDP_HAVE_STACK - if (psock->s_type == SOCK_DGRAM) - { - FAR struct udp_conn_s *conn = psock->s_conn; - *(FAR int *)value = (conn->timestamp != 0); - } - else -# endif - { - return -ENOPROTOOPT; - } - } - break; -#endif - default: return -ENOPROTOOPT; } @@ -790,7 +788,7 @@ static int inet_getsockopt(FAR struct socket *psock, int level, int option, #ifdef CONFIG_NET_IPv6 case IPPROTO_IPV6:/* IPv6 protocol socket options (see include/netinet/in.h) */ - return ipv6_getsockopt(psock, option, value, value_len); + return ipv6_getsockopt(psock, option, value, value_len); #endif default: @@ -1017,38 +1015,6 @@ static int inet_set_socketlevel_option(FAR struct socket *psock, int option, break; #endif -#ifdef CONFIG_NET_TIMESTAMP - case SO_TIMESTAMP: /* Report receive timestamps as cmsg */ - { - if (value_len < sizeof(int)) - { - return -EINVAL; - } - -# ifdef NET_UDP_HAVE_STACK - if (psock->s_type == SOCK_DGRAM) - { - conn_lock(psock->s_conn); - - /* For now the timestamp enable is just boolean. - * If SO_TIMESTAMPING support is added in future, it can be - * expanded to flags field for rx/tx timestamps. - */ - - FAR struct udp_conn_s *conn = psock->s_conn; - conn->timestamp = (*((FAR int *)value) != 0); - - conn_unlock(psock->s_conn); - } - else -# endif - { - return -ENOPROTOOPT; - } - } - break; - #endif - default: return -ENOPROTOOPT; } @@ -1143,6 +1109,7 @@ static int inet_listen(FAR struct socket *psock, int backlog) #if defined(CONFIG_NET_TCP) && defined(NET_TCP_HAVE_STACK) FAR struct tcp_conn_s *conn; int ret; + #endif /* Verify that the sockfd corresponds to a connected SOCK_STREAM */ @@ -1267,33 +1234,33 @@ static int inet_connect(FAR struct socket *psock, switch (inaddr->sin_family) { #ifdef CONFIG_NET_IPv4 - case AF_INET: - { - if (addrlen < sizeof(struct sockaddr_in)) - { - return -EINVAL; - } - } - break; + case AF_INET: + { + if (addrlen < sizeof(struct sockaddr_in)) + { + return -EINVAL; + } + } + break; #endif #ifdef CONFIG_NET_IPv6 - case AF_INET6: - { - if (addrlen < sizeof(struct sockaddr_in6)) - { - return -EINVAL; - } - } - break; + case AF_INET6: + { + if (addrlen < sizeof(struct sockaddr_in6)) + { + return -EINVAL; + } + } + break; #endif - case AF_UNSPEC: - break; + case AF_UNSPEC: + break; - default: - DEBUGPANIC(); - return -EAFNOSUPPORT; + default: + DEBUGPANIC(); + return -EAFNOSUPPORT; } /* Perform the connection depending on the protocol type */ @@ -1428,6 +1395,7 @@ static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr, { #if defined(CONFIG_NET_TCP) && defined(NET_TCP_HAVE_STACK) int ret; + #endif /* Is the socket a stream? */ @@ -1453,30 +1421,30 @@ static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr, switch (psock->s_domain) { #ifdef CONFIG_NET_IPv4 - case PF_INET: - { - if (*addrlen < sizeof(struct sockaddr_in)) - { - return -EINVAL; - } - } - break; + case PF_INET: + { + if (*addrlen < sizeof(struct sockaddr_in)) + { + return -EINVAL; + } + } + break; #endif /* CONFIG_NET_IPv4 */ #ifdef CONFIG_NET_IPv6 - case PF_INET6: - { - if (*addrlen < sizeof(struct sockaddr_in6)) - { - return -EINVAL; - } - } - break; + case PF_INET6: + { + if (*addrlen < sizeof(struct sockaddr_in6)) + { + return -EINVAL; + } + } + break; #endif /* CONFIG_NET_IPv6 */ - default: - DEBUGPANIC(); - return -EINVAL; + default: + DEBUGPANIC(); + return -EINVAL; } } @@ -1501,9 +1469,9 @@ static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr, return ret; } - /* Begin monitoring for TCP connection events on the newly connected - * socket - */ + /* Begin monitoring for TCP connection events on the newly connected + * socket + */ ret = tcp_start_monitor(newsock); if (ret < 0) @@ -1647,9 +1615,9 @@ static int inet_poll(FAR struct socket *psock, FAR struct pollfd *fds, return inet_pollteardown(psock, fds); } #else - { - return -ENOSYS; - } + { + return -ENOSYS; + } #endif /* NET_TCP_HAVE_STACK || !NET_UDP_HAVE_STACK */ } @@ -1678,6 +1646,7 @@ static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, { #ifdef NET_UDP_HAVE_STACK FAR struct socket_conn_s *conn = psock->s_conn; + #endif ssize_t ret; @@ -1713,9 +1682,9 @@ static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, case SOCK_DGRAM: { #if defined(CONFIG_NET_6LOWPAN) - /* Try 6LoWPAN UDP packet send */ + /* Try 6LoWPAN UDP packet send */ - ret = psock_6lowpan_udp_send(psock, buf, len); + ret = psock_6lowpan_udp_send(psock, buf, len); #ifdef NET_UDP_HAVE_STACK if (ret < 0) @@ -1788,20 +1757,20 @@ static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, switch (to->sa_family) { #ifdef CONFIG_NET_IPv4 - case AF_INET: - minlen = sizeof(struct sockaddr_in); - break; + case AF_INET: + minlen = sizeof(struct sockaddr_in); + break; #endif #ifdef CONFIG_NET_IPv6 - case AF_INET6: - minlen = sizeof(struct sockaddr_in6); - break; + case AF_INET6: + minlen = sizeof(struct sockaddr_in6); + break; #endif - default: - nerr("ERROR: Unrecognized address family: %d\n", to->sa_family); - return -EAFNOSUPPORT; + default: + nerr("ERROR: Unrecognized address family: %d\n", to->sa_family); + return -EAFNOSUPPORT; } if (tolen < minlen) @@ -1968,8 +1937,10 @@ static int inet_socketpair(FAR struct socket *psocks[2]) { #if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) FAR struct socket *pserver = psocks[1]; + #if defined(CONFIG_NET_TCP) FAR struct socket server; + #endif union sockaddr_u addr[2]; socklen_t len; @@ -2208,24 +2179,24 @@ static ssize_t inet_recvmsg(FAR struct socket *psock, switch (psock->s_domain) { #ifdef CONFIG_NET_IPv4 - case PF_INET: - { - minlen = sizeof(struct sockaddr_in); - } - break; + case PF_INET: + { + minlen = sizeof(struct sockaddr_in); + } + break; #endif #ifdef CONFIG_NET_IPv6 - case PF_INET6: - { - minlen = sizeof(struct sockaddr_in6); - } - break; + case PF_INET6: + { + minlen = sizeof(struct sockaddr_in6); + } + break; #endif - default: - DEBUGPANIC(); - return -EINVAL; + default: + DEBUGPANIC(); + return -EINVAL; } if (msg->msg_namelen < minlen) @@ -2241,35 +2212,35 @@ static ssize_t inet_recvmsg(FAR struct socket *psock, switch (psock->s_type) { #ifdef CONFIG_NET_TCP - case SOCK_STREAM: - { + case SOCK_STREAM: + { #ifdef NET_TCP_HAVE_STACK - ret = psock_tcp_recvfrom(psock, msg, flags); + ret = psock_tcp_recvfrom(psock, msg, flags); #else - ret = -ENOSYS; + ret = -ENOSYS; #endif - } - break; + } + break; #endif /* CONFIG_NET_TCP */ #ifdef CONFIG_NET_UDP - case SOCK_DGRAM: - { + case SOCK_DGRAM: + { #ifdef NET_UDP_HAVE_STACK - ret = psock_udp_recvfrom(psock, msg, flags); + ret = psock_udp_recvfrom(psock, msg, flags); #else - ret = -ENOSYS; + ret = -ENOSYS; #endif - } - break; + } + break; #endif /* CONFIG_NET_UDP */ - default: - { - nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); - ret = -ENOSYS; - } - break; + default: + { + nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); + ret = -ENOSYS; + } + break; } return ret; @@ -2437,9 +2408,9 @@ inet_sockif(sa_family_t family, int type, int protocol) return &g_inet_sockif; } #else - { - return NULL; - } + { + return NULL; + } #endif } diff --git a/net/netdev/netdev_input.c b/net/netdev/netdev_input.c index 6554e11520d8b..36333d27bc840 100644 --- a/net/netdev/netdev_input.c +++ b/net/netdev/netdev_input.c @@ -80,6 +80,13 @@ int netdev_input(FAR struct net_driver_s *dev, return ret; } +#if defined(CONFIG_NET_TIMESTAMP) + if ((dev->d_features & NETDEV_RX_STAMP) != 0) + { + dev->d_iob->io_time = dev->d_rxtime; + } +#endif + /* Copy data to iob entry */ ret = iob_trycopyin(dev->d_iob, buf, dev->d_len, -llhdrlen, false); diff --git a/net/pkt/pkt.h b/net/pkt/pkt.h index 63d946dafa284..2aaa52f6258ef 100644 --- a/net/pkt/pkt.h +++ b/net/pkt/pkt.h @@ -104,6 +104,10 @@ struct pkt_conn_s struct iob_queue_s readahead; /* Read-ahead buffering */ +#ifdef CONFIG_NET_TIMESTAMP + struct iob_queue_s errahead; /* Error-ahead buffering */ +#endif + FAR struct iob_s *pendiob; /* The iob currently being sent */ /* The following is a list of poll structures of threads waiting for diff --git a/net/pkt/pkt_input.c b/net/pkt/pkt_input.c index 5840d246a700e..a287f114d8b74 100644 --- a/net/pkt/pkt_input.c +++ b/net/pkt/pkt_input.c @@ -52,6 +52,7 @@ * Input Parameters: * dev - Device instance only the input packet in d_buf, length = d_len; * conn - A pointer to the PKT connection structure + * iobq - A pointer to the buffer queue * * Returned Value: * The number of bytes actually buffered is returned. This will be either @@ -60,7 +61,8 @@ ****************************************************************************/ static uint16_t pkt_datahandler(FAR struct net_driver_s *dev, - FAR struct pkt_conn_s *conn) + FAR struct pkt_conn_s *conn, + FAR struct iob_queue_s *iobq) { FAR struct iob_s *iob = iob_tryalloc(true); int ret; @@ -70,22 +72,6 @@ static uint16_t pkt_datahandler(FAR struct net_driver_s *dev, return 0; } -#ifdef CONFIG_NET_TIMESTAMP - if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) || - _SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMPNS)) - { - ret = iob_trycopyin(iob, (FAR const uint8_t *)&dev->d_rxtime, - sizeof(struct timespec), 0, true); - if (ret != sizeof(struct timespec)) - { - nerr("ERROR: Failed to write timestamp: %d\n", ret); - goto errout; - } - - iob_reserve(iob, sizeof(struct timespec)); - } -#endif - /* Clone an I/O buffer chain of the L2 data, use throttled IOB to avoid * overconsumption. * TODO: Optimize IOB clone after we support shared IOB. @@ -104,7 +90,7 @@ static uint16_t pkt_datahandler(FAR struct net_driver_s *dev, */ conn_lock(&conn->sconn); - ret = iob_tryadd_queue(iob, &conn->readahead); + ret = iob_tryadd_queue(iob, iobq); conn_unlock(&conn->sconn); if (ret < 0) @@ -171,13 +157,42 @@ static int pkt_in(FAR struct net_driver_s *dev) return OK; } -#if defined(CONFIG_NET_TIMESTAMP) && !defined(CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP) - /* Get system as timestamp if no hardware timestamp */ +#ifdef CONFIG_NET_TIMESTAMP + + /* Handle hardware timestamp */ - if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) || - _SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMPNS)) + if (dev->d_iob->io_conn == &conn->sconn) { - clock_gettime(CLOCK_REALTIME, &dev->d_rxtime); + if (pkt_datahandler(dev, conn, &conn->errahead) > 0) + { + pkt_callback(dev, conn, PKT_NEWDATA); + } + + pkt_conn_list_unlock(); + return OK; + } + + if (dev->d_iob->io_conn != NULL) + { + /* Skip no related pkt conn */ + + pkt_conn_list_unlock(); + return OK; + } +#endif + +#ifdef CONFIG_NET_TIMESTAMP + /* Storing reception timestamp provided by realtime + * if timestamp no provided by hardware. + */ + + if ((dev->d_features & NETDEV_RX_STAMP) == 0) + { + /* Storing reception timestamp provided by realtime + * if timestamp no provided by hardware. + */ + + clock_gettime(CLOCK_REALTIME, &dev->d_iob->io_time); } #endif /* CONFIG_NET_TIMESTAMP */ @@ -198,7 +213,7 @@ static int pkt_in(FAR struct net_driver_s *dev) { /* Add the PKT to the socket read-ahead buffer. */ - if (pkt_datahandler(dev, conn) == 0) + if (pkt_datahandler(dev, conn, &conn->readahead) == 0) { /* No.. the packet was not processed now. Return -EAGAIN so * that the driver may retry again later. diff --git a/net/pkt/pkt_netpoll.c b/net/pkt/pkt_netpoll.c index 991db490aa50d..5e07cd9bf03b5 100644 --- a/net/pkt/pkt_netpoll.c +++ b/net/pkt/pkt_netpoll.c @@ -124,7 +124,7 @@ static uint32_t pkt_poll_eventhandler(FAR struct net_driver_s *dev, if ((flags & NETDEV_DOWN) != 0) { - eventset |= (POLLHUP | POLLERR); + eventset |= POLLHUP | POLLERR; } /* A poll is a sign that we are free to send data. */ @@ -134,6 +134,15 @@ static uint32_t pkt_poll_eventhandler(FAR struct net_driver_s *dev, eventset |= POLLOUT; } +#ifdef CONFIG_NET_TIMESTAMP + /* Check for timestamping data */ + + if (!IOB_QEMPTY(&info->conn->errahead)) + { + eventset |= POLLPRI | POLLERR; + } +#endif + /* Awaken the caller of poll() is requested event occurred. */ poll_notify(&info->fds, 1, eventset); @@ -237,6 +246,13 @@ int pkt_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) cb->flags |= PKT_NEWDATA; } +#ifdef CONFIG_NET_TIMESTAMP + if ((fds->events & POLLPRI) != 0) + { + cb->flags |= PKT_NEWDATA; + } +#endif + /* Save the reference in the poll info structure as fds private as well * for use during poll teardown as well. */ @@ -261,6 +277,15 @@ int pkt_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) eventset |= POLLWRNORM; } + /* Check for timestamping data */ + +#ifdef CONFIG_NET_TIMESTAMP + if (!IOB_QEMPTY(&conn->errahead)) + { + eventset |= POLLPRI | POLLERR; + } +#endif + /* Check if any requested events are already in effect */ poll_notify(&fds, 1, eventset); diff --git a/net/pkt/pkt_recvmsg.c b/net/pkt/pkt_recvmsg.c index 531e48e9183d4..df1e4eea3842c 100644 --- a/net/pkt/pkt_recvmsg.c +++ b/net/pkt/pkt_recvmsg.c @@ -65,46 +65,6 @@ struct pkt_recvfrom_s uint8_t pr_type; /* Protocol type */ }; -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: pkt_store_cmsg_timestamp - * - * Description: - * Store the timestamp in the cmsg - * - * Input Parameters: - * pstate Recicve state information - * timestamp Timestamp information - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef CONFIG_NET_TIMESTAMP -static void pkt_store_cmsg_timestamp(FAR struct pkt_recvfrom_s *pstate, - FAR struct timespec *timestamp) -{ - FAR struct msghdr *msg = pstate->pr_msg; - struct timeval tv; - - if (_SO_GETOPT(pstate->pr_conn->sconn.s_options, SO_TIMESTAMPNS)) - { - cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMPNS, timestamp, - sizeof(struct timespec)); - } - else - { - TIMESPEC_TO_TIMEVAL(&tv, timestamp); - cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMP, &tv, - sizeof(struct timeval)); - } -} -#endif - /**************************************************************************** * Name: pkt_add_recvlen * @@ -158,13 +118,8 @@ static void pkt_recvfrom_newdata(FAR struct net_driver_s *dev, size_t recvlen; #ifdef CONFIG_NET_TIMESTAMP - /* Unpack stored timestamp if SO_TIMESTAMP socket option is enabled */ - - if (_SO_GETOPT(pstate->pr_conn->sconn.s_options, SO_TIMESTAMP) || - _SO_GETOPT(pstate->pr_conn->sconn.s_options, SO_TIMESTAMPNS)) - { - pkt_store_cmsg_timestamp(pstate, &dev->d_rxtime); - } + cmsg_store_timestamp(pstate->pr_msg, &dev->d_iob->io_time, + pstate->pr_conn->sconn.s_options); #endif recvlen = MIN(pstate->pr_msg->msg_iov->iov_len, dev->d_len); @@ -230,6 +185,18 @@ static uint32_t pkt_recvfrom_eventhandler(FAR struct net_driver_s *dev, { /* If a new packet is available, then complete the read action. */ +#ifdef CONFIG_NET_TIMESTAMP + if ((flags & PKT_NEWDATA) != 0 && dev->d_iob->io_conn != NULL) + { + pstate->pr_cb->flags = 0; + pstate->pr_cb->priv = NULL; + pstate->pr_cb->event = NULL; + pstate->pr_result = -EAGAIN; + nxsem_post(&pstate->pr_sem); + } + else +#endif + if ((flags & PKT_NEWDATA) != 0) { /* Copy the packet */ @@ -242,9 +209,9 @@ static uint32_t pkt_recvfrom_eventhandler(FAR struct net_driver_s *dev, /* Don't allow any further call backs. */ - pstate->pr_cb->flags = 0; - pstate->pr_cb->priv = NULL; - pstate->pr_cb->event = NULL; + pstate->pr_cb->flags = 0; + pstate->pr_cb->priv = NULL; + pstate->pr_cb->event = NULL; /* Save the sender's address in the caller's 'from' location */ @@ -353,23 +320,56 @@ static ssize_t pkt_recvfrom_result(int result, } /**************************************************************************** - * Name: pkt_readahead + * Name: pkt_readdata * * Description: - * Copy the buffered read-ahead data to the user buffer. + * Copy the buffered data to the user buffer based on the flag errmsg. * * Input Parameters: * pstate The state structure of the recv operation * * Returned Value: - * None + * copy length or -ENODATA * * Assumptions: * The network is locked. * ****************************************************************************/ -static inline void pkt_readahead(FAR struct pkt_recvfrom_s *pstate) +static void append_timestamp(FAR struct pkt_recvfrom_s *pstate, + FAR struct iob_s *iob) +{ +#ifdef CONFIG_NET_TIMESTAMP + FAR struct pkt_conn_s *conn = pstate->pr_conn; + cmsg_store_timestamp(pstate->pr_msg, &iob->io_time, + conn->sconn.s_options); +#endif +} + +#ifdef CONFIG_NET_TIMESTAMP +static void append_timestamping(FAR struct pkt_recvfrom_s *pstate, + FAR struct iob_s *iob) +{ + FAR struct pkt_conn_s *conn = pstate->pr_conn; + struct timespec ts[3]; + + memset(&ts, 0, sizeof(ts)); + + ts[0].tv_sec = iob->io_time.tv_sec; + ts[0].tv_nsec = iob->io_time.tv_nsec; + ts[2].tv_sec = iob->io_time.tv_sec; + ts[2].tv_nsec = iob->io_time.tv_nsec; + + cmsg_append(pstate->pr_msg, SOL_SOCKET, SO_TIMESTAMPING, &ts, + sizeof(ts)); + pstate->pr_msg->msg_flags |= MSG_ERRQUEUE; +} +#endif + +static inline int pkt_readdata(FAR struct pkt_recvfrom_s *pstate, + FAR struct iob_queue_s *iobq, + CODE void (*tsfunc)(FAR struct pkt_recvfrom_s *, + FAR struct iob_s *)) { FAR struct pkt_conn_s *conn = pstate->pr_conn; FAR struct iob_s *iob; @@ -380,28 +380,10 @@ static inline void pkt_readahead(FAR struct pkt_recvfrom_s *pstate) pstate->pr_recvlen = -ENODATA; - if ((iob = iob_peek_queue(&conn->readahead)) != NULL) + if ((iob = iob_remove_queue(iobq)) != NULL) { DEBUGASSERT(iob->io_pktlen > 0); -#ifdef CONFIG_NET_TIMESTAMP - /* Unpack stored timestamp if SO_TIMESTAMP/SO_TIMESTAMPNS socket option - * is enabled - */ - - if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) || - _SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMPNS)) - { - struct timespec ts; - recvlen = iob_copyout((FAR uint8_t *)&ts, iob, - sizeof(struct timespec), - -sizeof(struct timespec)); - DEBUGASSERT(recvlen == sizeof(struct timespec)); - - pkt_store_cmsg_timestamp(pstate, &ts); - } -#endif - /* Copy to user */ if (pstate->pr_type == SOCK_DGRAM) @@ -428,16 +410,14 @@ static inline void pkt_readahead(FAR struct pkt_recvfrom_s *pstate) ninfo("Received %d bytes (of %u)\n", recvlen, iob->io_pktlen); - /* Remove the I/O buffer chain from the head of the read-ahead - * buffer queue. - */ - - iob_remove_queue(&conn->readahead); + tsfunc(pstate, iob); /* And free the I/O buffer chain */ iob_free_chain(iob); } + + return pstate->pr_recvlen; } /**************************************************************************** @@ -520,14 +500,28 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, conn_dev_lock(&conn->sconn, dev); +#ifdef CONFIG_NET_TIMESTAMP + if (flags & MSG_ERRQUEUE) + { + if (!IOB_QEMPTY(&conn->errahead)) + { + ret = pkt_readdata(&state, &conn->errahead, append_timestamping); + } + else + { + ret = -EAGAIN; + } + } + else +#endif + /* Check if there is buffered read-ahead data for this socket. We may have * already received the response to previous command. */ if (!IOB_QEMPTY(&conn->readahead)) { - pkt_readahead(&state); - ret = pkt_recvfrom_result(ret, &state); + ret = pkt_readdata(&state, &conn->readahead, append_timestamp); } else if (_SS_ISNONBLOCK(conn->sconn.s_flags) || (flags & MSG_DONTWAIT) != 0) diff --git a/net/pkt/pkt_sendmsg_buffered.c b/net/pkt/pkt_sendmsg_buffered.c index 5a237e11f74a0..40b46051dbc78 100644 --- a/net/pkt/pkt_sendmsg_buffered.c +++ b/net/pkt/pkt_sendmsg_buffered.c @@ -294,6 +294,13 @@ ssize_t pkt_sendmsg(FAR struct socket *psock, FAR const struct msghdr *msg, iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE); iob_update_pktlen(iob, 0, false); +#ifdef CONFIG_NET_TIMESTAMP + if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMPING)) + { + iob->io_conn = &conn->sconn; + } +#endif + /* Copy the user data into the write buffer. We cannot wait for * buffer space if the socket was opened non-blocking. */ @@ -329,6 +336,7 @@ ssize_t pkt_sendmsg(FAR struct socket *psock, FAR const struct msghdr *msg, { FAR struct eth_hdr_s *ethhdr = (FAR struct eth_hdr_s *)(IOB_DATA(iob) - NET_LL_HDRLEN(dev)); + memcpy(ethhdr->dest, addr->sll_addr, ETHER_ADDR_LEN); memcpy(ethhdr->src, &dev->d_mac.ether, ETHER_ADDR_LEN); ethhdr->type = addr->sll_protocol; diff --git a/net/pkt/pkt_sendmsg_unbuffered.c b/net/pkt/pkt_sendmsg_unbuffered.c index 349440e5d4b92..5e9982c9bbb33 100644 --- a/net/pkt/pkt_sendmsg_unbuffered.c +++ b/net/pkt/pkt_sendmsg_unbuffered.c @@ -131,9 +131,18 @@ static uint32_t psock_send_eventhandler(FAR struct net_driver_s *dev, pstate->snd_sent = pstate->snd_buflen; pstate->snd_conn->pendiob = dev->d_iob; +#ifdef CONFIG_NET_TIMESTAMP + if (_SO_GETOPT(pstate->snd_conn->sconn.s_options, + SO_TIMESTAMPING)) + { + dev->d_iob->io_conn = &pstate->snd_conn->sconn; + } +#endif + if (pstate->snd_sock->s_type == SOCK_DGRAM) { FAR struct eth_hdr_s *ethhdr = NETLLBUF; + memcpy(ethhdr->dest, pstate->addr->sll_addr, ETHER_ADDR_LEN); memcpy(ethhdr->src, &dev->d_mac.ether, ETHER_ADDR_LEN); ethhdr->type = pstate->addr->sll_protocol; diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c index 1efca0a898c7b..1a3b386efdd94 100644 --- a/net/pkt/pkt_sockif.c +++ b/net/pkt/pkt_sockif.c @@ -108,6 +108,7 @@ static int pkt_sockif_alloc(FAR struct socket *psock) */ FAR struct pkt_conn_s *conn = pkt_alloc(); + if (conn == NULL) { /* Failed to reserve a connection structure */ @@ -368,6 +369,10 @@ static int pkt_close(FAR struct socket *psock) iob_free_queue(&conn->readahead); +#ifdef CONFIG_NET_TIMESTAMP + iob_free_queue(&conn->errahead); +#endif + #ifdef CONFIG_NET_PKT_WRITE_BUFFERS /* Free write buffer callback. */ diff --git a/net/socket/Kconfig b/net/socket/Kconfig index eb438e91e87a5..2ceaa2c88fa90 100644 --- a/net/socket/Kconfig +++ b/net/socket/Kconfig @@ -78,12 +78,12 @@ config NET_SOLINGER write buffer support. config NET_TIMESTAMP - bool "SO_TIMESTAMP socket option" + bool "SO_TIMESTAMP/SO_TIMESTAMPING socket option" default n - depends on NET_CAN || NET_ETHERNET ---help--- - Enable or disable support for the SO_TIMESTAMP socket option. - Supported on SocketCAN and Ethernet/UDP. + Enable or disable support for the SO_TIMESTAMP, + SO_TIMESTAMPNS and SO_TIMESTAMPING socket options. + Supported on SocketCAN, Ethernet/UDP and Ethernet/PKT. config NET_BINDTODEVICE bool "SO_BINDTODEVICE socket option Bind-to-device support" diff --git a/net/socket/getsockopt.c b/net/socket/getsockopt.c index 8f258ae1be441..99b64f6f84309 100644 --- a/net/socket/getsockopt.c +++ b/net/socket/getsockopt.c @@ -104,8 +104,8 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, } /* Get the timeout value. This is a atomic operation and should - * require no special operation. - */ + * require no special operation. + */ if (option == SO_RCVTIMEO) { @@ -150,6 +150,7 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, #ifdef CONFIG_NET_TIMESTAMP case SO_TIMESTAMP: /* Generates a timestamp in us for each incoming packet */ case SO_TIMESTAMPNS: /* Generates a timestamp in ns for each incoming packet */ + case SO_TIMESTAMPING:/* Timestamping options */ #endif { sockopt_t optionset; @@ -161,7 +162,7 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, if (*value_len < sizeof(int)) { return -EINVAL; - } + } /* Sample the current options. This is atomic operation and so * should not require any special steps for thread safety. We diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c index a5368be6b922f..7a3de8f0edea0 100644 --- a/net/socket/setsockopt.c +++ b/net/socket/setsockopt.c @@ -140,6 +140,7 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, #ifdef CONFIG_NET_TIMESTAMP case SO_TIMESTAMP: /* Generates a timestamp in us for each incoming packet */ case SO_TIMESTAMPNS: /* Generates a timestamp in ns for each incoming packet */ + case SO_TIMESTAMPING:/* Timestamp all packets */ #endif { int setting; diff --git a/net/udp/udp.h b/net/udp/udp.h index 68850022f6571..b95a2c57eeaad 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -159,9 +159,6 @@ struct udp_conn_s struct udp_poll_s pollinfo[CONFIG_NET_UDP_NPOLLWAITERS]; -#ifdef CONFIG_NET_TIMESTAMP - int timestamp; /* Nonzero when SO_TIMESTAMP is enabled */ -#endif FAR sem_t *txdrain_sem; }; diff --git a/net/udp/udp_callback.c b/net/udp/udp_callback.c index 1eae4b81d10c0..462910cd85f0b 100644 --- a/net/udp/udp_callback.c +++ b/net/udp/udp_callback.c @@ -159,22 +159,6 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev, */ offset = (dev->d_appdata - iob->io_data) - iob->io_offset; - -#ifdef CONFIG_NET_TIMESTAMP - /* Store timestamp while packet is being queued. - * This is done unconditionally to avoid race condition when SO_TIMESTAMP - * gets enabled after packet is received but before it is read. - */ - - offset -= sizeof(struct timespec); - ret = iob_trycopyin(iob, (FAR const uint8_t *)&dev->d_rxtime, - sizeof(struct timespec), offset, true); - if (ret < 0) - { - goto errout; - } -#endif - offset -= src_addr_size; ret = iob_trycopyin(iob, src_addr, src_addr_size, offset, true); if (ret < 0) @@ -275,7 +259,7 @@ net_dataevent(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn, * read-ahead buffers to retain the data -- drop the packet. */ - ninfo("Dropped %d bytes\n", dev->d_len); + ninfo("Dropped %d bytes\n", dev->d_len); #ifdef CONFIG_NET_STATISTICS g_netstats.udp.drop++; diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c index 66a4d8810faf6..120eba315489b 100644 --- a/net/udp/udp_recvfrom.c +++ b/net/udp/udp_recvfrom.c @@ -67,19 +67,6 @@ struct udp_recvfrom_s * Private Functions ****************************************************************************/ -#ifdef CONFIG_NET_TIMESTAMP -static void udp_store_cmsg_timestamp(FAR struct udp_recvfrom_s *pstate, - FAR struct timespec *timestamp) -{ - FAR struct msghdr *msg = pstate->ir_msg; - struct timeval tv; - - TIMESPEC_TO_TIMEVAL(&tv, timestamp); - cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMP, - &tv, sizeof(struct timeval)); -} -#endif - #ifdef CONFIG_NET_SOCKOPTS static void udp_recvpktinfo(FAR struct udp_recvfrom_s *pstate, FAR void *srcaddr, uint8_t ifindex) @@ -220,19 +207,8 @@ static inline void udp_readahead(struct udp_recvfrom_s *pstate) DEBUGASSERT(recvlen == src_addr_size); #ifdef CONFIG_NET_TIMESTAMP - /* Unpack stored timestamp if SO_TIMESTAMP socket option is enabled */ - - if (conn->timestamp) - { - struct timespec timestamp; - recvlen = iob_copyout((FAR uint8_t *)×tamp, iob, - sizeof(struct timespec), offset); - DEBUGASSERT(recvlen == sizeof(struct timespec)); - - udp_store_cmsg_timestamp(pstate, ×tamp); - } - - offset += sizeof(struct timespec); + cmsg_store_timestamp(pstate->ir_msg, &iob->io_time, + conn->sconn.s_options); #endif /* Copy to user */ @@ -470,10 +446,8 @@ static uint32_t udp_eventhandler(FAR struct net_driver_s *dev, /* Save packet timestamp, if requested */ #ifdef CONFIG_NET_TIMESTAMP - if (pstate->ir_conn->timestamp) - { - udp_store_cmsg_timestamp(pstate, &dev->d_rxtime); - } + cmsg_store_timestamp(pstate->ir_msg, &dev->d_iob->io_time, + pstate->ir_conn->sconn.s_options); #endif /* Save the sender's address in the caller's 'from' location */ diff --git a/net/utils/net_cmsg.c b/net/utils/net_cmsg.c index c36c989c3ce19..935e9fb6e2758 100644 --- a/net/utils/net_cmsg.c +++ b/net/utils/net_cmsg.c @@ -28,6 +28,7 @@ #include +#include "socket/socket.h" #include "utils/utils.h" /**************************************************************************** @@ -60,7 +61,7 @@ ****************************************************************************/ FAR void *cmsg_append(FAR struct msghdr *msg, int level, int type, - FAR void *value, int value_len) + FAR const void *value, int value_len) { FAR struct cmsghdr *cmsg; unsigned long cmsgspace = CMSG_SPACE(value_len); @@ -86,3 +87,38 @@ FAR void *cmsg_append(FAR struct msghdr *msg, int level, int type, return cmsgdata; } + +/**************************************************************************** + * Name: cmsg_store_timestamp + * + * Description: + * Store the timestamp in the cmsg + * + * Input Parameters: + * msg - Pointer to the msghdr containing ancillary data (CMSG). + * tstamp - Timestamp information. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_TIMESTAMP +void cmsg_store_timestamp(FAR struct msghdr *msg, + FAR const struct timespec *tstamp, sockopt_t opt) +{ + if (_SO_GETOPT(opt, SO_TIMESTAMP)) + { + struct timeval tv; + + TIMESPEC_TO_TIMEVAL(&tv, tstamp); + cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMP, &tv, + sizeof(struct timeval)); + } + else if (_SO_GETOPT(opt, SO_TIMESTAMPNS)) + { + cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMPNS, tstamp, + sizeof(struct timespec)); + } +} +#endif /* CONFIG_NET_TIMESTAMP */ diff --git a/net/utils/utils.h b/net/utils/utils.h index 03fe8d15d2c17..5b34ae2976c77 100644 --- a/net/utils/utils.h +++ b/net/utils/utils.h @@ -668,7 +668,27 @@ uint16_t icmpv6_chksum(FAR struct net_driver_s *dev, unsigned int iplen); ****************************************************************************/ FAR void *cmsg_append(FAR struct msghdr *msg, int level, int type, - FAR void *value, int value_len); + FAR const void *value, int value_len); + +/**************************************************************************** + * Name: cmsg_store_timestamp + * + * Description: + * Store the timestamp in the cmsg + * + * Input Parameters: + * msg - Pointer to the msghdr containing ancillary data (CMSG). + * tstamp - Timestamp information. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_TIMESTAMP +void cmsg_store_timestamp(FAR struct msghdr *msg, + FAR const struct timespec *tstamp, sockopt_t opt); +#endif /* CONFIG_NET_TIMESTAMP */ #undef EXTERN #ifdef __cplusplus