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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2e27698d6c SLIP: Switch to poll based design
2e27698d6c is described below

commit 2e27698d6c47c2895c645300a60ddaf104ae82c7
Author: Michael Jung <[email protected]>
AuthorDate: Thu May 25 18:12:53 2023 +0200

    SLIP: Switch to poll based design
    
    This is a refactored version of the SLIP network driver.  Updates
    include:
    
    1. The original design started two kernel threads per SLIP device.
       The refactored version uses file_poll to essentially be driven
       by the UART RX and TX interrupts and pushes work to the low
       priority work queue.
    
    2. The SLIP byte un-/stuffing is more efficient now, using memcpy
       instead of handling each byte individually.
    
    3. The switch of the old SLIP driver to IOBs caused buffer overwrites
       if packets were sent that would not fit into a single IOB.  This is
       fixed now.
    
    Signed-off-by: Michael Jung <[email protected]>
---
 drivers/net/slip.c | 1118 ++++++++++++++++++++++++++--------------------------
 net/Kconfig        |   16 +-
 2 files changed, 569 insertions(+), 565 deletions(-)

diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index ddccc0ae91..c46be4b6b9 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -26,33 +26,26 @@
 
 #include <nuttx/config.h>
 
-#include <sys/types.h>
-#include <sys/stat.h>
-
+#include <fcntl.h>
+#include <poll.h>
 #include <stdint.h>
 #include <stdbool.h>
-#include <stdio.h>
 #include <time.h>
 #include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <assert.h>
 #include <errno.h>
+#include <assert.h>
 #include <debug.h>
 
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
 #include <nuttx/irq.h>
-#include <nuttx/clock.h>
 #include <nuttx/signal.h>
-#include <nuttx/kthread.h>
-#include <nuttx/semaphore.h>
-#include <nuttx/fs/fs.h>
-#include <nuttx/mm/iob.h>
-#include <nuttx/net/net.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
 #include <nuttx/net/netdev.h>
-#include <nuttx/net/ip.h>
-#include <nuttx/net/slip.h>
 
-#if defined(CONFIG_NET) && defined(CONFIG_NET_SLIP)
+#ifdef CONFIG_NET_SLIP
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -66,14 +59,6 @@
 
 /* Configuration ************************************************************/
 
-#ifndef CONFIG_NET_SLIP_STACKSIZE
-#  define CONFIG_NET_SLIP_STACKSIZE 2048
-#endif
-
-#ifndef CONFIG_NET_SLIP_DEFPRIO
-#  define CONFIG_NET_SLIP_DEFPRIO 128
-#endif
-
 /* The Linux slip module hard-codes its MTU size to 296 (40 bytes for the
  * IP+TCP headers plus 256 bytes of data).  So you might as well set
  * CONFIG_NET_SLIP_PKTSIZE to 296 as well.
@@ -89,8 +74,25 @@
 #  error "CONFIG_NET_SLIP_PKTSIZE >= 296 is required"
 #endif
 
-/* CONFIG_NET_SLIP_NINTERFACES determines the number of physical interfaces
- * that will be supported.
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required in this configuration 
(CONFIG_SCHED_WORKQUEUE)
+#else
+
+/* The low priority work queue is preferred.  If it is not enabled, LPWORK
+ * will be the same as HPWORK.
+ *
+ * NOTE:  However, the network should NEVER run on the high priority work
+ * queue!  That queue is intended only to service short back end interrupt
+ * processing that never suspends.  Suspending the high priority work queue
+ * may bring the system to its knees!
+ */
+
+#define SLIPWORK LPWORK
+
+/* CONFIG_NET_SLIP_NINTERFACES determines the number of
+ * physical interfaces that will be supported.
  */
 
 #ifndef CONFIG_NET_SLIP_NINTERFACES
@@ -104,12 +106,6 @@
 #define SLIP_ESC_END  0334    /* ESC ESC_END means SLIP_END data byte */
 #define SLIP_ESC_ESC  0335    /* ESC ESC_ESC means ESC data byte */
 
-/* General driver definitions ***********************************************/
-
-/* TX poll delay = 1 second = 1000000 microseconds. */
-
-#define SLIP_WDDELAY   (1*1000000)
-
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -120,26 +116,29 @@
 
 struct slip_driver_s
 {
-  volatile bool bifup;      /* true:ifup false:ifdown */
-  bool          txnodelay;  /* True: nxsig_usleep() not needed */
-  struct file   file;       /* TTY file descriptor */
-  pid_t         rxpid;      /* Receiver thread ID */
-  pid_t         txpid;      /* Transmitter thread ID */
-  sem_t         waitsem;    /* Mutually exclusive access to the network */
+  bool             bifup;     /* true:ifup false:ifdown */
+  struct work_s    irqwork;   /* For deferring interrupt work */
+  struct work_s    pollwork;  /* For deferring poll work to the work queue */
+  struct file      tty;       /* TTY file */
+  struct pollfd    pollfd;    /* Polling TTY for read- or writeable */
+
+  uint8_t          rxbuf[2 * CONFIG_NET_SLIP_PKTSIZE + 2];
+  size_t           rxlen;
+
+  uint8_t          txbuf[2 * CONFIG_NET_SLIP_PKTSIZE + 2];
+  size_t           txlen;
+  size_t           txsent;
 
   /* This holds the information visible to the NuttX network */
 
   struct net_driver_s dev;  /* Interface understood by the network */
-  FAR struct iob_s *rxiob;
 };
 
 /****************************************************************************
  * Private Data
  ****************************************************************************/
 
-/* We really should get rid of CONFIG_NET_SLIP_NINTERFACES and, instead,
- * kmm_malloc() new interface instances as needed.
- */
+/* Driver state structure */
 
 static struct slip_driver_s g_slip[CONFIG_NET_SLIP_NINTERFACES];
 
@@ -149,98 +148,108 @@ static struct slip_driver_s 
g_slip[CONFIG_NET_SLIP_NINTERFACES];
 
 /* Common TX logic */
 
-static void slip_write(FAR struct slip_driver_s *priv,
-                       FAR const uint8_t *buffer, int len);
-static void slip_putc(FAR struct slip_driver_s *priv, int ch);
-static void slip_transmit(FAR struct slip_driver_s *priv);
-static int slip_txpoll(FAR struct net_driver_s *dev);
-static int slip_txtask(int argc, FAR char *argv[]);
+static void slip_transmit(FAR struct slip_driver_s *self);
+static int  slip_txpoll(FAR struct net_driver_s *dev);
+
+/* Interrupt handling */
 
-/* Packet receiver task */
+static void slip_reply(FAR struct slip_driver_s *self);
+static void slip_receive(FAR struct slip_driver_s *self);
+static void slip_txdone(FAR struct slip_driver_s *self);
 
-static int slip_getc(FAR struct slip_driver_s *priv);
-static inline void slip_receive(FAR struct slip_driver_s *priv);
-static int slip_rxtask(int argc, FAR char *argv[]);
+static void slip_interrupt_work(FAR void *arg);
+static void slip_pollfd_cb(FAR struct pollfd *pollfd);
+static void slip_set_pollfd_events(FAR struct slip_driver_s *self,
+                                   short events);
 
 /* NuttX callback functions */
 
-static int slip_ifup(FAR struct net_driver_s *dev);
-static int slip_ifdown(FAR struct net_driver_s *dev);
-static int slip_txavail(FAR struct net_driver_s *dev);
-#ifdef CONFIG_NET_MCASTGROUP
-static int slip_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac);
-static int slip_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac);
-#endif
+static int  slip_ifup(FAR struct net_driver_s *dev);
+static int  slip_ifdown(FAR struct net_driver_s *dev);
+
+static void slip_txavail_work(FAR void *arg);
+static int  slip_txavail(FAR struct net_driver_s *dev);
 
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: slip_write
+ * Name: slip_pollfd_cb
  *
  * Description:
- *   Just an inline wrapper around fwrite with error checking.
+ *   TTY reports to be read- or writable
  *
  * Input Parameters:
- *   priv - Reference to the driver state structure
- *   buffer - Buffer data to send
- *   len - Buffer length in bytes
+ *   pollfd  - Information about the event that happened.
+ *
+ * Returned Value:
+ *   OK on success
  *
  ****************************************************************************/
 
-static inline void slip_write(FAR struct slip_driver_s *priv,
-                              FAR const uint8_t *buffer, int len)
+static void slip_pollfd_cb(FAR struct pollfd *pollfd)
 {
-  /* Handle the case where the write is awakened by a signal */
+  FAR struct slip_driver_s *self = (FAR struct slip_driver_s *)pollfd->arg;
+
+  DEBUGASSERT(self != NULL);
+
+  /* Schedule to perform the processing on the worker thread. */
 
-  while (file_write(&priv->file, buffer, len) < 0)
+  if (work_available(&self->irqwork))
     {
+      work_queue(SLIPWORK, &self->irqwork, slip_interrupt_work, self, 0);
     }
 }
 
 /****************************************************************************
- * Name: slip_write_iob
+ * Name: slip_set_pollfd_events
  *
  * Description:
- *   Just an inline wrapper around IOB writing.
+ *   Setup TTY to report poll events (such as POLLIN and POLLOUT)
  *
  * Input Parameters:
- *   priv - Reference to the driver state structure
- *   iob - IO Buffer data to send
- *   len - Buffer length in bytes
+ *   self   - The SLIP interface to register for poll events
+ *   events - The poll events to request reporting for
  *
  ****************************************************************************/
 
-static inline void slip_write_iob(FAR struct slip_driver_s *priv,
-                                  FAR struct iob_s **iob, int len)
+static void slip_set_pollfd_events(FAR struct slip_driver_s *self,
+                                   short events)
 {
-  while (len > 0 && (*iob)->io_pktlen > 0)
+  int ret;
+
+  /* Teardown any potentially pending poll, if applicable */
+
+  if (self->pollfd.events != 0)
     {
-      int nbytes = MIN(len, (*iob)->io_len);
-      slip_write(priv, IOB_DATA(*iob), nbytes);
+      ret = file_poll(&self->tty, &self->pollfd, false);
 
-      len -= nbytes;
-      *iob = iob_trimhead(*iob, nbytes);
+      if (ret != OK)
+        {
+          nerr("file_poll(false) failed: %d\n", ret);
+        }
     }
-}
 
-/****************************************************************************
- * Name: slip_putc
- *
- * Description:
- *   Just an inline wrapper around putc with error checking.
- *
- * Input Parameters:
- *   priv - Reference to the driver state structure
- *   ch - The character to send
- *
- ****************************************************************************/
+  memset(&self->pollfd, 0, sizeof(self->pollfd));
 
-static inline void slip_putc(FAR struct slip_driver_s *priv, int ch)
-{
-  uint8_t buffer = (uint8_t)ch;
-  slip_write(priv, &buffer, 1);
+  /* Setup requested poll, if applicable */
+
+  if (events != 0)
+    {
+      self->pollfd.arg     = self;
+      self->pollfd.cb      = slip_pollfd_cb;
+      self->pollfd.events  = events;
+      self->pollfd.revents = 0;
+      self->pollfd.priv    = NULL;
+
+      ret = file_poll(&self->tty, &self->pollfd, true);
+
+      if (ret != OK)
+        {
+          nerr("file_poll(true) failed: %d\n", ret);
+        }
+    }
 }
 
 /****************************************************************************
@@ -251,490 +260,540 @@ static inline void slip_putc(FAR struct slip_driver_s 
*priv, int ch)
  *   handling or from watchdog based polling.
  *
  * Input Parameters:
- *   priv  - Reference to the driver state structure
+ *   self - Reference to the driver state structure
  *
- * Returned Value:
- *   OK on success; a negated errno on failure
+ * Assumptions:
+ *   The network is locked.
  *
  ****************************************************************************/
 
-static void slip_transmit(FAR struct slip_driver_s *priv)
+static void slip_transmit(FAR struct slip_driver_s *self)
 {
-  uint8_t  src;
-  uint8_t  esc;
-  int      remaining;
-  int      len;
+  ssize_t ssz;
+  uint8_t *p;
 
-  /* Increment statistics */
+  DEBUGASSERT(self->dev.d_len > 0);
+
+  /* Verify that the hardware is ready to send another packet.  If we get
+   * here, then we are committed to sending a packet; Higher level logic
+   * must have assured that there is no transmission in progress.
+   */
+
+  if (self->txlen > 0)
+    {
+      /* Transmission of previous packet is still pending.  This might happen
+       * in the 'slip_receive' -> 'slip_reply' -> 'slip_transmit' case.  Try
+       * to forward pending packet into UART's transmit buffer.  Timeout on
+       * packet if not forwarded within a second.
+       */
 
-  ninfo("Sending packet size %d\n", priv->dev.d_len);
-  NETDEV_TXPACKETS(&priv->dev);
+      for (int i = 0; (i < 10) && (self->txsent != self->txlen); )
+        {
+          ssz = file_write(&self->tty,
+                           &self->txbuf[self->txsent],
+                           self->txlen - self->txsent);
+          if (ssz <= 0)
+            {
+              nxsig_usleep(10000);
+              i++;
+              continue;
+            }
+
+          self->txsent += ssz;
+        }
+
+      if (self->txsent == self->txlen)
+        {
+          slip_txdone(self);
+        }
+      else
+        {
+          NETDEV_TXTIMEOUTS(&self->dev);
+        }
+    }
+
+  self->txlen = 0;
+  self->txsent = 0;
 
   /* Send an initial END character to flush out any data that may have
    * accumulated in the receiver due to line noise
    */
 
-  slip_putc(priv, SLIP_END);
+  self->txbuf[self->txlen++] = SLIP_END;
 
-  /* For each byte in the packet, send the appropriate character sequence */
+  /* Now copy the I/O buffer into self->txbuf */
 
-  iob_copyout(&src, priv->dev.d_iob, 1, 0);
-  remaining = priv->dev.d_len;
-  len       = 0;
-
-  while (remaining-- > 0)
+  for (unsigned int bytesread = 0; bytesread < self->dev.d_len; )
     {
-      switch (src)
+      unsigned int chunk_sz = sizeof(self->txbuf) - self->txlen;
+      int copied;
+
+      if (self->dev.d_len - bytesread < chunk_sz)
         {
-          /* If it's the same code as an END character, we send a special two
-           * character code so as not to make the receiver think we sent an
-           * END
-           */
+          chunk_sz = self->dev.d_len - bytesread;
+        }
 
-          case SLIP_END:
-            esc = SLIP_ESC_END;
-            goto escape;
+      copied = iob_copyout(&self->txbuf[self->txlen],
+                           self->dev.d_iob,
+                           chunk_sz,
+                           bytesread);
+      if (copied <= 0)
+        {
+          goto error;
+        }
 
-          /* If it's the same code as an ESC character, we send a special two
-           * character code so as not to make the receiver think we sent an
-           * ESC
-           */
+      bytesread += (unsigned int)copied;
+      self->txlen += (size_t)copied;
+    }
 
-          case SLIP_ESC:
-            esc = SLIP_ESC_ESC;
+  /* SLIP encode self->txbuf.  First escape the ESC bytes. */
 
-          escape:
-            {
-              /* Flush any unsent data */
+  for (p = memchr(&self->txbuf[1], SLIP_ESC, self->txlen - 1);
+       p != NULL;
+       p = memchr(p, SLIP_ESC, self->txlen - 1))
+    {
+      ssize_t postfix_len = self->txlen - (p - self->txbuf) - 1;
 
-              if (len > 0)
-                {
-                  slip_write_iob(priv, &priv->dev.d_iob, len);
-                }
+      if (self->txlen >= sizeof(self->txbuf))
+        {
+          goto error;
+        }
 
-              /* Reset */
+      if (postfix_len > 0)
+        {
+          memmove(p + 2, p + 1, postfix_len);
+        }
 
-              priv->dev.d_iob = iob_trimhead(priv->dev.d_iob, 1);
-              len             = 0;
+      p++;
+      *p = SLIP_ESC_ESC;
+      self->txlen++;
+    }
 
-              /* Then send the escape sequence */
+  /* SLIP encode self->txbuf.  Then escape the END bytes. */
 
-              slip_putc(priv, SLIP_ESC);
-              slip_putc(priv, esc);
-            }
-          break;
+  for (p = memchr(&self->txbuf[1], SLIP_END, self->txlen - 1);
+       p != NULL;
+       p = memchr(p, SLIP_END, self->txlen - 1))
+    {
+      ssize_t postfix_len = self->txlen - (p - self->txbuf) - 1;
 
-          /* otherwise, just bump up the count */
+      if (self->txlen >= sizeof(self->txbuf))
+        {
+          goto error;
+        }
 
-          default:
-            len++;
-            break;
+      if (postfix_len > 0)
+        {
+          memmove(p + 2, p + 1, postfix_len);
         }
 
-      /* Copyout the next character in the packet */
+      *p = SLIP_ESC;
+      p++;
+      *p = SLIP_ESC_END;
+      self->txlen++;
+    }
 
-      iob_copyout(&src, priv->dev.d_iob, 1, len);
+  /* Append the END token */
+
+  if (self->txlen >= sizeof(self->txbuf))
+    {
+      goto error;
     }
 
-  /* We have looked at every character in the packet.  Now flush any unsent
-   * data
-   */
+  self->txbuf[self->txlen++] = SLIP_END;
+
+  /* Increment statistics */
+
+  NETDEV_TXPACKETS(&self->dev);
+
+  /* Try to send packet */
 
-  if (len > 0)
+  ssz = file_write(&self->tty, self->txbuf, self->txlen);
+
+  if (ssz > 0)
+    {
+      self->txsent = (size_t)ssz;
+    }
+  else
     {
-      slip_write_iob(priv, &priv->dev.d_iob, len);
+      self->txsent = 0;
     }
 
-  /* And send the END token */
+  if (self->txsent == self->txlen)
+    {
+      /* Complete packet went out at first try. */
 
-  slip_putc(priv, SLIP_END);
-  NETDEV_TXDONE(&priv->dev);
-  priv->txnodelay = true;
+      slip_txdone(self);
+    }
+
+  return;
+
+error:
+
+  /* Drop the packet and reset the receiver logic. */
+
+  self->txlen  = 0;
+  self->txsent = 0;
+
+  NETDEV_TXERRORS(&self->dev);
 }
 
 /****************************************************************************
  * Name: slip_txpoll
  *
  * Description:
- *   Check if the network has any outgoing packets ready to send.  This is a
- *   callback from devif_poll().  devif_poll() may be called:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
  *
- *   1. When the preceding TX packet send is complete, or
- *   2. During normal periodic polling
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
  *
  * Input Parameters:
- *   dev  - Reference to the NuttX driver state structure
+ *   dev - Reference to the NuttX driver state structure
  *
  * Returned Value:
  *   OK on success; a negated errno on failure
  *
  * Assumptions:
- *   The initiator of the poll holds the priv->waitsem;
+ *   The network is locked.
  *
  ****************************************************************************/
 
 static int slip_txpoll(FAR struct net_driver_s *dev)
 {
-  FAR struct slip_driver_s *priv =
-    (FAR struct slip_driver_s *)dev->d_private;
+  FAR struct slip_driver_s *self =
+      (FAR struct slip_driver_s *)dev->d_private;
+
+  /* Send the packet */
 
-  slip_transmit(priv);
+  slip_transmit(self);
 
   /* If zero is returned, the polling will continue until all connections
-   * have been examined.
+   * have been examined.  We return -EBUSY if there is still transmission
+   * data pending in the TTY's buffer.
    */
 
-  return 0;
+  return (self->txlen > 0) ? -EBUSY : OK;
 }
 
 /****************************************************************************
- * Name: slip_txtask
+ * Name: slip_reply
  *
  * Description:
- *   Polling and transmission is performed on tx thread.
+ *   After a packet has been received and dispatched to the network, it
+ *   may return return with an outgoing packet.  This function checks for
+ *   that case and performs the transmission if necessary.
  *
  * Input Parameters:
- *   arg  - Reference to the NuttX driver state structure
+ *   self - Reference to the driver state structure
  *
  * Returned Value:
  *   None
  *
+ * Assumptions:
+ *   The network is locked.
+ *
  ****************************************************************************/
 
-static int slip_txtask(int argc, FAR char *argv[])
+static void slip_reply(struct slip_driver_s *self)
 {
-  FAR struct slip_driver_s *priv;
-  unsigned int index = *(argv[1]) - '0';
+  /* If the packet dispatch resulted in data that should be sent out on the
+   * network, the field d_len will set to a value > 0.
+   */
+
+  if (self->dev.d_len > 0)
+    {
+      /* And send the packet */
+
+      slip_transmit(self);
+    }
+}
+
+/****************************************************************************
+ * Name: slip_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of a new RX packet
+ *
+ * Input Parameters:
+ *   self - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void slip_receive(FAR struct slip_driver_s *self)
+{
+  FAR struct net_driver_s *dev = &self->dev;
+  FAR struct iob_s *iob;
+  FAR uint8_t *p;
+  FAR uint8_t *pend;
+  size_t remaining;
+  size_t copied;
   int ret;
 
-  nerr("index: %d\n", index);
-  DEBUGASSERT(index < CONFIG_NET_SLIP_NINTERFACES);
+  /* Drop potential prefix SLIP_ENDs */
 
-  /* Get our private data structure instance and wake up the waiting
-   * initialization logic.
-   */
+  while ((self->rxbuf[0] == SLIP_END) && (self->rxlen > 0))
+    {
+      self->rxlen--;
+      memmove(&self->rxbuf[0], &self->rxbuf[1], self->rxlen);
+    }
 
-  priv = &g_slip[index];
-  nxsem_post(&priv->waitsem);
+  /* Find end of packet */
 
-  /* Loop forever */
+  pend = memchr(self->rxbuf, SLIP_END, self->rxlen);
 
-  for (; ; )
+  if (pend == NULL)
     {
-      /* Wait for the timeout to expire (or until we are signaled by  */
+      /* No complete packet present.  Let's wait for more bytes to arrive. */
 
-      ret = nxsem_wait_uninterruptible(&priv->waitsem);
-      if (ret < 0)
+      if (self->rxlen == sizeof(self->rxbuf))
         {
-          DEBUGASSERT(ret == -ECANCELED);
-          break;
+          /* Purge receive buffer overflow due to overflow. */
+
+          NETDEV_RXERRORS(&self->dev);
+          self->rxlen = 0;
         }
 
-      if (!priv->txnodelay)
+      return;
+    }
+
+  p = self->rxbuf;
+  remaining = pend - p;
+  copied = 0;
+  iob = iob_alloc(false);
+  iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE);
+
+  while (remaining)
+    {
+      uint8_t *pesc = memchr(p, SLIP_ESC, remaining);
+
+      if (pesc != NULL)
         {
-          nxsem_post(&priv->waitsem);
-          nxsig_usleep(SLIP_WDDELAY);
+          unsigned int prefix_len = (unsigned int)(pesc - p);
+
+          if (prefix_len > 0)
+            {
+              ret = iob_copyin(iob, p, prefix_len, copied, false);
+
+              DEBUGASSERT(ret >= 0);
+              DEBUGASSERT((unsigned int)ret == prefix_len);
+
+              copied += prefix_len;
+              remaining -= prefix_len;
+            }
+
+          p = pesc + 1;
+          remaining--;
+
+          switch (*p)
+            {
+              case SLIP_ESC_END:
+                *p = SLIP_END;
+                break;
+
+              case SLIP_ESC_ESC:
+                *p = SLIP_ESC;
+                break;
+
+              default:
+
+                /* SLIP protocol error */
+
+                goto error;
+            }
+
+          ret = iob_copyin(iob, p, 1, copied, false);
+          DEBUGASSERT(ret == 1);
+          p++;
+          copied++;
+          remaining--;
         }
       else
         {
-          priv->txnodelay = false;
-          nxsem_post(&priv->waitsem);
+          ret = iob_copyin(iob, p, remaining, copied, false);
+          DEBUGASSERT(ret >= 0);
+          DEBUGASSERT((unsigned int)ret == remaining);
+          p += remaining;
+          copied += remaining;
+          remaining = 0;
         }
+    }
 
-      /* Is the interface up? */
+  /* Move remaining bytes in rxbuf to the front */
 
-      if (priv->bifup)
-        {
-          /* Poll the networking layer for new XMIT data. */
+  DEBUGASSERT((pend - self->rxbuf) <= self->rxlen);
+  self->rxlen -= (pend - self->rxbuf);
+  memmove(self->rxbuf, pend, self->rxlen);
 
-          net_lock();
+  /* Handle the IP input. */
 
-          /* perform the normal TX poll */
+  netdev_iob_replace(&self->dev, iob);
+  iob = NULL;
 
-          devif_poll(&priv->dev, slip_txpoll);
+  NETDEV_RXPACKETS(&self->dev);
 
-          net_unlock();
-        }
+  /* All packets are assumed to be IP packets (we don't have a choice..
+   * there is no Ethernet header containing the EtherType).  So pass the
+   * received packet on for IP processing -- but only if it is big
+   * enough to hold an IP header.
+   */
+
+  if ((IPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION)
+    {
+      NETDEV_RXIPV4(&self->dev);
+
+      ipv4_input(&self->dev);
+
+      slip_reply(self);
+    }
+  else
+    {
+      NETDEV_RXDROPPED(&self->dev);
     }
 
-  return OK;
-}
+  return;
 
-/****************************************************************************
- * Name: slip_getc
- *
- * Description:
- *   Get one byte from the serial input.
- *
- * Input Parameters:
- *   priv - Reference to the driver state structure
- *
- * Returned Value:
- *   The returned byte
- *
- ****************************************************************************/
+error:
 
-static inline int slip_getc(FAR struct slip_driver_s *priv)
-{
-  uint8_t ch;
+  NETDEV_RXERRORS(&self->dev);
 
-  while (file_read(&priv->file, &ch, 1) < 0)
+  if (iob)
     {
+      iob_free_chain(iob);
+      iob = NULL;
     }
 
-  return ch;
+  /* Move remaining bytes in rxbuf to the front */
+
+  DEBUGASSERT((pend - self->rxbuf) <= self->rxlen);
+  self->rxlen -= (pend - self->rxbuf);
+  memmove(self->rxbuf, pend, self->rxlen);
 }
 
 /****************************************************************************
- * Name: slip_receive
+ * Name: slip_txdone
  *
  * Description:
- *   Read a packet from the serial input
+ *   An interrupt was received indicating that the last TX packet(s) is done
  *
  * Input Parameters:
- *   priv  - Reference to the driver state structure
+ *   self - Reference to the driver state structure
  *
  * Returned Value:
  *   None
  *
+ * Assumptions:
+ *   The network is locked.
+ *
  ****************************************************************************/
 
-static inline void slip_receive(FAR struct slip_driver_s *priv)
+static void slip_txdone(FAR struct slip_driver_s *self)
 {
-  uint8_t ch;
-
-  /* Copy the data from the hardware to the RX buffer until we put
-   * together a whole packet. Make sure not to copy them into the
-   * packet if we run out of room.
-   */
+  /* Update statistics */
 
-  ninfo("Receiving packet\n");
-  for (; ; )
-    {
-      /* Get the next character in the stream. */
-
-      ch = slip_getc(priv);
-
-      /* Handle bytestuffing if necessary */
+  self->txlen  = 0;
+  self->txsent = 0;
 
-      switch (ch)
-        {
-        /* If it's an END character then we're done with the packet.
-         * (OR we are just starting a packet)
-         */
-
-        case SLIP_END:
-          {
-            ninfo("END\n");
-
-            /* A minor optimization: if there is no data in the packet,
-             * ignore it. This is meant to avoid bothering IP with all the
-             * empty packets generated by the duplicate END characters which
-             * are in turn sent to try to detect line noise.
-             */
-
-            if (priv->rxiob->io_pktlen > 0)
-              {
-                ninfo("Received packet size %d\n", priv->rxiob->io_pktlen);
-                return;
-              }
-          }
-          break;
-
-        /* if it's the same code as an ESC character, wait and get another
-         * character and then figure out what to store in the packet based
-         * on that.
-         */
-
-        case SLIP_ESC:
-          {
-            ninfo("ESC\n");
-            ch = slip_getc(priv);
-
-            /* if "ch" is not one of these two, then we have a protocol
-             * violation.  The best bet seems to be to leave the byte alone
-             * and just stuff it into the packet
-             */
-
-            switch (ch)
-              {
-              case SLIP_ESC_END:
-                ninfo("ESC-END\n");
-                ch = SLIP_END;
-                break;
+  NETDEV_TXDONE(&self->dev);
 
-               case SLIP_ESC_ESC:
-                ninfo("ESC-ESC\n");
-                ch = SLIP_ESC;
-                break;
+  /* Poll the network for new TX data */
 
-              default:
-                nerr("ERROR: Protocol violation: %02x\n", ch);
-                break;
-              }
-
-            /* Here we fall into the default handler and let it store the
-             * character for us
-             */
-          }
-
-        default:
-          {
-            if (priv->rxiob->io_pktlen < CONFIG_NET_SLIP_PKTSIZE + 2)
-              {
-                iob_copyin(priv->rxiob, &ch, 1, priv->rxiob->io_pktlen,
-                           false);
-              }
-          }
-          break;
-        }
+  if (work_available(&self->pollwork))
+    {
+      work_queue(SLIPWORK, &self->pollwork, slip_txavail_work, self, 0);
     }
 }
 
 /****************************************************************************
- * Name: slip_rxtask
+ * Name: slip_interrupt_work
  *
  * Description:
- *   Wait for incoming data.
+ *   Perform interrupt related work from the worker thread
  *
  * Input Parameters:
- *   argc
- *   argv
+ *   arg - The argument passed when work_queue() was called.
  *
  * Returned Value:
- *   (Does not return)
+ *   OK on success
  *
  * Assumptions:
+ *   Runs on a worker thread.
  *
  ****************************************************************************/
 
-static int slip_rxtask(int argc, FAR char *argv[])
+static void slip_interrupt_work(FAR void *arg)
 {
-  FAR struct slip_driver_s *priv;
-  FAR struct net_driver_s *dev;
-  unsigned int index = *(argv[1]) - '0';
-  uint8_t ch;
-
-  nerr("index: %d\n", index);
-  DEBUGASSERT(index < CONFIG_NET_SLIP_NINTERFACES);
-
-  /* Get our private data structure instance and wake up the waiting
-   * initialization logic.
-   */
-
-  priv = &g_slip[index];
-  nxsem_post(&priv->waitsem);
+  FAR struct slip_driver_s *self = (FAR struct slip_driver_s *)arg;
+  ssize_t ssz;
 
-  /* Loop forever */
-
-  dev = &priv->dev;
-  for (; ; )
+  if (!self->bifup)
     {
-      /* Wait for the next character to be available on the input stream. */
-
-      ninfo("Waiting...\n");
-      ch = slip_getc(priv);
-
-      /* Ignore any input that we receive before the interface is up. */
+      return;
+    }
 
-      if (!priv->bifup)
-        {
-          continue;
-        }
+  /* Lock the network and serialize driver operations if necessary.
+   * NOTE: Serialization is only required in the case where the driver work
+   * is performed on an LP worker thread and where more than one LP worker
+   * thread has been configured.
+   */
 
-      /* Prepare our IOB for data receiving, may block until we get one. */
+  net_lock();
 
-      DEBUGASSERT(priv->rxiob == NULL);
+  /* Process pending Ethernet interrupts */
 
-      priv->rxiob = iob_alloc(false);
-      iob_reserve(priv->rxiob, CONFIG_NET_LL_GUARDSIZE);
+  /* Get and clear interrupt status bits */
 
-      /* We have something...
-       *
-       * END characters may appear at packet boundaries BEFORE as well as
-       * after the beginning of the packet.  This is normal and expected.
-       */
+  /* Handle interrupts according to status bit settings */
 
-      if (ch == SLIP_END)
+  if (self->rxlen < sizeof(self->rxbuf))
+    {
+      ssz = file_read(&self->tty,
+                      &self->rxbuf[self->rxlen],
+                      sizeof(self->rxbuf) - self->rxlen);
+      if (ssz > 0)
         {
-          /* Do nothing. */
+          self->rxlen += (size_t)ssz;
         }
+    }
 
-      /* Otherwise, we are in danger of being out-of-sync.  Apparently the
-       * leading END character is optional.  Let's try to continue.
-       */
-
-      else
+  if (self->txsent < self->txlen)
+    {
+      ssz = file_write(&self->tty,
+                       &self->txbuf[self->txsent],
+                       self->txlen - self->txsent);
+      if (ssz > 0)
         {
-          iob_copyin(priv->rxiob, &ch, 1, 0, false);
+          self->txsent += (size_t)ssz;
         }
+    }
 
-      /* Copy the data data from the hardware to priv->rxbuf until we put
-       * together a whole packet.
-       */
-
-      slip_receive(priv);
-
-      /* Handle the IP input.  Get exclusive access to the network. */
-
-      net_lock();
-      netdev_iob_replace(&priv->dev, priv->rxiob);
-      priv->rxiob = NULL;
-
-      NETDEV_RXPACKETS(&priv->dev);
-
-      /* All packets are assumed to be IP packets (we don't have a choice..
-       * there is no Ethernet header containing the EtherType).  So pass the
-       * received packet on for IP processing -- but only if it is big
-       * enough to hold an IP header.
-       */
-
-#ifdef CONFIG_NET_IPv4
-      if ((IPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION)
-        {
-          NETDEV_RXIPV4(&priv->dev);
-          ipv4_input(&priv->dev);
-
-          /* If the above function invocation resulted in data that should
-           * be sent out on the network, the field  d_len will set to a
-           * value > 0.  NOTE that we are transmitting using the RX buffer!
-           */
-
-          if (priv->dev.d_len > 0)
-            {
-              slip_transmit(priv);
-            }
-        }
-      else
-#endif
-#ifdef CONFIG_NET_IPv6
-      if ((IPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION)
-        {
-          NETDEV_RXIPV6(&priv->dev);
-          ipv6_input(&priv->dev);
+  /* Check if we received an incoming packet, if so, call slip_receive() */
 
-          /* If the above function invocation resulted in data that should
-           * be sent out on the network, the field  d_len will set to a
-           * value > 0.  NOTE that we are transmitting using the RX buffer!
-           */
+  if (self->rxlen == sizeof(self->rxbuf) ||
+      memchr(self->rxbuf, SLIP_END, self->rxlen))
+    {
+      slip_receive(self);
+    }
 
-          if (priv->dev.d_len > 0)
-            {
-              slip_transmit(priv);
-            }
-        }
-      else
-#endif
-        {
-          NETDEV_RXERRORS(&priv->dev);
-        }
+  /* Check if a packet transmission just completed.  If so, call skel_txdone.
+   * This may disable further Tx interrupts if there are no pending
+   * transmissions.
+   */
 
-      net_unlock();
+  if (self->txlen > 0 && self->txsent == self->txlen)
+    {
+      slip_txdone(self);
     }
 
-  /* We won't get here */
-
-  return OK;
+  net_unlock();
 }
 
 /****************************************************************************
@@ -745,36 +804,35 @@ static int slip_rxtask(int argc, FAR char *argv[])
  *   provided
  *
  * Input Parameters:
- *   dev  - Reference to the NuttX driver state structure
+ *   dev - Reference to the NuttX driver state structure
  *
  * Returned Value:
  *   None
  *
  * Assumptions:
+ *   The network is locked.
  *
  ****************************************************************************/
 
 static int slip_ifup(FAR struct net_driver_s *dev)
 {
-  FAR struct slip_driver_s *priv =
-    (FAR struct slip_driver_s *)dev->d_private;
+  FAR struct slip_driver_s *self =
+      (FAR struct slip_driver_s *)dev->d_private;
 
-#ifdef CONFIG_NET_IPv4
-  nerr("Bringing up: %d.%d.%d.%d\n",
-        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
-        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
-#endif
-#ifdef CONFIG_NET_IPv6
-  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
-        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
-        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
-        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
-#endif
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)dev->d_ipaddr & 0xff,
+        (int)(dev->d_ipaddr >> 8) & 0xff,
+        (int)(dev->d_ipaddr >> 16) & 0xff,
+        (int)dev->d_ipaddr >> 24);
+
+  /* Enable POLLIN and POLLOUT events on the TTY */
+
+  slip_set_pollfd_events(self, POLLIN | POLLOUT);
+
+  /* Mark the device "up" */
 
-  /* Mark the interface up */
+  self->bifup = true;
 
-  priv->bifup = true;
-  netdev_carrier_on(dev);
   return OK;
 }
 
@@ -785,115 +843,118 @@ static int slip_ifup(FAR struct net_driver_s *dev)
  *   NuttX Callback: Stop the interface.
  *
  * Input Parameters:
- *   dev  - Reference to the NuttX driver state structure
+ *   dev - Reference to the NuttX driver state structure
  *
  * Returned Value:
  *   None
  *
  * Assumptions:
+ *   The network is locked.
  *
  ****************************************************************************/
 
 static int slip_ifdown(FAR struct net_driver_s *dev)
 {
-  FAR struct slip_driver_s *priv =
+  FAR struct slip_driver_s *self =
     (FAR struct slip_driver_s *)dev->d_private;
 
-  netdev_carrier_off(dev);
+  /* Disable the Ethernet interrupt */
+
+  slip_set_pollfd_events(self, 0);
 
   /* Mark the device "down" */
 
-  priv->bifup = false;
+  self->bifup = false;
+
   return OK;
 }
 
 /****************************************************************************
- * Name: slip_txavail
+ * Name: slip_txavail_work
  *
  * Description:
- *   Driver callback invoked when new TX data is available.  This is a
- *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
- *   latency.
+ *   Perform an out-of-cycle poll on the worker thread.
  *
  * Input Parameters:
- *   dev  - Reference to the NuttX driver state structure
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
  *
  * Returned Value:
  *   None
  *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
  ****************************************************************************/
 
-static int slip_txavail(FAR struct net_driver_s *dev)
+static void slip_txavail_work(FAR void *arg)
 {
-  FAR struct slip_driver_s *priv =
-    (FAR struct slip_driver_s *)dev->d_private;
+  FAR struct slip_driver_s *self = (FAR struct slip_driver_s *)arg;
+
+  /* Lock the network and serialize driver operations if necessary.
+   * NOTE: Serialization is only required in the case where the driver work
+   * is performed on an LP worker thread and where more than one LP worker
+   * thread has been configured.
+   */
+
+  net_lock();
 
   /* Ignore the notification if the interface is not yet up */
 
-  if (priv->bifup)
+  if (self->bifup)
     {
-      /* Wake up the TX polling thread */
+      /* Check if there is room in the hardware to hold another packet. */
+
+      if (self->txlen == 0)
+        {
+          /* If so, then poll the network for new XMIT data */
 
-      priv->txnodelay = true;
-      nxsig_kill(priv->txpid, SIGALRM);
+          self->dev.d_buf = NULL;
+
+          devif_poll(&self->dev, slip_txpoll);
+        }
     }
 
-  return OK;
+  net_unlock();
 }
 
 /****************************************************************************
- * Name: slip_addmac
+ * Name: slip_txavail
  *
  * Description:
- *   NuttX Callback: Add the specified MAC address to the hardware multicast
- *   address filtering
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
  *
  * Input Parameters:
- *   dev  - Reference to the NuttX driver state structure
- *   mac  - The MAC address to be added
+ *   dev - Reference to the NuttX driver state structure
  *
  * Returned Value:
  *   None
  *
  * Assumptions:
+ *   The network is locked.
  *
  ****************************************************************************/
 
-#ifdef CONFIG_NET_MCASTGROUP
-static int slip_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
+static int slip_txavail(FAR struct net_driver_s *dev)
 {
-  /* Add the MAC address to the hardware multicast routing table */
+  FAR struct slip_driver_s *self =
+      (FAR struct slip_driver_s *)dev->d_private;
 
-  return OK;
-}
-#endif
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
 
-/****************************************************************************
- * Name: slip_rmmac
- *
- * Description:
- *   NuttX Callback: Remove the specified MAC address from the hardware
- *   multicast address filtering
- *
- * Input Parameters:
- *   dev  - Reference to the NuttX driver state structure
- *   mac  - The MAC address to be removed
- *
- * Returned Value:
- *   None
- *
- * Assumptions:
- *
- ****************************************************************************/
+  if (work_available(&self->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
 
-#ifdef CONFIG_NET_MCASTGROUP
-static int slip_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
-{
-  /* Add the MAC address to the hardware multicast routing table */
+      work_queue(SLIPWORK, &self->pollwork, slip_txavail_work, self, 0);
+    }
 
   return OK;
 }
-#endif
 
 /****************************************************************************
  * Public Functions
@@ -915,91 +976,48 @@ static int slip_rmmac(FAR struct net_driver_s *dev, FAR 
const uint8_t *mac)
  * Returned Value:
  *   OK on success; Negated errno on failure.
  *
- * Assumptions:
- *
  ****************************************************************************/
 
 int slip_initialize(int intf, FAR const char *devname)
 {
-  FAR struct slip_driver_s *priv;
-  char buffer[12];
-  FAR char *argv[2];
+  FAR struct slip_driver_s *self;
   int ret;
 
   /* Get the interface structure associated with this interface number. */
 
   DEBUGASSERT(intf < CONFIG_NET_SLIP_NINTERFACES);
-  priv = &g_slip[intf];
+  self = &g_slip[intf];
 
   /* Initialize the driver structure */
 
-  memset(priv, 0, sizeof(struct slip_driver_s));
-  priv->dev.d_ifup    = slip_ifup;     /* I/F up (new IP address) callback */
-  priv->dev.d_ifdown  = slip_ifdown;   /* I/F down callback */
-  priv->dev.d_txavail = slip_txavail;  /* New TX data callback */
-#ifdef CONFIG_NET_MCASTGROUP
-  priv->dev.d_addmac  = slip_addmac;   /* Add multicast MAC address */
-  priv->dev.d_rmmac   = slip_rmmac;    /* Remove multicast MAC address */
-#endif
-  priv->dev.d_private = priv;          /* Used to recover private state from 
dev */
+  memset(self, 0, sizeof(struct slip_driver_s));
+  self->dev.d_ifup    = slip_ifup;    /* I/F up (new IP address) callback */
+  self->dev.d_ifdown  = slip_ifdown;  /* I/F down callback */
+  self->dev.d_txavail = slip_txavail; /* New TX data callback */
+  self->dev.d_private = self;         /* Used to recover SLIP I/F instance */
 
-  /* Open the device */
+  ret = file_open(&self->tty, devname, O_RDWR | O_NONBLOCK);
 
-  ret = file_open(&priv->file, devname, O_RDWR, 0666);
   if (ret < 0)
     {
       nerr("ERROR: Failed to open %s: %d\n", devname, ret);
       return ret;
     }
 
-  /* Initialize the wait semaphore */
-
-  nxsem_init(&priv->waitsem, 0, 0);
-
-  /* Start the SLIP receiver kernel thread */
-
-  snprintf(buffer, sizeof(buffer), "%d", intf);
-  argv[0] = buffer;
-  argv[1] = NULL;
-
-  ret = kthread_create("rxslip", CONFIG_NET_SLIP_DEFPRIO,
-                       CONFIG_NET_SLIP_STACKSIZE, slip_rxtask, argv);
-  if (ret < 0)
-    {
-      nerr("ERROR: Failed to start receiver task\n");
-      return ret;
-    }
-
-  priv->rxpid = (pid_t)ret;
-
-  /* Wait and make sure that the receive task is started. */
-
-  nxsem_wait_uninterruptible(&priv->waitsem);
-
-  /* Start the SLIP transmitter kernel thread */
-
-  ret = kthread_create("txslip", CONFIG_NET_SLIP_DEFPRIO,
-                       CONFIG_NET_SLIP_STACKSIZE, slip_txtask, argv);
-  if (ret < 0)
-    {
-      nerr("ERROR: Failed to start receiver task\n");
-      return ret;
-    }
-
-  priv->txpid = (pid_t)ret;
-
-  /* Wait and make sure that the transmit task is started. */
-
-  nxsem_wait_uninterruptible(&priv->waitsem);
-
-  /* Bump the semaphore count so that it can now be used as a mutex */
+  /* Put the interface in the down state.  This usually amounts to resetting
+   * the device and/or calling slip_ifdown().
+   */
 
-  nxsem_post(&priv->waitsem);
+  slip_set_pollfd_events(self, 0);
+  self->bifup = false;
 
   /* Register the device with the OS so that socket IOCTLs can be performed */
 
-  netdev_register(&priv->dev, NET_LL_SLIP);
+  netdev_register(&self->dev, NET_LL_SLIP);
+
   return OK;
 }
 
-#endif /* CONFIG_NET && CONFIG_NET_SLIP */
+#endif /* !defined(CONFIG_SCHED_WORKQUEUE) */
+
+#endif /* CONFIG_NET_SLIP */
diff --git a/net/Kconfig b/net/Kconfig
index 21d813b645..c5d1d40d77 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -192,6 +192,7 @@ menuconfig NET_MBIM
 menuconfig NET_SLIP
        bool "SLIP support"
        select ARCH_HAVE_NETDEV_STATISTICS
+       depends on NET_IPv4
        default n
        ---help---
                Enables building of the SLIP driver. SLIP requires
@@ -221,21 +222,6 @@ config NET_SLIP_NINTERFACES
                interfaces to support.
                Default: 1
 
-config NET_SLIP_STACKSIZE
-       int "SLIP per-thread stack size"
-       default DEFAULT_TASK_STACKSIZE
-       ---help---
-               Select the stack size of the SLIP RX and TX tasks.
-
-               SLIP starts two dedicated threads per interface
-               to handle network events, enabling high performance.
-
-config NET_SLIP_DEFPRIO
-       int "SLIP threads priority"
-       default 128
-       ---help---
-               The priority of the SLIP RX and TX tasks. Default: 128
-
 endif # NET_SLIP
 
 menuconfig NET_TUN


Reply via email to