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

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 81f42cb2cfc475f6ba07e2b35ed3362b62cff8d9
Author: gaohedong <[email protected]>
AuthorDate: Sun Feb 16 17:35:04 2025 +0800

    net/ethernet: add timestamp for socket packet
    
    add timestamp for socket packet
    
    Signed-off-by: gaohedong <[email protected]>
---
 include/sys/socket.h    |   5 +-
 net/pkt/pkt_input.c     |  27 +++++++++
 net/pkt/pkt_recvmsg.c   | 144 +++++++++++++++++++++++++++++++++---------------
 net/socket/getsockopt.c |  17 +++---
 net/socket/setsockopt.c |  17 +++---
 net/socket/socket.h     |   1 +
 6 files changed, 151 insertions(+), 60 deletions(-)

diff --git a/include/sys/socket.h b/include/sys/socket.h
index bd477422280..ead113a62ef 100644
--- a/include/sys/socket.h
+++ b/include/sys/socket.h
@@ -212,7 +212,7 @@
 #define SO_TYPE         15 /* Reports the socket type (get only).
                             * return: int
                             */
-#define SO_TIMESTAMP    16 /* Generates a timestamp for each incoming packet
+#define SO_TIMESTAMP    16 /* Generates a timestamp in us for each incoming 
packet
                             * arg: integer value
                             */
 #define SO_BINDTODEVICE 17 /* Bind this socket to a specific network device.
@@ -220,6 +220,9 @@
 #define SO_PEERCRED     18 /* Return the credentials of the peer process
                             * connected to this socket.
                             */
+#define SO_TIMESTAMPNS  20 /* Generates a timestamp in ns for each incoming 
packet
+                            * arg: integer value
+                            */
 
 /* The options are unsupported but included for compatibility
  * and portability
diff --git a/net/pkt/pkt_input.c b/net/pkt/pkt_input.c
index 321d9eb8f5d..230aaf03695 100644
--- a/net/pkt/pkt_input.c
+++ b/net/pkt/pkt_input.c
@@ -36,6 +36,7 @@
 
 #include "devif/devif.h"
 #include "pkt/pkt.h"
+#include "socket/socket.h"
 
 /****************************************************************************
  * Private Functions
@@ -68,6 +69,22 @@ 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.
@@ -139,6 +156,16 @@ static int pkt_in(FAR struct net_driver_s *dev)
     {
       uint16_t flags;
 
+#if defined(CONFIG_NET_TIMESTAMP) && 
!defined(CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP)
+      /* Get system as timestamp if no hardware timestamp */
+
+      if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) ||
+          _SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMPNS))
+        {
+          clock_gettime(CLOCK_REALTIME, &dev->d_rxtime);
+        }
+#endif /* CONFIG_NET_TIMESTAMP */
+
       /* Setup for the application callback */
 
       dev->d_appdata = dev->d_buf;
diff --git a/net/pkt/pkt_recvmsg.c b/net/pkt/pkt_recvmsg.c
index 79919516af8..08b93092de9 100644
--- a/net/pkt/pkt_recvmsg.c
+++ b/net/pkt/pkt_recvmsg.c
@@ -30,6 +30,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
@@ -54,18 +55,54 @@
 
 struct pkt_recvfrom_s
 {
-  FAR struct devif_callback_s *pr_cb;  /* Reference to callback instance */
-  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 */
-  int          pr_result;              /* Success:OK, failure:negated errno */
+  FAR struct pkt_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;     /* Receive info and buffer */
+  sem_t                        pr_sem;     /* Semaphore signals recv 
completion */
+  ssize_t                      pr_recvlen; /* The received length */
+  int                          pr_result;  /* Success:OK, failure:negated 
errno */
 };
 
 /****************************************************************************
  * 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
  *
@@ -92,8 +129,6 @@ static inline void pkt_add_recvlen(FAR struct pkt_recvfrom_s 
*pstate,
     }
 
   pstate->pr_recvlen += recvlen;
-  pstate->pr_buffer  += recvlen;
-  pstate->pr_buflen  -= recvlen;
 }
 
 /****************************************************************************
@@ -120,20 +155,24 @@ static void pkt_recvfrom_newdata(FAR struct net_driver_s 
*dev,
   unsigned int offset;
   size_t recvlen;
 
-  if (dev->d_len > pstate->pr_buflen)
-    {
-      recvlen = pstate->pr_buflen;
-    }
-  else
+#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))
     {
-      recvlen = dev->d_len;
+      pkt_store_cmsg_timestamp(pstate, &dev->d_rxtime);
     }
+#endif
+
+  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);
 
   ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len);
 
@@ -228,9 +267,8 @@ static uint16_t pkt_recvfrom_eventhandler(FAR struct 
net_driver_s *dev,
  *   Initialize the state structure
  *
  * Input Parameters:
- *   psock    Pointer to the socket structure for the socket
- *   buf      Buffer to receive data
- *   len      Length of buffer
+ *   conn     The PKT connection of interest
+ *   msg      Receive info and buffer for receive data
  *   pstate   A pointer to the state structure to be initialized
  *
  * Returned Value:
@@ -240,9 +278,8 @@ static uint16_t pkt_recvfrom_eventhandler(FAR struct 
net_driver_s *dev,
  *
  ****************************************************************************/
 
-static void pkt_recvfrom_initialize(FAR struct socket *psock, FAR void *buf,
-                                    size_t len, FAR struct sockaddr *infrom,
-                                    FAR socklen_t *fromlen,
+static void pkt_recvfrom_initialize(FAR struct pkt_conn_s *conn,
+                                    FAR struct msghdr *msg,
                                     FAR struct pkt_recvfrom_s *pstate)
 {
   /* Initialize the state structure. */
@@ -250,8 +287,8 @@ static void pkt_recvfrom_initialize(FAR struct socket 
*psock, FAR void *buf,
   memset(pstate, 0, sizeof(struct pkt_recvfrom_s));
   nxsem_init(&pstate->pr_sem, 0, 0); /* Doesn't really fail */
 
-  pstate->pr_buflen = len;
-  pstate->pr_buffer = buf;
+  pstate->pr_conn = conn;
+  pstate->pr_msg  = msg;
 }
 
 /* The only un-initialization that has to be performed is destroying the
@@ -313,35 +350,58 @@ static ssize_t pkt_recvfrom_result(int result,
  *   Copy the buffered read-ahead data to the user buffer.
  *
  * Input Parameters:
- *   conn  -  PKT socket connection structure containing the read-
- *            ahead data.
- *   buf      target buffer.
+ *   pstate       The state structure of the recv operation
  *
  * Returned Value:
- *   Number of bytes copied to the user buffer
+ *   None
  *
  * Assumptions:
  *   The network is locked.
  *
  ****************************************************************************/
 
-static inline ssize_t pkt_readahead(FAR struct pkt_conn_s *conn,
-                                    FAR void *buf, size_t buflen)
+static inline void pkt_readahead(FAR struct pkt_recvfrom_s *pstate)
 {
+  FAR struct pkt_conn_s *conn = pstate->pr_conn;
   FAR struct iob_s *iob;
-  ssize_t ret = -ENODATA;
+  int recvlen;
 
   /* Check there is any packets already buffered in a read-ahead buffer. */
 
+  pstate->pr_recvlen = -ENODATA;
+
   if ((iob = iob_peek_queue(&conn->readahead)) != 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 */
 
-      ret = iob_copyout(buf, iob, buflen, 0);
+      recvlen = iob_copyout(pstate->pr_msg->msg_iov->iov_base, iob,
+                            pstate->pr_msg->msg_iov->iov_len, 0);
 
-      ninfo("Received %zd bytes (of %u)\n", ret, iob->io_pktlen);
+      /* Update the accumulated size of the data read */
+
+      pstate->pr_recvlen = recvlen;
+
+      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.
@@ -353,8 +413,6 @@ static inline ssize_t pkt_readahead(FAR struct pkt_conn_s 
*conn,
 
       iob_free_chain(iob);
     }
-
-  return ret;
 }
 
 /****************************************************************************
@@ -392,14 +450,12 @@ static inline ssize_t pkt_readahead(FAR struct pkt_conn_s 
*conn,
 ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
                     int flags)
 {
-  FAR void *buf = msg->msg_iov->iov_base;
-  size_t len = msg->msg_iov->iov_len;
   FAR struct sockaddr *from = msg->msg_name;
   FAR socklen_t *fromlen = &msg->msg_namelen;
   FAR struct pkt_conn_s *conn = psock->s_conn;
   FAR struct net_driver_s *dev;
   struct pkt_recvfrom_s state;
-  ssize_t ret;
+  ssize_t ret = 0;
 
   /* If a 'from' address has been provided, verify that it is large
    * enough to hold this address family.
@@ -427,6 +483,8 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct 
msghdr *msg,
    * locked because we don't want anything to happen until we are ready.
    */
 
+  pkt_recvfrom_initialize(conn, msg, &state);
+
   net_lock();
 
   /* Check if there is buffered read-ahead data for this socket.  We may have
@@ -435,7 +493,8 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct 
msghdr *msg,
 
   if (!IOB_QEMPTY(&conn->readahead))
     {
-      ret = pkt_readahead(conn, buf, len);
+      pkt_readahead(&state);
+      ret = pkt_recvfrom_result(ret, &state);
     }
   else if (_SS_ISNONBLOCK(conn->sconn.s_flags) ||
            (flags & MSG_DONTWAIT) != 0)
@@ -446,8 +505,6 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct 
msghdr *msg,
     }
   else
     {
-      pkt_recvfrom_initialize(psock, buf, len, from, fromlen, &state);
-
       /* Get the device driver that will service this transfer */
 
       dev  = pkt_find_device(conn);
@@ -496,12 +553,13 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct 
msghdr *msg,
         {
           ret = -EBUSY;
         }
-
-errout_with_state:
-      pkt_recvfrom_uninitialize(&state);
     }
 
   net_unlock();
+
+errout_with_state:
+  pkt_recvfrom_uninitialize(&state);
+
   return ret;
 }
 
diff --git a/net/socket/getsockopt.c b/net/socket/getsockopt.c
index 2f01f39e485..d4acc4becbd 100644
--- a/net/socket/getsockopt.c
+++ b/net/socket/getsockopt.c
@@ -140,15 +140,16 @@ static int psock_socketlevel_option(FAR struct socket 
*psock, int option,
        * is outside of the scope of getsockopt.
        */
 
-      case SO_BROADCAST:  /* Permits sending of broadcast messages */
-      case SO_DEBUG:      /* Enables recording of debugging information */
-      case SO_DONTROUTE:  /* Requests outgoing messages bypass standard 
routing */
-      case SO_KEEPALIVE:  /* Verifies TCP connections active by enabling the
-                           * periodic transmission of probes */
-      case SO_OOBINLINE:  /* Leaves received out-of-band data inline */
-      case SO_REUSEADDR:  /* Allow reuse of local addresses */
+      case SO_BROADCAST:   /* Permits sending of broadcast messages */
+      case SO_DEBUG:       /* Enables recording of debugging information */
+      case SO_DONTROUTE:   /* Requests outgoing messages bypass standard 
routing */
+      case SO_KEEPALIVE:   /* Verifies TCP connections active by enabling the
+                            * periodic transmission of probes */
+      case SO_OOBINLINE:   /* Leaves received out-of-band data inline */
+      case SO_REUSEADDR:   /* Allow reuse of local addresses */
 #ifdef CONFIG_NET_TIMESTAMP
-      case SO_TIMESTAMP:  /* Generates a timestamp for each incoming packet */
+      case SO_TIMESTAMP:   /* Generates a timestamp in us for each incoming 
packet */
+      case SO_TIMESTAMPNS: /* Generates a timestamp in ns for each incoming 
packet */
 #endif
         {
           sockopt_t optionset;
diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c
index 5e3a59157a8..97b039a6ede 100644
--- a/net/socket/setsockopt.c
+++ b/net/socket/setsockopt.c
@@ -130,15 +130,16 @@ static int psock_socketlevel_option(FAR struct socket 
*psock, int option,
           return OK;
         }
 
-      case SO_BROADCAST:  /* Permits sending of broadcast messages */
-      case SO_DEBUG:      /* Enables recording of debugging information */
-      case SO_DONTROUTE:  /* Requests outgoing messages bypass standard 
routing */
-      case SO_KEEPALIVE:  /* Verifies TCP connections active by enabling the
-                           * periodic transmission of probes */
-      case SO_OOBINLINE:  /* Leaves received out-of-band data inline */
-      case SO_REUSEADDR:  /* Allow reuse of local addresses */
+      case SO_BROADCAST:   /* Permits sending of broadcast messages */
+      case SO_DEBUG:       /* Enables recording of debugging information */
+      case SO_DONTROUTE:   /* Requests outgoing messages bypass standard 
routing */
+      case SO_KEEPALIVE:   /* Verifies TCP connections active by enabling the
+                            * periodic transmission of probes */
+      case SO_OOBINLINE:   /* Leaves received out-of-band data inline */
+      case SO_REUSEADDR:   /* Allow reuse of local addresses */
 #ifdef CONFIG_NET_TIMESTAMP
-      case SO_TIMESTAMP:  /* Generates a timestamp for each incoming packet */
+      case SO_TIMESTAMP:   /* Generates a timestamp in us for each incoming 
packet */
+      case SO_TIMESTAMPNS: /* Generates a timestamp in ns for each incoming 
packet */
 #endif
         {
           int setting;
diff --git a/net/socket/socket.h b/net/socket/socket.h
index d05c66c83be..75362f06609 100644
--- a/net/socket/socket.h
+++ b/net/socket/socket.h
@@ -65,6 +65,7 @@
 #define _SO_SNDTIMEO     _SO_BIT(SO_SNDTIMEO)
 #define _SO_TYPE         _SO_BIT(SO_TYPE)
 #define _SO_TIMESTAMP    _SO_BIT(SO_TIMESTAMP)
+#define _SO_TIMESTAMPNS  _SO_BIT(SO_TIMESTAMPNS)
 #define _SO_BINDTODEVICE _SO_BIT(SO_BINDTODEVICE)
 
 /* This is the largest option value.  REVISIT: belongs in sys/socket.h */

Reply via email to