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 e2525f61b1e7635e4d660c5624ccf2e3f849ea56
Author: zhanghongyu <[email protected]>
AuthorDate: Thu Apr 24 16:58:35 2025 +0800

    net/pkt: add nonblock mode and send buffered support
    
    Supports more flexible configuration of sending methods
    
    Signed-off-by: zhanghongyu <[email protected]>
---
 net/pkt/CMakeLists.txt                             |  37 +-
 net/pkt/Kconfig                                    |  12 +
 net/pkt/Make.defs                                  |   9 +-
 net/pkt/pkt.h                                      |  82 ++++
 net/pkt/pkt_conn.c                                 |   6 +
 net/pkt/pkt_getsockopt.c                           | 125 +++++++
 net/pkt/pkt_netpoll.c                              |   9 +
 net/pkt/pkt_sendmsg_buffered.c                     | 412 +++++++++++++++++++++
 .../{pkt_sendmsg.c => pkt_sendmsg_unbuffered.c}    |   2 +-
 net/pkt/pkt_setsockopt.c                           | 133 +++++++
 net/pkt/pkt_sockif.c                               |  40 +-
 net/socket/Kconfig                                 |   6 +
 12 files changed, 857 insertions(+), 16 deletions(-)

diff --git a/net/pkt/CMakeLists.txt b/net/pkt/CMakeLists.txt
index da3fac7dbe7..1481c6d76d3 100644
--- a/net/pkt/CMakeLists.txt
+++ b/net/pkt/CMakeLists.txt
@@ -23,17 +23,28 @@
 # Packet socket support
 
 if(CONFIG_NET_PKT)
-  target_sources(
-    net
-    PRIVATE # Socket layer
-            pkt_sockif.c
-            pkt_sendmsg.c
-            pkt_recvmsg.c
-            # Transport layer
-            pkt_conn.c
-            pkt_input.c
-            pkt_callback.c
-            pkt_poll.c
-            pkt_netpoll.c
-            pkt_finddev.c)
+
+  set(SRCS # Socket layer
+      pkt_sockif.c
+      pkt_recvmsg.c
+      # Transport layer
+      pkt_conn.c
+      pkt_input.c
+      pkt_callback.c
+      pkt_poll.c
+      pkt_netpoll.c
+      pkt_finddev.c)
+
+  if(CONFIG_NET_PKT_WRITE_BUFFERS)
+    list(APPEND SRCS pkt_sendmsg_buffered.c) # Socket layer
+  else()
+    list(APPEND SRCS pkt_sendmsg_unbuffered.c) # Socket layer
+
+  endif()
+
+  if(CONFIG_NET_PKTPROTO_OPTIONS)
+    list(APPEND SRCS pkt_setsockopt.c pkt_getsockopt.c) # Socket layer
+  endif()
+
+  target_sources(net PRIVATE ${SRCS})
 endif()
diff --git a/net/pkt/Kconfig b/net/pkt/Kconfig
index 9e012c7bc12..acecd593f29 100644
--- a/net/pkt/Kconfig
+++ b/net/pkt/Kconfig
@@ -20,6 +20,18 @@ config NET_PKT
 
 if NET_PKT
 
+config NET_PKT_WRITE_BUFFERS
+       bool  "Enable PKT write buffering"
+       default n
+       select NET_WRITE_BUFFERS
+       select NET_PKTPROTO_OPTIONS
+       ---help---
+               Write buffers allows buffering of ongoing PKT packets, providing
+               for higher performance, nonblocking output.
+
+               You might want to disable PKT write buffering on a highly memory
+               memory constrained system where there are no performance issues.
+
 config NET_PKT_PREALLOC_CONNS
        int "Preallocated packet sockets"
        default 1
diff --git a/net/pkt/Make.defs b/net/pkt/Make.defs
index 6d846c2489b..f9d9c215d91 100644
--- a/net/pkt/Make.defs
+++ b/net/pkt/Make.defs
@@ -27,8 +27,15 @@ ifeq ($(CONFIG_NET_PKT),y)
 # Socket layer
 
 SOCK_CSRCS += pkt_sockif.c
-SOCK_CSRCS += pkt_sendmsg.c
+ifeq ($(CONFIG_NET_PKT_WRITE_BUFFERS),y)
+SOCK_CSRCS += pkt_sendmsg_buffered.c
+else
+SOCK_CSRCS += pkt_sendmsg_unbuffered.c
+endif
 SOCK_CSRCS += pkt_recvmsg.c
+ifeq ($(CONFIG_NET_PKTPROTO_OPTIONS),y)
+SOCK_CSRCS += pkt_setsockopt.c pkt_getsockopt.c
+endif
 
 # Transport layer
 
diff --git a/net/pkt/pkt.h b/net/pkt/pkt.h
index ab866c21336..999358b067b 100644
--- a/net/pkt/pkt.h
+++ b/net/pkt/pkt.h
@@ -76,6 +76,25 @@ struct pkt_conn_s
   uint8_t    crefs;    /* Reference counts on this instance */
   uint16_t   type;     /* The Ethernet type of the packet */
 
+#ifdef CONFIG_NET_PKT_WRITE_BUFFERS
+  /* Write buffering
+   *
+   *   write_q   - The queue of unsent I/O buffers.  The head of this
+   *               list may be partially sent.  FIFO ordering.
+   */
+
+  struct iob_queue_s write_q;     /* Write buffering for pkt messages */
+
+  /* Callback instance for pkt send */
+
+  FAR struct devif_callback_s *sndcb;
+#  if CONFIG_NET_SEND_BUFSIZE > 0
+  int32_t  sndbufs;               /* Maximum amount of bytes queued in send */
+  sem_t    sndsem;                /* Semaphore signals send completion */
+#  endif
+
+#endif
+
   /* Read-ahead buffering.
    *
    *   readahead - A singly linked list of type struct iob_qentry_s
@@ -347,6 +366,69 @@ int pkt_pollteardown(FAR struct socket *psock, FAR struct 
pollfd *fds);
 ssize_t pkt_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
                     int flags);
 
+#ifdef CONFIG_NET_PKTPROTO_OPTIONS
+/****************************************************************************
+ * Name: pkt_getsockopt
+ *
+ * Description:
+ *   pkt_getsockopt() retrieves the value for the option specified by the
+ *   'option' argument for the socket specified by the 'psock' argument.  If
+ *   the size of the option value is greater than 'value_len', the value
+ *   stored in the object pointed to by the 'value' argument will be silently
+ *   truncated. Otherwise, the length pointed to by the 'value_len' argument
+ *   will be modified to indicate the actual length of the 'value'.
+ *
+ *   See <sys/socket.h> a complete list of values for the socket-level
+ *   'option' argument.  Protocol-specific options are are protocol specific
+ *   header files (such as nuttx/pkt.h for the case of the PKT protocol).
+ *
+ * Input Parameters:
+ *   psock     Socket structure of the socket to query
+ *   level     Protocol level to set the option
+ *   option    identifies the option to get
+ *   value     Points to the argument value
+ *   value_len The length of the argument value
+ *
+ * Returned Value:
+ *   Returns zero (OK) on success.  On failure, it returns a negated errno
+ *   value to indicate the nature of the error.  See psock_getsockopt() for
+ *   the complete list of appropriate return error codes.
+ *
+ ****************************************************************************/
+
+int pkt_getsockopt(FAR struct socket *psock, int level, int option,
+                   FAR void *value, FAR socklen_t *value_len);
+
+/****************************************************************************
+ * Name: pkt_setsockopt
+ *
+ * Description:
+ *   pkt_setsockopt() sets the PKT-protocol option specified by the
+ *   'option' argument to the value pointed to by the 'value' argument for
+ *   the socket specified by the 'psock' argument.
+ *
+ *   See <nuttx/pkt.h> for the a complete list of values of PKT protocol
+ *   options.
+ *
+ * Input Parameters:
+ *   psock     Socket structure of socket to operate on
+ *   level     Protocol level to set the option
+ *   option    identifies the option to set
+ *   value     Points to the argument value
+ *   value_len The length of the argument value
+ *
+ * Returned Value:
+ *   Returns zero (OK) on success.  On failure, it returns a negated errno
+ *   value to indicate the nature of the error.  See psock_setcockopt() for
+ *   the list of possible error values.
+ *
+ ****************************************************************************/
+
+int pkt_setsockopt(FAR struct socket *psock, int level, int option,
+                   FAR const void *value, socklen_t value_len);
+
+#endif
+
 #undef EXTERN
 #ifdef __cplusplus
 }
diff --git a/net/pkt/pkt_conn.c b/net/pkt/pkt_conn.c
index 9387cd961f5..168080cfdc1 100644
--- a/net/pkt/pkt_conn.c
+++ b/net/pkt/pkt_conn.c
@@ -139,6 +139,12 @@ void pkt_free(FAR struct pkt_conn_s *conn)
 
   dq_rem(&conn->sconn.node, &g_active_pkt_connections);
 
+#ifdef CONFIG_NET_CAN_WRITE_BUFFERS
+  /* Free the write queue */
+
+  iob_free_queue(&conn->write_q);
+#endif
+
   /* Free the connection. */
 
   NET_BUFPOOL_FREE(g_pkt_connections, conn);
diff --git a/net/pkt/pkt_getsockopt.c b/net/pkt/pkt_getsockopt.c
new file mode 100644
index 00000000000..3ca9265adff
--- /dev/null
+++ b/net/pkt/pkt_getsockopt.c
@@ -0,0 +1,125 @@
+/****************************************************************************
+ * net/pkt/pkt_getsockopt.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/time.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/net/net.h>
+#include <nuttx/net/pkt.h>
+
+#include "socket/socket.h"
+#include "utils/utils.h"
+#include "pkt/pkt.h"
+
+#ifdef CONFIG_NET_PKTPROTO_OPTIONS
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pkt_getsockopt
+ *
+ * Description:
+ *   pkt_getsockopt() retrieves the value for the option specified by the
+ *   'option' argument for the socket specified by the 'psock' argument.  If
+ *   the size of the option value is greater than 'value_len', the value
+ *   stored in the object pointed to by the 'value' argument will be silently
+ *   truncated. Otherwise, the length pointed to by the 'value_len' argument
+ *   will be modified to indicate the actual length of the 'value'.
+ *
+ *   See <sys/socket.h> a complete list of values for the socket-level
+ *   'option' argument.  Protocol-specific options are are protocol specific
+ *   header files (such as nuttx/pkt.h for the case of the PKT protocol).
+ *
+ * Input Parameters:
+ *   psock     Socket structure of the socket to query
+ *   level     Protocol level to set the option
+ *   option    identifies the option to get
+ *   value     Points to the argument value
+ *   value_len The length of the argument value
+ *
+ * Returned Value:
+ *   Returns zero (OK) on success.  On failure, it returns a negated errno
+ *   value to indicate the nature of the error.  See psock_getsockopt() for
+ *   the complete list of appropriate return error codes.
+ *
+ ****************************************************************************/
+
+int pkt_getsockopt(FAR struct socket *psock, int level, int option,
+                   FAR void *value, FAR socklen_t *value_len)
+{
+  int ret = OK;
+
+  DEBUGASSERT(value != NULL && value_len != NULL);
+
+  if (level != SOL_PACKET)
+    {
+      return -ENOPROTOOPT;
+    }
+
+  if (psock->s_type != SOCK_RAW)
+    {
+      nerr("ERROR:  Not a RAW PKT socket\n");
+      return -ENOTCONN;
+    }
+
+  switch (option)
+    {
+#if CONFIG_NET_SEND_BUFSIZE > 0
+      case SO_SNDBUF:
+        {
+          FAR struct pkt_conn_s *conn;
+          conn = psock->s_conn;
+          /* Verify that option is the size of an 'int'.  Should also check
+           * that 'value' is properly aligned for an 'int'
+           */
+
+          if (*value_len < sizeof(int))
+            {
+              return -EINVAL;
+            }
+
+            *(FAR int *)value = conn->sndbufs;
+            break;
+          }
+#endif
+
+      default:
+        nerr("ERROR: Unrecognized RAW PKT socket option: %d\n", option);
+        ret = -ENOPROTOOPT;
+        break;
+    }
+
+  return ret;
+}
+
+#endif /* CONFIG_NET_PKTPROTO_OPTIONS */
diff --git a/net/pkt/pkt_netpoll.c b/net/pkt/pkt_netpoll.c
index 114a40184c1..652848acea4 100644
--- a/net/pkt/pkt_netpoll.c
+++ b/net/pkt/pkt_netpoll.c
@@ -65,6 +65,15 @@
 
 static int psock_pkt_cansend(FAR struct pkt_conn_s *conn)
 {
+  if (iob_navail(false) <= 0
+#if defined(CONFIG_NET_PKT_WRITE_BUFFERS) && CONFIG_NET_SEND_BUFSIZE > 0
+      || iob_get_queue_size(&conn->write_q) >= conn->sndbufs
+#endif
+     )
+    {
+      return -EWOULDBLOCK;
+    }
+
   return OK;
 }
 
diff --git a/net/pkt/pkt_sendmsg_buffered.c b/net/pkt/pkt_sendmsg_buffered.c
new file mode 100644
index 00000000000..4a84d6feee3
--- /dev/null
+++ b/net/pkt/pkt_sendmsg_buffered.c
@@ -0,0 +1,412 @@
+/****************************************************************************
+ * net/pkt/pkt_sendmsg_buffered.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/irq.h>
+
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/net.h>
+#include <nuttx/net/ip.h>
+
+#include "devif/devif.h"
+#include "netdev/netdev.h"
+#include "socket/socket.h"
+#include "utils/utils.h"
+#include "pkt/pkt.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pkt_sendbuffer_notify
+ *
+ * Description:
+ *   Notify the send buffer semaphore
+ *
+ * Input Parameters:
+ *   conn - The PKT connection of interest
+ *
+ * Assumptions:
+ *   Called from user logic with the network locked.
+ *
+ ****************************************************************************/
+
+#if CONFIG_NET_SEND_BUFSIZE > 0
+static void pkt_sendbuffer_notify(FAR struct pkt_conn_s *conn)
+{
+  int val = 0;
+
+  nxsem_get_value(&conn->sndsem, &val);
+  if (val < 0)
+    {
+      nxsem_post(&conn->sndsem);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: psock_send_eventhandler
+ ****************************************************************************/
+
+static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
+                                        FAR void *pvpriv, uint16_t flags)
+{
+  FAR struct pkt_conn_s *conn = pvpriv;
+
+  DEBUGASSERT(dev != NULL && conn != NULL);
+
+  /* Check if the outgoing packet is available. It may have been claimed
+   * by a send event handler serving a different thread -OR- if the
+   * output buffer currently contains unprocessed incoming data. In
+   * these cases we will just have to wait for the next polling cycle.
+   */
+
+  if (dev->d_sndlen > 0 || (flags & PKT_NEWDATA) != 0)
+    {
+      /* Another thread has beat us sending data or the buffer is busy,
+       * Check for a timeout. If not timed out, wait for the next
+       * polling cycle and check again.
+       */
+
+      /* No timeout. Just wait for the next polling cycle */
+
+      return flags;
+    }
+
+  /* It looks like we are good to send the data */
+
+  else
+    {
+      uint32_t write_q_len;
+      FAR struct iob_s *iob;
+
+      /* Copy the packet data into the device packet buffer and send it */
+
+      write_q_len = iob_get_queue_entry_count(&conn->write_q);
+
+      if (write_q_len == 0)
+        {
+          return flags;
+        }
+
+      /* Peek at the head of the write queue (but don't remove anything
+       * from the write queue yet).  We know from the above test that
+       * the write_q is not empty.
+       */
+
+      iob = iob_remove_queue(&conn->write_q);
+      DEBUGASSERT(iob != NULL);
+
+      /* Then set-up to send that amount of data with the offset
+       * corresponding to the size of the IP-dependent address structure.
+       */
+
+      netdev_iob_replace(dev, iob);
+      conn->pendiob = iob;
+
+      /* Get the amount of data that we can send in the next packet.
+       * We will send either the remaining data in the buffer I/O
+       * buffer chain, or as much as will fit given the MSS and current
+       * window size.
+       */
+
+      dev->d_sndlen = iob->io_pktlen;
+      ninfo("wrb=%p sndlen=%d\n", iob, dev->d_sndlen);
+
+      if (write_q_len > 1)
+        {
+          /* Set up for the next packet transfer
+           * next packet now at the header of the write buffer queue.
+           */
+
+          /* Notify the device driver that new TX data is available. */
+
+          netdev_txnotify_dev(dev);
+        }
+      else
+        {
+          /* stifle any further callbacks until more write data is
+           * enqueued.
+           */
+
+          conn->sndcb->flags = 0;
+          conn->sndcb->priv = NULL;
+          conn->sndcb->event = NULL;
+        }
+
+      /* Only one data can be sent by low level driver at once,
+       * tell the caller stop polling the other connections.
+       */
+
+      flags &= ~PKT_POLL;
+
+#if CONFIG_NET_SEND_BUFSIZE > 0
+      pkt_sendbuffer_notify(conn);
+#endif
+    }
+
+  return flags;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pkt_sendmsg
+ *
+ * Description:
+ *   The pkt_sendmsg() call may be used only when the packet socket is in
+ *   a connected state (so that the intended recipient is known).
+ *
+ * Input Parameters:
+ *   psock    An instance of the internal socket structure.
+ *   msg      Message to send
+ *   flags    Send flags
+ *
+ * Returned Value:
+ *   On success, returns the number of characters sent. On error, a negated
+ *   errno value is returned (see sendmsg() for the complete list of return
+ *   values.
+ *
+ ****************************************************************************/
+
+ssize_t pkt_sendmsg(FAR struct socket *psock, FAR const struct msghdr *msg,
+                    int flags)
+{
+  FAR const void *buf = msg->msg_iov->iov_base;
+  size_t len = msg->msg_iov->iov_len;
+  FAR struct net_driver_s *dev;
+  FAR struct pkt_conn_s *conn;
+  FAR struct iob_s *iob;
+  bool nonblock;
+  int ret = OK;
+
+  /* Validity check, only single iov supported */
+
+  if (msg->msg_iovlen != 1)
+    {
+      return -ENOTSUP;
+    }
+
+  if (msg->msg_name != NULL)
+    {
+      /* pkt_sendto */
+
+      nerr("ERROR: sendto() not supported for raw packet sockets\n");
+      return -EAFNOSUPPORT;
+    }
+
+  /* Verify that the sockfd corresponds to valid, allocated socket */
+
+  if (psock == NULL || psock->s_conn == NULL)
+    {
+      return -EBADF;
+    }
+
+  /* Only SOCK_RAW is supported */
+
+  if (psock->s_type != SOCK_RAW)
+    {
+      /* EDESTADDRREQ.  Signifies that the socket is not connection-mode and
+       * no peer address is set.
+       */
+
+      return -EDESTADDRREQ;
+    }
+
+  if (len <= 0)
+    {
+      return 0;
+    }
+
+  net_lock();
+
+  /* Get the device driver that will service this transfer */
+
+  dev = pkt_find_device(psock->s_conn);
+  if (dev == NULL)
+    {
+      ret = -ENODEV;
+      goto errout_with_lock;
+    }
+
+  conn = psock->s_conn;
+  nonblock = _SS_ISNONBLOCK(conn->sconn.s_flags) ||
+             (flags & MSG_DONTWAIT) != 0;
+
+#if CONFIG_NET_SEND_BUFSIZE > 0
+  if ((iob_get_queue_size(&conn->write_q) + len) >= conn->sndbufs)
+    {
+      /* send buffer size exceeds the send limit */
+
+      if (nonblock)
+        {
+          nerr("ERROR: Buffer overflow\n");
+          ret = -EAGAIN;
+          goto errout_with_lock;
+        }
+
+      ret = net_sem_timedwait_uninterruptible(&conn->sndsem,
+        _SO_TIMEOUT(conn->sconn.s_sndtimeo));
+      if (ret < 0)
+        {
+          goto errout_with_lock;
+        }
+    }
+#endif
+
+  if (nonblock)
+    {
+      iob = iob_tryalloc(false);
+    }
+  else
+    {
+      iob = net_iobtimedalloc(true, _SO_TIMEOUT(conn->sconn.s_sndtimeo));
+    }
+
+  if (iob == NULL)
+    {
+      /* A buffer allocation error occurred */
+
+      nerr("ERROR: Failed to allocate write buffer\n");
+
+      if (nonblock)
+        {
+          ret = -EAGAIN;
+        }
+      else
+        {
+          ret = -ENOMEM;
+        }
+
+      goto errout_with_lock;
+    }
+
+  iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE);
+  iob_update_pktlen(iob, 0, false);
+
+  /* Copy the user data into the write buffer.  We cannot wait for
+   * buffer space if the socket was opened non-blocking.
+   */
+
+  if (nonblock)
+    {
+      ret = iob_trycopyin(iob, buf, len, -NET_LL_HDRLEN(dev), false);
+    }
+  else
+    {
+      unsigned int count;
+      int blresult;
+
+      /* iob_copyin might wait for buffers to be freed, but if
+       * network is locked this might never happen, since network
+       * driver is also locked, therefore we need to break the lock
+       */
+
+      blresult = net_breaklock(&count);
+      ret = iob_copyin(iob, buf, len, -NET_LL_HDRLEN(dev), false);
+      if (blresult >= 0)
+        {
+          net_restorelock(count);
+        }
+    }
+
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to copy data into write buffer\n");
+      goto errout_with_iob;
+    }
+
+  if (nonblock)
+    {
+      ret = iob_tryadd_queue(iob, &conn->write_q);
+    }
+  else
+    {
+      ret = iob_add_queue(iob, &conn->write_q);
+    }
+
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to add buffer to w_queue\n");
+      goto errout_with_iob;
+    }
+
+  /* Allocate resource to receive a callback */
+
+  if (conn->sndcb == NULL)
+    {
+      conn->sndcb = pkt_callback_alloc(dev, conn);
+    }
+
+  /* Test if the callback has been allocated */
+
+  if (conn->sndcb == NULL)
+    {
+      /* A buffer allocation error occurred */
+
+      nerr("ERROR: Failed to allocate callback\n");
+      ret = -ENOMEM;
+      goto errout_with_iob;
+    }
+  else
+    {
+      /* Set up the callback in the connection */
+
+      conn->sndcb->flags = PKT_POLL;
+      conn->sndcb->priv  = conn;
+      conn->sndcb->event = psock_send_eventhandler;
+
+      /* Notify the device driver that new TX data is available. */
+
+      netdev_txnotify_dev(dev);
+    }
+
+  net_unlock();
+
+  return len;
+
+errout_with_iob:
+  iob_free_chain(iob);
+
+errout_with_lock:
+  net_unlock();
+
+  return ret;
+}
diff --git a/net/pkt/pkt_sendmsg.c b/net/pkt/pkt_sendmsg_unbuffered.c
similarity index 99%
rename from net/pkt/pkt_sendmsg.c
rename to net/pkt/pkt_sendmsg_unbuffered.c
index 7d153d67cf8..b7d7bb510b4 100644
--- a/net/pkt/pkt_sendmsg.c
+++ b/net/pkt/pkt_sendmsg_unbuffered.c
@@ -1,5 +1,5 @@
 /****************************************************************************
- * net/pkt/pkt_sendmsg.c
+ * net/pkt/pkt_sendmsg_unbuffered.c
  *
  * SPDX-License-Identifier: Apache-2.0
  *
diff --git a/net/pkt/pkt_setsockopt.c b/net/pkt/pkt_setsockopt.c
new file mode 100644
index 00000000000..9c340117404
--- /dev/null
+++ b/net/pkt/pkt_setsockopt.c
@@ -0,0 +1,133 @@
+/****************************************************************************
+ * net/pkt/pkt_setsockopt.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/time.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/net/net.h>
+#include <nuttx/net/pkt.h>
+
+#include "socket/socket.h"
+#include "utils/utils.h"
+#include "pkt/pkt.h"
+
+#ifdef CONFIG_NET_PKTPROTO_OPTIONS
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pkt_setsockopt
+ *
+ * Description:
+ *   pkt_setsockopt() sets the PKT-protocol option specified by the
+ *   'option' argument to the value pointed to by the 'value' argument for
+ *   the socket specified by the 'psock' argument.
+ *
+ *   See <nuttx/pkt.h> for the a complete list of values of PKT protocol
+ *   options.
+ *
+ * Input Parameters:
+ *   psock     Socket structure of socket to operate on
+ *   level     Protocol level to set the option
+ *   option    identifies the option to set
+ *   value     Points to the argument value
+ *   value_len The length of the argument value
+ *
+ * Returned Value:
+ *   Returns zero (OK) on success.  On failure, it returns a negated errno
+ *   value to indicate the nature of the error.  See psock_setcockopt() for
+ *   the list of possible error values.
+ *
+ ****************************************************************************/
+
+int pkt_setsockopt(FAR struct socket *psock, int level, int option,
+                   FAR const void *value, socklen_t value_len)
+{
+  int ret = OK;
+
+  DEBUGASSERT(value_len == 0 || value != NULL);
+
+  if (level != SOL_PACKET)
+    {
+      return -ENOPROTOOPT;
+    }
+
+  if (psock->s_type != SOCK_RAW)
+    {
+      nerr("ERROR:  Not a RAW PKT socket\n");
+      return -ENOTCONN;
+    }
+
+  switch (option)
+    {
+#if CONFIG_NET_SEND_BUFSIZE > 0
+      case SO_SNDBUF:
+        {
+          FAR struct pkt_conn_s *conn;
+          conn = psock->s_conn;
+          int buffersize;
+
+          /* Verify options */
+
+          if (value_len != sizeof(int))
+            {
+              return -EINVAL;
+            }
+
+          buffersize = *(FAR int *)value;
+          if (buffersize < 0)
+            {
+              return -EINVAL;
+            }
+
+#   if CONFIG_NET_MAX_SEND_BUFSIZE > 0
+          /* Limit the size of the send buffer */
+
+          buffersize = MIN(buffersize, CONFIG_NET_MAX_SEND_BUFSIZE);
+#   endif
+
+          conn->sndbufs = buffersize;
+          break;
+        }
+#endif
+
+      default:
+        nerr("ERROR: Unrecognized PKT option: %d\n", option);
+        ret = -ENOPROTOOPT;
+        break;
+    }
+
+  return ret;
+}
+
+#endif /* CONFIG_NET_PKTPROTO_OPTIONS */
diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c
index be401da804f..fef6470296d 100644
--- a/net/pkt/pkt_sockif.c
+++ b/net/pkt/pkt_sockif.c
@@ -39,6 +39,7 @@
 #include <nuttx/net/net.h>
 #include <nuttx/net/netdev.h>
 
+#include "devif/devif.h"
 #include "netdev/netdev.h"
 #include <socket/socket.h>
 #include "pkt/pkt.h"
@@ -77,7 +78,14 @@ const struct sock_intf_s g_pkt_sockif =
   pkt_netpoll,     /* si_poll */
   pkt_sendmsg,     /* si_sendmsg */
   pkt_recvmsg,     /* si_recvmsg */
-  pkt_close        /* si_close */
+  pkt_close,       /* si_close */
+  NULL,            /* si_ioctl */
+  NULL,            /* si_socketpair */
+  NULL             /* si_shutdown */
+#if defined(CONFIG_NET_SOCKOPTS) && defined(CONFIG_NET_PKTPROTO_OPTIONS)
+  , pkt_getsockopt /* si_getsockopt */
+  , pkt_setsockopt /* si_setsockopt */
+#endif
 };
 
 /****************************************************************************
@@ -117,6 +125,11 @@ static int pkt_sockif_alloc(FAR struct socket *psock)
 
   conn->type = psock->s_proto;
 
+#if defined(CONFIG_NET_PKT_WRITE_BUFFERS) && CONFIG_NET_SEND_BUFSIZE > 0
+  conn->sndbufs = CONFIG_NET_SEND_BUFSIZE;
+  nxsem_init(&conn->sndsem, 0, 0);
+#endif
+
   /* Save the pre-allocated connection in the socket structure */
 
   psock->s_conn = conn;
@@ -347,6 +360,31 @@ static int pkt_close(FAR struct socket *psock)
 
               iob_free_queue(&conn->readahead);
 
+#ifdef CONFIG_NET_PKT_WRITE_BUFFERS
+              /* Free write buffer callback. */
+
+              if (conn->sndcb != NULL)
+                {
+                  FAR struct net_driver_s *dev;
+                  int ret;
+
+                  while (iob_get_queue_entry_count(&conn->write_q) != 0)
+                    {
+                      ret = net_sem_timedwait_uninterruptible(&conn->sndsem,
+                            _SO_TIMEOUT(conn->sconn.s_sndtimeo));
+                      if (ret < 0)
+                        {
+                          break;
+                        }
+                    }
+
+                  dev = pkt_find_device(conn);
+
+                  pkt_callback_free(dev, conn, conn->sndcb);
+                  conn->sndcb = NULL;
+                }
+#endif
+
               /* Then free the connection structure */
 
               conn->crefs = 0;          /* No more references on the 
connection */
diff --git a/net/socket/Kconfig b/net/socket/Kconfig
index c05b90ad1cb..fb62b24eb9c 100644
--- a/net/socket/Kconfig
+++ b/net/socket/Kconfig
@@ -60,6 +60,12 @@ config NET_CANPROTO_OPTIONS
        ---help---
                Enable or disable support for CAN protocol level socket option
 
+config NET_PKTPROTO_OPTIONS
+       bool
+       default n
+       ---help---
+               Enable or disable support for PKT protocol level socket option
+
 if NET_SOCKOPTS
 
 config NET_SOLINGER

Reply via email to