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

commit 242c25317872b93020c444ace834806e60ddad95
Author: Zhe Weng <[email protected]>
AuthorDate: Tue Jan 7 11:04:53 2025 +0800

    net: Add VLAN device support
    
    Inspired by Linux's way, we also create VLAN devices for managing VLAN,
    which will become interfaces like `eth0.58`.
    
    QinQ is also supported, we can create VLAN devices above another VLAN
    devices, like `eth0.100.101` (or even `eth0.1.2.3.4`, also supported on
    Linux).
    
    Signed-off-by: Zhe Weng <[email protected]>
---
 .../components/drivers/special/net/index.rst       |   2 +-
 .../components/drivers/special/net/vlan.rst        |  17 +
 drivers/net/CMakeLists.txt                         |   4 +
 drivers/net/Make.defs                              |   4 +
 drivers/net/netdev_upperhalf.c                     | 233 ++++++++++++-
 drivers/net/vlan.c                                 | 362 +++++++++++++++++++++
 include/nuttx/net/netdev_lowerhalf.h               |  38 +++
 include/nuttx/net/vlan.h                           | 107 ++++++
 net/Kconfig                                        |  21 ++
 9 files changed, 775 insertions(+), 13 deletions(-)

diff --git a/Documentation/components/drivers/special/net/index.rst 
b/Documentation/components/drivers/special/net/index.rst
index 483b70b4a12..03ec5461bae 100644
--- a/Documentation/components/drivers/special/net/index.rst
+++ b/Documentation/components/drivers/special/net/index.rst
@@ -11,4 +11,4 @@ Network interface drivers
   :caption: Supported Drivers
 
   ethernet.rst
-
+  vlan.rst
diff --git a/Documentation/components/drivers/special/net/vlan.rst 
b/Documentation/components/drivers/special/net/vlan.rst
new file mode 100644
index 00000000000..186f6d27091
--- /dev/null
+++ b/Documentation/components/drivers/special/net/vlan.rst
@@ -0,0 +1,17 @@
+===================
+Vlan Device Drivers
+===================
+
+-  ``include/nuttx/net/vlan.h``. All structures and APIs
+   needed to work with Vlan drivers are provided in this
+   header file.
+-  we also create VLAN devices for managing VLAN, which will become
+   interfaces like ``eth0.58``
+-  QinQ is also supported, we can create VLAN devices above another VLAN
+   devices, like ``eth0.100.101`` (or even ``eth0.1.2.3.4``, also supported
+   on Linux).
+-  Supporting ADD_VLAN_CMD and DEL_VLAN_CMD of SIOCSIFVLAN.
+-  We add default PCP because some of our apps may not want to set
+   PCP manually
+
+-  **Driver**: ``drivers/net/vlan.c``
diff --git a/drivers/net/CMakeLists.txt b/drivers/net/CMakeLists.txt
index 5120c29d4d1..c8b9fcf1466 100644
--- a/drivers/net/CMakeLists.txt
+++ b/drivers/net/CMakeLists.txt
@@ -65,6 +65,10 @@ if(CONFIG_NET)
     list(APPEND SRCS tun.c)
   endif()
 
+  if(CONFIG_NET_VLAN)
+    list(APPEND SRCS vlan.c)
+  endif()
+
   if(CONFIG_NET_FTMAC100)
     list(APPEND SRCS ftmac100.c)
   endif()
diff --git a/drivers/net/Make.defs b/drivers/net/Make.defs
index a5f18c71276..9dc64e5c641 100644
--- a/drivers/net/Make.defs
+++ b/drivers/net/Make.defs
@@ -66,6 +66,10 @@ ifeq ($(CONFIG_NET_TUN),y)
   CSRCS += tun.c
 endif
 
+ifeq ($(CONFIG_NET_VLAN),y)
+  CSRCS += vlan.c
+endif
+
 ifeq ($(CONFIG_NET_FTMAC100),y)
   CSRCS += ftmac100.c
 endif
diff --git a/drivers/net/netdev_upperhalf.c b/drivers/net/netdev_upperhalf.c
index e102c0b9c6f..3c6bd282bae 100644
--- a/drivers/net/netdev_upperhalf.c
+++ b/drivers/net/netdev_upperhalf.c
@@ -39,6 +39,7 @@
 #include <nuttx/net/net.h>
 #include <nuttx/net/netdev_lowerhalf.h>
 #include <nuttx/net/pkt.h>
+#include <nuttx/net/vlan.h>
 #include <nuttx/semaphore.h>
 #include <nuttx/spinlock.h>
 
@@ -66,6 +67,14 @@
  * Private Types
  ****************************************************************************/
 
+#ifdef CONFIG_NET_VLAN
+struct netdev_vlan_entry_s
+{
+  FAR struct netdev_lowerhalf_s *dev;
+  uint16_t vid;
+};
+#endif
+
 /* This structure describes the state of the upper half driver */
 
 struct netdev_upperhalf_s
@@ -87,8 +96,18 @@ struct netdev_upperhalf_s
 #if CONFIG_IOB_NCHAINS > 0
   struct iob_queue_s txq;
 #endif
+
+#ifdef CONFIG_NET_VLAN
+  struct netdev_vlan_entry_s vlan[CONFIG_NET_VLAN_COUNT];
+#endif
 };
 
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int netdev_upper_txavail(FAR struct net_driver_s *dev);
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -400,6 +419,7 @@ static void netdev_upper_queue_tx(FAR struct net_driver_s 
*dev)
   if ((ret = iob_tryadd_queue(dev->d_iob, &upper->txq)) >= 0)
     {
       netdev_iob_clear(dev);
+      netdev_upper_txavail(dev);
     }
   else
     {
@@ -413,6 +433,66 @@ static void netdev_upper_queue_tx(FAR struct net_driver_s 
*dev)
 }
 #endif
 
+/****************************************************************************
+ * Name: netdev_upper_vlan_dev
+ *
+ * Description:
+ *   Get the VLAN device for the VID.
+ *
+ * Input Parameters:
+ *   upper - Reference to the upper half driver structure
+ *   vid   - VLAN ID
+ *
+ * Returned Value:
+ *   Reference to the network device structure, or NULL if not found.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_VLAN
+static FAR struct netdev_lowerhalf_s *
+netdev_upper_vlan_dev(FAR struct netdev_upperhalf_s *upper, uint16_t vid)
+{
+  int i;
+
+  for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++)
+    {
+      if (upper->vlan[i].vid == vid)
+        {
+          return upper->vlan[i].dev;
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: netdev_upper_vlan_foreach
+ *
+ * Description:
+ *   Call the callback for each VLAN device.
+ *
+ * Input Parameters:
+ *   upper - Reference to the upper half driver structure
+ *   cb    - The callback function
+ *
+ ****************************************************************************/
+
+static void
+netdev_upper_vlan_foreach(FAR struct netdev_upperhalf_s *upper,
+                          CODE void (*cb)(FAR struct netdev_lowerhalf_s *))
+{
+  int i;
+
+  for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++)
+    {
+      if (upper->vlan[i].dev)
+        {
+          cb(upper->vlan[i].dev);
+        }
+    }
+}
+#endif
+
 /****************************************************************************
  * Name: eth_input
  *
@@ -435,21 +515,45 @@ static void eth_input(FAR struct net_driver_s *dev)
 
   /* Check if this is an 802.1Q VLAN tagged packet */
 
-  if (eth_hdr->type == HTONS(TPID_8021QVLAN))
+#ifdef CONFIG_NET_VLAN
+  if (eth_hdr->type == HTONS(ETHERTYPE_VLAN))
     {
-      /* Need to remove the 4 octet VLAN Tag, by moving src and dest
-       * addresses 4 octets to the right, and then read the actual
-       * ethertype. The VLAN ID and priority fields are currently
-       * ignored.
-       */
+      FAR struct netdev_upperhalf_s *upper = dev->d_private;
+      FAR struct netdev_lowerhalf_s *vlan;
+      FAR struct eth_8021qhdr_s *vlan_hdr = NETLLBUF;
+      uint16_t vid = NTOHS(vlan_hdr->tci) & VLAN_VID_MASK;
+
+      vlan = netdev_upper_vlan_dev(upper, vid);
+      if (vlan)
+        {
+          /* Need to remove the 4 octet VLAN Tag, by moving src and dest
+           * addresses 4 octets to the right, and then read the actual
+           * ethertype.
+           */
+
+          memmove((FAR uint8_t *)eth_hdr + 4, eth_hdr,
+                  offsetof(struct eth_hdr_s, type));
+          netdev_iob_release(&vlan->netdev);
+          vlan->netdev.d_iob = iob_trimhead(dev->d_iob, 4);
+          vlan->netdev.d_len = dev->d_len - 4;
+          netdev_iob_clear(dev);
 
-      memmove((FAR uint8_t *)eth_hdr + 4, eth_hdr,
-              offsetof(struct eth_hdr_s, type));
-      dev->d_iob  = iob_trimhead(dev->d_iob, 4);
-      dev->d_len -= 4;
+          /* Then call eth_input again with the new dev */
 
-      eth_hdr = (FAR struct eth_hdr_s *)NETLLBUF;
+#ifdef CONFIG_NET_PKT
+          pkt_input(&vlan->netdev);
+#endif
+          eth_input(&vlan->netdev);
+        }
+      else
+        {
+          ninfo("INFO: Dropped, unknown vlan id: %d\n", vid);
+          NETDEV_RXDROPPED(dev);
+          dev->d_len = 0;
+        }
     }
+  else
+#endif
 
   /* We only accept IP packets of the configured type and ARP packets */
 
@@ -1270,6 +1374,11 @@ int netdev_lower_unregister(FAR struct 
netdev_lowerhalf_s *dev)
     }
 
   upper = (FAR struct netdev_upperhalf_s *)dev->netdev.d_private;
+
+#ifdef CONFIG_NET_VLAN
+  netdev_upper_vlan_foreach(upper, vlan_unregister);
+#endif
+
   ret = netdev_unregister(&dev->netdev);
   if (ret < 0)
     {
@@ -1317,6 +1426,11 @@ int netdev_lower_unregister(FAR struct 
netdev_lowerhalf_s *dev)
 
 void netdev_lower_carrier_on(FAR struct netdev_lowerhalf_s *dev)
 {
+#ifdef CONFIG_NET_VLAN
+  FAR struct netdev_upperhalf_s *upper = dev->netdev.d_private;
+  netdev_upper_vlan_foreach(upper, netdev_lower_carrier_on);
+#endif
+
   netdev_carrier_on(&dev->netdev);
 }
 
@@ -1334,6 +1448,11 @@ void netdev_lower_carrier_on(FAR struct 
netdev_lowerhalf_s *dev)
 
 void netdev_lower_carrier_off(FAR struct netdev_lowerhalf_s *dev)
 {
+#ifdef CONFIG_NET_VLAN
+  FAR struct netdev_upperhalf_s *upper = dev->netdev.d_private;
+  netdev_upper_vlan_foreach(upper, netdev_lower_carrier_off);
+#endif
+
   netdev_carrier_off(&dev->netdev);
 }
 
@@ -1351,6 +1470,10 @@ void netdev_lower_carrier_off(FAR struct 
netdev_lowerhalf_s *dev)
 void netdev_lower_rxready(FAR struct netdev_lowerhalf_s *dev)
 {
 #if CONFIG_NETDEV_WORK_THREAD_POLLING_PERIOD == 0
+  /* Note: Don't need to handle VLAN here, because RX of VLAN is handled in
+   * eth_input.
+   */
+
   netdev_upper_queue_work(&dev->netdev);
 #endif
 }
@@ -1368,12 +1491,98 @@ void netdev_lower_rxready(FAR struct netdev_lowerhalf_s 
*dev)
 
 void netdev_lower_txdone(FAR struct netdev_lowerhalf_s *dev)
 {
-  NETDEV_TXDONE(&dev->netdev);
 #if CONFIG_NETDEV_WORK_THREAD_POLLING_PERIOD == 0
+#  ifdef CONFIG_NET_VLAN
+  FAR struct netdev_upperhalf_s *upper = dev->netdev.d_private;
+  netdev_upper_vlan_foreach(upper, netdev_lower_txdone);
+#  endif
+
   netdev_upper_queue_work(&dev->netdev);
 #endif
+  NETDEV_TXDONE(&dev->netdev);
 }
 
+/****************************************************************************
+ * Name: netdev_lower_vlan_add
+ *
+ * Description:
+ *   Add a VLAN device to the network device.
+ *
+ * Input Parameters:
+ *   dev  - The lower half device driver structure
+ *   vid  - VLAN ID
+ *   vlan - The VLAN device to add
+ *
+ * Returned Value:
+ *   0:Success; negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_VLAN
+int netdev_lower_vlan_add(FAR struct netdev_lowerhalf_s *dev, uint16_t vid,
+                          FAR struct netdev_lowerhalf_s *vlan)
+{
+  FAR struct netdev_upperhalf_s  *upper = dev->netdev.d_private;
+  FAR struct netdev_vlan_entry_s *entry = NULL;
+  int i;
+
+  for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++)
+    {
+      if (upper->vlan[i].vid == vid)
+        {
+          return -EEXIST;
+        }
+
+      if (upper->vlan[i].vid == 0 && entry == NULL)
+        {
+          entry = &upper->vlan[i];
+        }
+    }
+
+  if (entry)
+    {
+      entry->vid = vid;
+      entry->dev = vlan;
+      return OK;
+    }
+
+  return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: netdev_lower_vlan_del
+ *
+ * Description:
+ *   Delete a VLAN device from the network device.
+ *
+ * Input Parameters:
+ *   dev - The lower half device driver structure
+ *   vid - VLAN ID
+ *
+ * Returned Value:
+ *   0:Success; negated errno on failure
+ *
+ ****************************************************************************/
+
+int netdev_lower_vlan_del(FAR struct netdev_lowerhalf_s *dev, uint16_t vid)
+{
+  FAR struct netdev_upperhalf_s *upper = dev->netdev.d_private;
+  int i;
+
+  for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++)
+    {
+      if (upper->vlan[i].vid == vid)
+        {
+          upper->vlan[i].vid = 0;
+          upper->vlan[i].dev = NULL;
+          return OK;
+        }
+    }
+
+  return -ENOENT;
+}
+#endif
+
 /****************************************************************************
  * Name: netpkt_alloc
  *
diff --git a/drivers/net/vlan.c b/drivers/net/vlan.c
new file mode 100644
index 00000000000..0b0d33f3609
--- /dev/null
+++ b/drivers/net/vlan.c
@@ -0,0 +1,362 @@
+/****************************************************************************
+ * drivers/net/vlan.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>
+
+#ifdef CONFIG_NET_VLAN
+
+#include <debug.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/net/netdev_lowerhalf.h>
+#include <nuttx/net/vlan.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define VLAN_DEV_NAME_FMT "%s.%i"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct vlan_device_s
+{
+  struct netdev_lowerhalf_s dev;
+
+  FAR struct netdev_lowerhalf_s *real;
+  uint16_t vid;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int vlan_ifup(FAR struct netdev_lowerhalf_s *dev);
+static int vlan_ifdown(FAR struct netdev_lowerhalf_s *dev);
+static int vlan_transmit(FAR struct netdev_lowerhalf_s *dev,
+                         FAR netpkt_t *pkt);
+static FAR netpkt_t *vlan_receive(FAR struct netdev_lowerhalf_s *dev);
+#ifdef CONFIG_NET_MCASTGROUP
+static int vlan_addmac(FAR struct netdev_lowerhalf_s *dev,
+                       FAR const uint8_t *mac);
+static int vlan_rmmac(FAR struct netdev_lowerhalf_s *dev,
+                      FAR const uint8_t *mac);
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+static int vlan_ioctl(FAR struct netdev_lowerhalf_s *dev, int cmd,
+                      unsigned long arg);
+#endif
+static void vlan_reclaim(FAR struct netdev_lowerhalf_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct netdev_ops_s g_vlan_ops =
+{
+  vlan_ifup,     /* ifup */
+  vlan_ifdown,   /* ifdown */
+  vlan_transmit, /* transmit */
+  vlan_receive,  /* receive */
+#ifdef CONFIG_NET_MCASTGROUP
+  vlan_addmac,   /* addmac */
+  vlan_rmmac,    /* rmmac */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  vlan_ioctl,    /* ioctl */
+#endif
+  vlan_reclaim   /* reclaim */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: vlan_ifup
+ ****************************************************************************/
+
+static int vlan_ifup(FAR struct netdev_lowerhalf_s *dev)
+{
+  FAR struct vlan_device_s      *vlan = (FAR struct vlan_device_s *)dev;
+  FAR struct netdev_lowerhalf_s *real = vlan->real;
+
+  if (IFF_IS_RUNNING(real->netdev.d_flags))
+    {
+      netdev_lower_carrier_on(dev);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: vlan_ifdown
+ ****************************************************************************/
+
+static int vlan_ifdown(FAR struct netdev_lowerhalf_s *dev)
+{
+  netdev_lower_carrier_off(dev);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: vlan_transmit
+ ****************************************************************************/
+
+static int vlan_transmit(FAR struct netdev_lowerhalf_s *dev,
+                         FAR netpkt_t *pkt)
+{
+  FAR struct vlan_device_s      *vlan = (FAR struct vlan_device_s *)dev;
+  FAR struct netdev_lowerhalf_s *real = vlan->real;
+  FAR struct eth_8021qhdr_s     *vlan_hdr;
+  FAR uint8_t                   *base = netpkt_getbase(pkt);
+  FAR uint8_t                   *data = netpkt_getdata(dev, pkt);
+
+  /* Check space for the VLAN tag, normally we want user to set bigger
+   * CONFIG_NET_LL_GUARDSIZE to allow inserting VLAN tag in the headroom.
+   */
+
+  if (data - base < 4)
+    {
+      /* TODO: Support backup path for too small CONFIG_NET_LL_GUARDSIZE */
+
+      nerr("ERROR: No headroom for VLAN tag, please enlarge "
+           "CONFIG_NET_LL_GUARDSIZE\n");
+      return -ENOMEM;
+    }
+
+  /* Move the data to make space for the VLAN tag */
+
+  netpkt_copyin(dev, pkt, data, offsetof(struct eth_hdr_s, type), -4);
+  netpkt_reset_reserved(dev, pkt, data - base - 4);
+
+  /* Set value of the VLAN tag */
+
+  vlan_hdr       = (FAR struct eth_8021qhdr_s *)netpkt_getdata(dev, pkt);
+  vlan_hdr->tpid = HTONS(TPID_8021QVLAN);
+  vlan_hdr->tci  = HTONS(vlan->vid);
+
+  /* Transmit the packet on the real device */
+
+  /* TODO: Call pkt_input to allow tcpdump capture tx packet on real dev. */
+
+  return real->ops->transmit(real, pkt);
+}
+
+/****************************************************************************
+ * Name: vlan_receive
+ ****************************************************************************/
+
+static FAR netpkt_t *vlan_receive(FAR struct netdev_lowerhalf_s *dev)
+{
+  /* VLAN device doesn't receive packets, the real device does. */
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: vlan_addmac
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int vlan_addmac(FAR struct netdev_lowerhalf_s *dev,
+                       FAR const uint8_t *mac)
+{
+  FAR struct vlan_device_s      *vlan = (FAR struct vlan_device_s *)dev;
+  FAR struct netdev_lowerhalf_s *real = vlan->real;
+
+  if (real->ops->addmac)
+    {
+      return real->ops->addmac(real, mac);
+    }
+
+  return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: vlan_rmmac
+ ****************************************************************************/
+
+static int vlan_rmmac(FAR struct netdev_lowerhalf_s *dev,
+                      FAR const uint8_t *mac)
+{
+  FAR struct vlan_device_s      *vlan = (FAR struct vlan_device_s *)dev;
+  FAR struct netdev_lowerhalf_s *real = vlan->real;
+
+  if (real->ops->rmmac)
+    {
+      return real->ops->rmmac(real, mac);
+    }
+
+  return -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: vlan_ioctl
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int vlan_ioctl(FAR struct netdev_lowerhalf_s *dev, int cmd,
+                      unsigned long arg)
+{
+  FAR struct vlan_device_s      *vlan = (FAR struct vlan_device_s *)dev;
+  FAR struct netdev_lowerhalf_s *real = vlan->real;
+
+  /* TODO: Maybe we should only pass some of IOCTL commands. */
+
+  if (real->ops->ioctl)
+    {
+      return real->ops->ioctl(real, cmd, arg);
+    }
+
+  return -ENOTTY;
+}
+#endif
+
+/****************************************************************************
+ * Name: vlan_reclaim
+ ****************************************************************************/
+
+static void vlan_reclaim(FAR struct netdev_lowerhalf_s *dev)
+{
+  FAR struct vlan_device_s      *vlan = (FAR struct vlan_device_s *)dev;
+  FAR struct netdev_lowerhalf_s *real = vlan->real;
+
+  real->ops->reclaim(real);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: vlan_register
+ *
+ * Description:
+ *   Create a new VLAN device and register it.
+ *
+ * Input Parameters:
+ *   real - The real device to which the VLAN is attached
+ *   vid  - VLAN ID
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int vlan_register(FAR struct netdev_lowerhalf_s *real, uint16_t vid)
+{
+  FAR struct vlan_device_s *vlan;
+  char vlanifname[IFNAMSIZ + 8];
+  int ret;
+
+  if (real == NULL || real->netdev.d_lltype != NET_LL_ETHERNET)
+    {
+      return -EINVAL;
+    }
+
+  /* Create a new VLAN device */
+
+  vlan = kmm_zalloc(sizeof(struct vlan_device_s));
+  if (vlan == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* Init the VLAN device */
+
+  vlan->vid           = vid;
+  vlan->real          = real;
+  vlan->dev.quota_ptr = real->quota_ptr;
+  vlan->dev.ops       = &g_vlan_ops;
+
+  /* Set the VLAN device name, use a buffer to make compiler happy */
+
+  snprintf(vlanifname, sizeof(vlanifname), VLAN_DEV_NAME_FMT,
+           real->netdev.d_ifname, vid);
+  strlcpy(vlan->dev.netdev.d_ifname, vlanifname, IFNAMSIZ);
+
+  /* Copy the MAC address from the real device */
+
+  memcpy(vlan->dev.netdev.d_mac.ether.ether_addr_octet,
+         real->netdev.d_mac.ether.ether_addr_octet, IFHWADDRLEN);
+
+  /* Register the VLAN device */
+
+  ret = netdev_lower_vlan_add(real, vid, &vlan->dev);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  ret = netdev_lower_register(&vlan->dev, NET_LL_ETHERNET);
+  if (ret < 0)
+    {
+      netdev_lower_vlan_del(real, vid);
+      goto errout;
+    }
+
+  return ret;
+
+errout:
+  kmm_free(vlan);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: vlan_unregister
+ *
+ * Description:
+ *   Unregister a VLAN device.
+ *
+ * Input Parameters:
+ *   dev - The VLAN device to be unregistered.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void vlan_unregister(FAR struct netdev_lowerhalf_s *dev)
+{
+  FAR struct vlan_device_s *vlan = (FAR struct vlan_device_s *)dev;
+
+  if (vlan == NULL)
+    {
+      return;
+    }
+
+  netdev_lower_vlan_del(vlan->real, vlan->vid);
+  netdev_lower_unregister(dev);
+  kmm_free(dev);
+}
+
+#endif /* CONFIG_NET_VLAN */
diff --git a/include/nuttx/net/netdev_lowerhalf.h 
b/include/nuttx/net/netdev_lowerhalf.h
index 3f7785e23c3..dcc52e56e6f 100644
--- a/include/nuttx/net/netdev_lowerhalf.h
+++ b/include/nuttx/net/netdev_lowerhalf.h
@@ -323,6 +323,44 @@ void netdev_lower_txdone(FAR struct netdev_lowerhalf_s 
*dev);
 
 #define netdev_lower_quota_load(dev, type) atomic_read(&dev->quota[type])
 
+/****************************************************************************
+ * Name: netdev_lower_vlan_add
+ *
+ * Description:
+ *   Add a VLAN device to the network device.
+ *
+ * Input Parameters:
+ *   dev  - The lower half device driver structure
+ *   vid  - VLAN ID
+ *   vlan - The VLAN device to add
+ *
+ * Returned Value:
+ *   0:Success; negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_VLAN
+int netdev_lower_vlan_add(FAR struct netdev_lowerhalf_s *dev, uint16_t vid,
+                          FAR struct netdev_lowerhalf_s *vlan);
+
+/****************************************************************************
+ * Name: netdev_lower_vlan_del
+ *
+ * Description:
+ *   Delete a VLAN device from the network device.
+ *
+ * Input Parameters:
+ *   dev - The lower half device driver structure
+ *   vid - VLAN ID
+ *
+ * Returned Value:
+ *   0:Success; negated errno on failure
+ *
+ ****************************************************************************/
+
+int netdev_lower_vlan_del(FAR struct netdev_lowerhalf_s *dev, uint16_t vid);
+#endif
+
 /****************************************************************************
  * Name: netpkt_alloc
  *
diff --git a/include/nuttx/net/vlan.h b/include/nuttx/net/vlan.h
new file mode 100644
index 00000000000..1085d540534
--- /dev/null
+++ b/include/nuttx/net/vlan.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+ * include/nuttx/net/vlan.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_NET_VLAN_H
+#define __INCLUDE_NUTTX_NET_VLAN_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/net/netdev_lowerhalf.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* VLAN tag definitions */
+
+#define VLAN_PRIO_MASK  0xe000 /* Priority Code Point */
+#define VLAN_PRIO_SHIFT 13
+#define VLAN_CFI_MASK   0x1000 /* Canonical Format Indicator / Drop Eligible 
Indicator */
+#define VLAN_VID_MASK   0x0fff /* VLAN Identifier */
+#define VLAN_N_VID      4096
+
+#ifdef CONFIG_NET_VLAN
+
+/****************************************************************************
+ * Public Type Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: vlan_register
+ *
+ * Description:
+ *   Create a new VLAN device and register it.
+ *
+ * Input Parameters:
+ *   real - The real device to which the VLAN is attached
+ *   vid  - VLAN ID
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int vlan_register(FAR struct netdev_lowerhalf_s *real, uint16_t vid);
+
+/****************************************************************************
+ * Name: vlan_unregister
+ *
+ * Description:
+ *   Unregister a VLAN device.
+ *
+ * Input Parameters:
+ *   dev - The VLAN device to be unregistered.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void vlan_unregister(FAR struct netdev_lowerhalf_s *dev);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_NET_VLAN */
+#endif /* __INCLUDE_NUTTX_NET_VLAN_H */
diff --git a/net/Kconfig b/net/Kconfig
index 10ce0b23017..1e158c9db40 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -138,6 +138,7 @@ config NET_GUARDSIZE
 config NET_LL_GUARDSIZE
        int "Data Link Layer(L2) Guard size of Network buffer(IOB)"
        default 50 if RNDIS
+       default 18 if NET_VLAN
        default 16 if NET_CAN && NET_TIMESTAMP
        default 14 if NET_ETHERNET
        default 0
@@ -296,6 +297,26 @@ config NET_TUN_PKTSIZE
 
 endif # NET_TUN
 
+menuconfig NET_VLAN
+       bool "VLAN support"
+       default n
+       depends on NET_ETHERNET
+       ---help---
+               Enable support for VLANs (Virtual Local Area Networks) on 
Ethernet
+               devices.
+
+if NET_VLAN
+
+config NET_VLAN_COUNT
+       int "MAX number of VLAN interfaces per physical interface"
+       default 2
+       range 1 32
+       ---help---
+               Selects the number of VLAN interfaces per physical interface to 
support.
+               Default: 2
+
+endif # NET_VLAN
+
 config NETDEV_LATEINIT
        bool "Late driver initialization"
        default n

Reply via email to