You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/11/24 04:40:53 UTC

[GitHub] [incubator-nuttx] masayuki2009 opened a new pull request, #7673: Add virtio-mmio and virtio-net drivers

masayuki2009 opened a new pull request, #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673

   ## Summary
   
   - This PR adds virtio-mmio and virtio-net drivers
   - NOTE: virtnet_txdone needs to be implemented later
   
   ## Impact
   
   - None (new features)
   
   ## Testing
   
   - Tested iperf and telnetd with the following defconfigs on QEMU-7.1
   - rv-virt:netnsh, rv-virt:netnsh_smp, rv-virt:netnsh64
   - qemu-armv8a:netnsh, qemu-armv8a:netnsh_smp
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] xiaoxiang781216 commented on pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#issuecomment-1326019108

   @masayuki2009 since https://github.com/apache/incubator-nuttx/tree/master/openamp also contain virtio implementation, we need consider how to unify both part. BTW, OpenAMP is adding virito driver support:
   https://github.com/OpenAMP/open-amp/issues/390
   https://github.com/OpenAMP/open-amp/tree/virtio-exp
   https://github.com/OpenAMP/open-amp/pulls?q=author%3Adanmilea+
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#discussion_r1031195267


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,33 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	default n
+
+menuconfig DRIVERS_VIRTIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0

Review Comment:
   why not let caller pass the address to virtio_init to resolve this issue? I think it's better to address the architecture issue in early stage as much as possible.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#discussion_r1031193535


##########
boards/arm64/qemu/qemu-armv8a/src/qemu_virtio.c:
##########
@@ -0,0 +1,41 @@
+/****************************************************************************
+ * boards/arm64/qemu/qemu-armv8a/src/qemu_virtio.c
+ *
+ * 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 <syslog.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/board.h>
+#include <nuttx/virtio/virtio.h>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void arm64_netinitialize(void)
+{
+  virtio_init();

Review Comment:
   it's better to move initialize out of arm64_netinitialize since other virtio driver also initialize in virito_init in the feature, I think.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034210809


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,47 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+if DRIVERS_VIRTIO
+menuconfig DRIVERS_VIRTIO_MMIO_NUM
+	int "The number of virtio mmio devices"
+	default 1
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_REGSIZE
+	hex "Virtio-mmio register size"
+	default 0x0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_IRQ
+	int "Virtio-mmio irq number"
+	default 0

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1033242533


##########
drivers/virtio/virtio-mmio.c:
##########
@@ -0,0 +1,226 @@
+/****************************************************************************
+ * drivers/virtio/virtio-mmio.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/virtio/virtio.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+#  include <nuttx/virtio/virtio-net.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ptr_to_uint64(x)  ((uint64_t)(uintptr_t)(x))
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtq_create
+ ****************************************************************************/
+
+FAR struct virtqueue *virtq_create(uint32_t len)
+{
+  FAR struct virtqueue *virtq = (FAR struct virtqueue *)
+    kmm_zalloc(sizeof(struct virtqueue));
+  ASSERT(virtq);
+
+  virtq->len = len;
+
+  /* See: 2.6 Split Virtqueues */
+
+  virtq->desc  = (FAR struct virtqueue_desc *)
+    kmm_memalign(16, 16 * len);
+  ASSERT(virtq->desc);
+
+  virtq->avail = (FAR struct virtqueue_avail *)
+    kmm_memalign(2, 6 + 2 * len);
+  ASSERT(virtq->avail);
+
+  virtq->used  = (FAR struct virtqueue_used *)
+    kmm_memalign(4, 6 + 8 * len);
+  ASSERT(virtq->used);
+
+  virtq->desc_virt = (FAR void **)
+    kmm_memalign(16, sizeof(FAR void *) * len);
+  ASSERT(virtq->desc_virt);
+
+  _info("virtq=%p (len=%" PRId32 ")\n", virtq, len);
+  _info("virtq->desc=%p \n", virtq->desc);
+  _info("virtq->avail=%p \n", virtq->avail);
+  _info("virtq->used=%p \n", virtq->used);
+
+  virtq->avail->idx = 0;
+  virtq->used->idx = 0;
+  virtq->last_used_idx = 0;
+
+  return virtq;
+}
+
+/****************************************************************************
+ * Name: virtq_dev_init
+ ****************************************************************************/
+
+static int virtio_dev_init(uintptr_t virt, uint32_t irq)
+{
+  FAR struct virtio_mmio_regs *regs = (FAR struct virtio_mmio_regs *)virt;
+  int ret = -1;
+  uint32_t val;
+
+  _info("examine virtio at 0x%" PRIxPTR "\n", virt);
+
+  val = getreg32(&regs->magic_value);
+
+  if (VIRTIO_MAGIC != val)
+    {
+      _err("error: virtio at 0x%" PRIxPTR
+           " had wrong magic value 0x%" PRIx32 "\n", virt, val);
+      return ret;
+    }
+
+  val = getreg32(&regs->version);
+
+  if (VIRTIO_VERSION != val)
+    {
+      _err("error: virtio at 0x%" PRIxPTR
+           " had wrong version 0x%" PRIx32 "\n", virt, val);
+      return ret;
+    }
+
+  /* Reset */
+
+  putreg32(0, &regs->status);
+  mb();
+
+  /* Set ack */
+
+  val = getreg32(&regs->status) | VIRTIO_STATUS_ACKNOWLEDGE;
+  putreg32(val, &regs->status);
+  mb();
+
+  /* Set driver */
+
+  val = getreg32(&regs->status) | VIRTIO_STATUS_DRIVER;
+  putreg32(val, &regs->status);
+  mb();
+
+  /* Check the device_id */
+
+  val = getreg32(&regs->device_id);
+
+  switch (val)
+    {
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+    case VIRTIO_DEV_NET:
+      ret = virtnet_init(regs, irq);
+      break;
+#endif
+    default:
+      _warn("unsupported device_id 0x%" PRIx32 "\n", val);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtq_alloc_desc
+ ****************************************************************************/
+
+uint32_t virtq_alloc_desc(FAR struct virtqueue *virtq,
+                          uint32_t idx, FAR void *addr)
+{
+  uint32_t id = idx % virtq->len;
+
+  _info("virtq=%p, idx=%" PRId32 "\n", virtq, idx);
+
+  virtq->desc[id].addr = (uintptr_t)addr;
+  virtq->desc_virt[id] = addr;
+  return id;
+}
+
+/****************************************************************************
+ * Name: virtq_add_to_device
+ ****************************************************************************/
+
+void virtq_add_to_device(FAR struct virtio_mmio_regs *regs,
+                         FAR struct virtqueue *virtq,
+                         uint32_t queue_sel)
+{
+  _info("==== queue_sel=%" PRId32 ", virtq->len=%" PRId32 "\n",
+        queue_sel, virtq->len);
+
+  putreg32(queue_sel, &regs->queue_sel);
+  mb();
+
+  putreg32(virtq->len, &regs->queue_num);
+
+  putreg32((uint32_t)(ptr_to_uint64(virtq->desc)),
+           &regs->queue_desc_low);
+  putreg32((uint32_t)(ptr_to_uint64(virtq->desc) >> 32),
+           &regs->queue_desc_high);
+
+  putreg32((uint32_t)(ptr_to_uint64(virtq->avail)),
+           &regs->queue_avail_low);
+  putreg32((uint32_t)(ptr_to_uint64(virtq->avail) >> 32),
+           &regs->queue_avail_high);
+
+  putreg32((uint32_t)(ptr_to_uint64(virtq->used)),
+           &regs->queue_used_low);
+  putreg32((uint32_t)(ptr_to_uint64(virtq->used) >> 32),
+           &regs->queue_used_high);
+
+  mb();
+  putreg32(1, &regs->queue_ready);
+}
+
+/****************************************************************************
+ * Name: virtio_init
+ ****************************************************************************/
+
+void virtio_init(void)
+{
+  uintptr_t virtio = (uintptr_t)CONFIG_DRIVERS_VIRTIO_BASE;
+  uint32_t  irq = CONFIG_DRIVERS_VIRTIO_IRQ;
+  uint32_t  size = CONFIG_DRIVERS_VIRTIO_REGSIZE;
+  uint32_t  i;
+
+  for (i = 0; i < CONFIG_DRIVERS_VIRTIO_NUM; i++)
+    {
+      virtio_dev_init(virtio + size * i, irq + i);

Review Comment:
   Yes



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#discussion_r1031949542


##########
boards/arm64/qemu/qemu-armv8a/src/qemu_virtio.c:
##########
@@ -0,0 +1,41 @@
+/****************************************************************************
+ * boards/arm64/qemu/qemu-armv8a/src/qemu_virtio.c

Review Comment:
   Done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] masayuki2009 commented on pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#issuecomment-1326953244

   @xiaoxiang781216 @qinwei2004 
   I've added multiple mmio devices support to `virtio_init()` in drivers/virtio-mmio.c
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#issuecomment-1329958673

   @xiaoxiang781216 
   I've just rebased this PR to the latest upstream and fixed the code you pointed out.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034210913


##########
drivers/virtio/virtio-net.c:
##########
@@ -0,0 +1,1340 @@
+/****************************************************************************
+ * drivers/virtio/virtio-net.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/virtio/virtio-mmio.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEBUG
+
+/* 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 ETHWORK LPWORK
+
+/* VIRTIO_NET_NINTERFACES determines the number of
+ * physical interfaces that will be supported.
+ */
+
+#ifndef VIRTIO_NET_NINTERFACES
+# define VIRTIO_NET_NINTERFACES 1
+#endif
+
+/* TX timeout = 1 minute */
+
+#define VIRTNET_TXTIMEOUT (60*CLK_TCK)
+
+/* Packet buffer size */
+
+#define PKTBUF_SIZE (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE)
+
+/* This is a helper pointer for accessing the contents of Ethernet header */
+
+#define BUF ((struct eth_hdr_s *)priv->vnet_dev.d_buf)
+
+/* virtio net related definition */
+
+#define VIRTIO_NET_HDRLEN 10
+
+#define VIRTIO_NET_Q_RX 0
+#define VIRTIO_NET_Q_TX 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct virtnet_hdr_s
+{
+  uint8_t  flags;
+  uint8_t  gso_type;
+  uint16_t hdr_len;
+  uint16_t gso_size;
+  uint16_t csum_start;
+  uint16_t csum_offset;
+};
+
+/* The virtnet_driver_s encapsulates all state information for
+ * a single hardware interface
+ */
+
+struct virtnet_driver_s
+{
+  bool vnet_bifup;               /* true:ifup false:ifdown */
+  int  irq;
+
+  FAR struct virtio_mmio_regs *regs; /* virtio_mmio registers */
+  FAR struct virtqueue   *txq;       /* TX queue */
+  FAR struct virtqueue   *rxq;       /* RX queue */
+
+  struct wdog_s vnet_txtimeout;  /* TX timeout timer */
+  struct work_s vnet_irqwork;    /* For deferring interrupt work to the work queue */
+  struct work_s vnet_pollwork;   /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s vnet_dev;  /* Interface understood by the network */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* These statically allocated structures would mean that only a single
+ * instance of the device could be supported.  In order to support multiple
+ * devices instances, this data would have to be allocated dynamically.
+ */
+
+/* A single packet buffer per device is used in this example.  There might
+ * be multiple packet buffers in a more complex, pipelined design.  Many
+ * contemporary Ethernet interfaces, for example,  use multiple, linked DMA
+ * descriptors in rings to implement such a pipeline.  This example assumes
+ * much simpler hardware that simply handles one packet at a time.
+ *
+ * NOTE that if VIRTIO_NET_NINTERFACES were greater than 1,
+ * you would need a minimum on one packet buffer per instance.
+ * Much better to be allocated dynamically in cases where more than
+ * one are needed.
+ */
+
+static uint16_t
+  g_pktbuf[VIRTIO_NET_NINTERFACES][(PKTBUF_SIZE + 1) / 2];
+
+/* Driver state structure */
+
+static struct virtnet_driver_s g_virtnet[VIRTIO_NET_NINTERFACES];
+
+static uint32_t g_ninterfaces = 0;
+
+/* Common virtnet header */
+
+static struct virtnet_hdr_s g_hdr;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int  virtnet_transmit(FAR struct virtnet_driver_s *priv);
+static int  virtnet_txpoll(FAR struct net_driver_s *dev);
+
+/* Interrupt handling */
+
+static void virtnet_reply(FAR struct virtnet_driver_s *priv);
+static void virtnet_receive(FAR struct virtnet_driver_s *priv);
+
+#ifdef TODO
+static void virtnet_txdone(FAR struct virtnet_driver_s *priv);
+#endif
+
+static void virtnet_interrupt_work(FAR void *arg);
+static int  virtnet_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/* Watchdog timer expirations */
+
+static void virtnet_txtimeout_work(FAR void *arg);
+static void virtnet_txtimeout_expiry(FAR wdparm_t arg);
+
+/* NuttX callback functions */
+
+static int  virtnet_ifup(FAR struct net_driver_s *dev);
+static int  virtnet_ifdown(FAR struct net_driver_s *dev);
+
+static void virtnet_txavail_work(FAR void *arg);
+static int  virtnet_txavail(FAR struct net_driver_s *dev);
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int  virtnet_addmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac);
+#ifdef CONFIG_NET_MCASTGROUP
+static int  virtnet_rmmac(FAR struct net_driver_s *dev,
+                          FAR const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_ICMPv6
+static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv);
+#endif
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+static int  virtnet_ioctl(FAR struct net_driver_s *dev, int cmd,
+                          unsigned long arg);
+#endif
+
+static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_cmd_status
+ ****************************************************************************/
+
+#ifdef DEBUG
+static void virtnet_cmd_status(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct virtio_mmio_regs *regs = priv->regs;
+
+  vrtinfo("virtnet at %p\n", regs);
+  vrtinfo("  status=0x%" PRIx32 "\n",
+          virtio_getreg32(&regs->status));
+  vrtinfo("  interrupt_status=0x%" PRIx32 "\n",
+          virtio_getreg32(&regs->interrupt_status));
+
+  virtio_putreg32(VIRTIO_NET_Q_TX, &regs->queue_sel);
+  mb();
+
+  vrtinfo("  txq: avail->idx=%d, used->idx=%d, ready=0x%" PRIx32 "\n",
+          priv->txq->avail->idx, priv->txq->used->idx,
+          virtio_getreg32(&regs->queue_ready));
+
+  virtio_putreg32(VIRTIO_NET_Q_RX, &regs->queue_sel);
+  mb();
+
+  vrtinfo("  rxq: avail->idx=%d used->idx=%d ready=0x%" PRIx32 "\n",
+          priv->rxq->avail->idx, priv->rxq->used->idx,
+          virtio_getreg32(&regs->queue_ready));
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_add_packets_to_rxq
+ ****************************************************************************/
+
+static void virtnet_add_packets_to_rxq(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct virtqueue *rxq = priv->rxq;
+  FAR uint8_t  *pkt;
+  uint16_t d1;
+  uint16_t d2;
+  uint32_t i;
+
+  vrtinfo("+++ rxq->len=%" PRId32 "  rxq->avail-idx=%d \n",
+          rxq->len, rxq->avail->idx);
+  DEBUGASSERT(rxq->avail->idx == 0);
+
+  for (i = 0; i < rxq->len / 2; i++)
+    {
+      pkt = kmm_memalign(16, PKTBUF_SIZE);
+      ASSERT(pkt);
+
+      /* Allocate new descriptors for header and packet */
+
+      d1 = virtq_alloc_desc(rxq, (i * 2),  (FAR void *)&g_hdr);
+      d2 = virtq_alloc_desc(rxq, (i * 2) + 1, pkt);
+
+      /* Set up the descriptor for header */
+
+      rxq->desc[d1].len   = VIRTIO_NET_HDRLEN;
+      rxq->desc[d1].flags = VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT;
+      rxq->desc[d1].next  = d2;
+
+      /* Set up the descriptor for packet */
+
+      rxq->desc[d2].len   = PKTBUF_SIZE;
+      rxq->desc[d2].flags = VIRTQ_DESC_F_WRITE;
+
+      /* Set the first descriptor to the avail->ring */
+
+      rxq->avail->ring[i] = d1;
+
+      /* Increment the avail->idx for each two descriptors */
+
+      mb();
+      rxq->avail->idx += 1;
+    }
+
+  vrtinfo("+++ virtq->avail-idx=%d \n", rxq->avail->idx);
+  vrtinfo("+++ virtq->used->idx=%d \n", rxq->used->idx);
+}
+
+/****************************************************************************
+ * Name: virtnet_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the txdone interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_transmit(FAR struct virtnet_driver_s *priv)
+{
+  FAR void *pkt = priv->vnet_dev.d_buf;
+  uint32_t len  = priv->vnet_dev.d_len;
+  uint16_t idx  = priv->txq->avail->idx;
+  uint16_t d1;
+  uint16_t d2;
+
+  /* Send the packet:
+   * address=priv->vnet_dev.d_buf, length=priv->vnet_dev.d_len
+   */
+
+  /* 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.
+   */
+
+  /* Increment statistics */
+
+  NETDEV_TXPACKETS(priv->vnet_dev);
+
+  vrtinfo("=== Sending packet, length: %d\n", priv->vnet_dev.d_len);
+
+#ifdef DEBUG
+  virtnet_cmd_status(priv);
+#endif
+
+  /* Allocate new descriptors for header and packet */
+
+  d1 = virtq_alloc_desc(priv->txq, idx, (FAR void *)&g_hdr);
+  d2 = virtq_alloc_desc(priv->txq, idx + 1, pkt);
+  DEBUGASSERT(d1 != d2);
+
+  /* Set up the descriptor for header */
+
+  priv->txq->desc[d2].len   = len;
+  priv->txq->desc[d2].flags = 0;
+
+  /* Set up the descriptor for packet */
+
+  priv->txq->desc[d1].len   = VIRTIO_NET_HDRLEN;
+  priv->txq->desc[d1].flags = VIRTQ_DESC_F_NEXT;
+  priv->txq->desc[d1].next  = d2;
+
+  /* Set the first descriptor to the avail->ring */
+
+  priv->txq->avail->ring[d1] = d1;
+
+  /* Increment the avail->idx for each two descriptors */
+
+  mb();
+  priv->txq->avail->idx += 1;
+
+  vrtinfo("*** d1=%d, d2=%d, txq->avail->idx=%d\n",
+          d1, d2, priv->txq->avail->idx);
+
+  virtio_putreg32(VIRTIO_NET_Q_TX, &priv->regs->queue_notify);
+
+  vrtinfo("*** finish updating queue_notify\n");
+
+#ifdef TODO
+  /* Enable Tx interrupts */
+#endif
+
+  /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->vnet_txtimeout, VIRTNET_TXTIMEOUT,
+           virtnet_txtimeout_expiry, (wdparm_t)priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txpoll
+ *
+ * Description:
+ *   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,
+ *   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
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_txpoll(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  vrtinfo("Poll result: d_len=%d\n", priv->vnet_dev.d_len);
+
+  /* Send the packet */
+
+  virtnet_reply(priv);
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: virtnet_reply
+ *
+ * Description:
+ *   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:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void virtnet_reply(FAR struct virtnet_driver_s *priv)
+{
+  /* 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 (priv->vnet_dev.d_len > 0)
+    {
+      /* And send the packet */
+
+      virtnet_transmit(priv);
+    }
+}
+
+/****************************************************************************
+ * Function: virtnet_rxdispatch
+ *
+ * Description:
+ *   A new Rx packet was received; dispatch that packet to the network layer
+ *   as necessary.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv)
+{
+  /* Update statistics */
+
+  NETDEV_RXPACKETS(&priv->dev);
+
+#ifdef CONFIG_NET_PKT
+  /* When packet sockets are enabled, feed the frame into the tap */
+
+  pkt_input(&priv->vnet_dev);
+#endif
+
+#ifdef CONFIG_NET_IPv4
+  /* Check for an IPv4 packet */
+
+  if (BUF->type == HTONS(ETHTYPE_IP))
+    {
+      vrtinfo("IPv4 frame\n");
+      NETDEV_RXIPV4(&priv->vnet_dev);
+
+      /* Receive an IPv4 packet from the network device */
+
+      vrtinfo("+++ call ipv4_input() d_len=%d\n", priv->vnet_dev.d_len);
+      ipv4_input(&priv->vnet_dev);
+
+      /* Check for a reply to the IPv4 packet */
+
+      virtnet_reply(priv);
+    }
+  else
+#endif
+#ifdef CONFIG_NET_IPv6
+  /* Check for an IPv6 packet */
+
+  if (BUF->type == HTONS(ETHTYPE_IP6))
+    {
+      vrtinfo("IPv6 frame\n");
+      NETDEV_RXIPV6(&priv->vnet_dev);
+
+      /* Dispatch IPv6 packet to the network layer */
+
+      ipv6_input(&priv->vnet_dev);
+
+      /* Check for a reply to the IPv6 packet */
+
+      virtnet_reply(priv);
+    }
+  else
+#endif
+#ifdef CONFIG_NET_ARP
+  /* Check for an ARP packet */
+
+  if (BUF->type == HTONS(ETHTYPE_ARP))
+    {
+      /* Dispatch ARP packet to the network layer */
+
+      arp_arpin(&priv->vnet_dev);
+      NETDEV_RXARP(&priv->vnet_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.
+       */
+
+      if (priv->vnet_dev.d_len > 0)
+        {
+          virtnet_transmit(priv);
+        }
+    }
+  else
+#endif
+    {
+      NETDEV_RXDROPPED(&priv->vnet_dev);
+
+      vrtwarn("+++ dropped BUF->type=0x%x \n", BUF->type);
+      DEBUGASSERT(0 != BUF->type);
+    }
+}
+
+/****************************************************************************
+ * Name: virtnet_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of a new RX packet
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void virtnet_receive(FAR struct virtnet_driver_s *priv)
+{
+  uint16_t idx;
+
+  vrtinfo("+++ rxq->last_used_idx=%d: rxq->used->idx=%d\n",
+          priv->rxq->last_used_idx, priv->rxq->used->idx);
+
+  for (idx = priv->rxq->last_used_idx; idx != priv->rxq->used->idx; idx++)
+    {
+      uint16_t id  = idx % priv->rxq->len;
+      uint16_t d1  = priv->rxq->used->ring[id].id; /* index for header */
+      uint16_t d2  = priv->rxq->desc[d1].next;     /* index for packet */
+      uint32_t len = priv->rxq->used->ring[id].len;
+      DEBUGASSERT(d2 == (d1 + 1));
+
+      vrtinfo("+++ idx=%d id=%d used->idx=%d: d1=%d d2=%d len=%" PRId32 "\n",
+              idx, id, priv->rxq->used->idx, d1, d2, len);
+
+      /* Set the packet info to d_buf and set d_len */
+
+      priv->vnet_dev.d_buf = priv->rxq->desc_virt[d2];
+      priv->vnet_dev.d_len = len - VIRTIO_NET_HDRLEN;
+
+      vrtinfo("Receiving packet, pktlen: %d\n", priv->vnet_dev.d_len);
+
+      /* Dispatch the incoming packet */
+
+      virtnet_rxdispatch(priv);
+
+      /* Set the descriptor back into the avail queue */
+
+      id = priv->rxq->avail->idx % priv->rxq->len;
+      priv->rxq->avail->ring[id] = d1;
+
+      mb();
+      priv->rxq->avail->idx += 1;
+
+      vrtinfo("+++ rxq->avail->idx=%d\n", priv->rxq->avail->idx);
+    }
+
+  priv->rxq->last_used_idx = priv->rxq->used->idx;
+  vrtinfo("+++ rxq->last_used_idx=%d\n",  priv->rxq->last_used_idx);
+}
+
+/****************************************************************************
+ * Name: virtnet_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef TODO
+static void virtnet_txdone(FAR struct virtnet_driver_s *priv)
+{
+  /* Check for errors and update statistics */
+
+  NETDEV_TXDONE(priv->vnet_dev);
+
+  /* Check if there are pending transmissions */
+
+  /* If no further transmissions are pending, then cancel the TX timeout and
+   * disable further Tx interrupts.
+   */
+
+  wd_cancel(&priv->vnet_txtimeout);
+
+  /* And disable further TX interrupts. */
+
+  /* In any event, poll the network for new TX data */
+
+  devif_poll(&priv->vnet_dev, virtnet_txpoll);
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Runs on a worker thread.
+ *
+ ****************************************************************************/
+
+static void virtnet_interrupt_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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();
+
+  /* Process pending Ethernet interrupts */
+
+  /* Handle interrupts according to status bit settings */
+
+  /* Check if we received an incoming packet, if so, call virtnet_receive() */
+
+  virtnet_receive(priv);
+
+  /* Check if a packet transmission just completed.
+   * If so, call virtnet_txdone.
+   * This may disable further Tx interrupts if there are no pending
+   * transmissions.
+   */
+
+#ifdef TODO
+  virtnet_txdone(priv);
+#endif
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  priv->rxq->avail->flags = 0;
+  priv->txq->avail->flags = 0;
+  mb();
+}
+
+/****************************************************************************
+ * Name: virtnet_interrupt
+ *
+ * Description:
+ *   Hardware interrupt handler
+ *
+ * Input Parameters:
+ *   irq     - Number of the IRQ that generated the interrupt
+ *   context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Runs in the context of a the Ethernet interrupt handler.  Local
+ *   interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static int virtnet_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg;
+  uint32_t stat;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Get and clear interrupt status bits */
+
+  stat = virtio_getreg32(&priv->regs->interrupt_status);
+  virtio_putreg32(stat, &priv->regs->interrupt_ack);
+  vrtinfo("+++ called (stat=0x%" PRIx32 ")\n", stat);
+
+  /* Disable further Ethernet interrupts.  Because Ethernet interrupts are
+   * also disabled if the TX timeout event occurs, there can be no race
+   * condition here.
+   */
+
+  priv->rxq->avail->flags = 1;
+  priv->txq->avail->flags = 1;
+  mb();
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->vnet_irqwork, virtnet_interrupt_work, priv, 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ ****************************************************************************/
+
+static void virtnet_txtimeout_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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();
+
+  /* Increment statistics and dump debug info */
+
+  NETDEV_TXTIMEOUTS(priv->vnet_dev);
+
+  /* Then reset the hardware */
+
+  /* Then poll the network for new XMIT data */
+
+  devif_poll(&priv->vnet_dev, virtnet_txpoll);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: virtnet_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs in the context of a the timer interrupt handler.  Local
+ *   interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static void virtnet_txtimeout_expiry(wdparm_t arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg;
+
+#ifdef TODO
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+#endif
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->vnet_irqwork, virtnet_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Name: virtnet_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  vrtinfo("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
+  vrtinfo("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
+
+  /* Initialize PHYs, Ethernet interface, and setup up Ethernet interrupts */
+
+  /* Instantiate MAC address from
+   * priv->vnet_dev.d_mac.ether.ether_addr_octet
+   */
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  virtnet_ipv6multicast(priv);
+#endif
+
+  /* Enable the Ethernet interrupt */
+
+  priv->vnet_bifup = true;
+  up_enable_irq(priv->irq);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->irq);
+
+  /* Cancel the TX timeout timers */
+
+  wd_cancel(&priv->vnet_txtimeout);
+
+  /* Put the EMAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the virtnet_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  /* Mark the device "down" */
+
+  priv->vnet_bifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void virtnet_txavail_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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->vnet_bifup)
+    {
+      /* Check if there is room in the hardware to hold another packet. */
+
+      /* If so, then poll the network for new XMIT data */
+
+      devif_poll(&priv->vnet_dev, virtnet_txpoll);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: virtnet_txavail
+ *
+ * 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.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* 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.
+   */
+
+  if (work_available(&priv->vnet_pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->vnet_pollwork,
+                 virtnet_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int virtnet_addmac(FAR struct net_driver_s *dev,
+                          FAR const uint8_t *mac)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  UNUSED(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_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:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int virtnet_rmmac(FAR struct net_driver_s *dev,
+                         FAR const uint8_t *mac)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  UNUSED(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t tmp16;
+  uint8_t mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  vrtinfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  virtnet_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  virtnet_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  virtnet_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Name: virtnet_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int virtnet_ioctl(FAR struct net_driver_s *dev, int cmd,
+                         unsigned long arg)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+  int ret;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+      /* Add cases here to support the IOCTL commands */
+
+      default:
+        vrterr("ERROR: Unrecognized IOCTL command: %d\n", command);
+        return -ENOTTY;  /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_initialize
+ *
+ * Description:
+ *   Initialize the virt-net driver
+ *
+ * Input Parameters:
+ *
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called early in initialization before multi-tasking is initiated.
+ *
+ ****************************************************************************/
+
+static int virtnet_initialize(FAR struct virtio_mmio_regs *regs, int irq)
+{
+  FAR struct virtnet_driver_s *priv;
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(g_ninterfaces < VIRTIO_NET_NINTERFACES);
+  priv = &g_virtnet[g_ninterfaces];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct virtnet_driver_s));
+
+  /* Check if a Ethernet chip is recognized at its I/O base */
+
+  /* Attach the IRQ to the driver */
+
+  priv->irq = irq;
+
+  if (irq_attach(priv->irq, virtnet_interrupt, priv))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  /* Setup virtio related */
+
+  priv->regs = regs;
+
+  priv->txq  = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN);
+  priv->rxq  = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN);
+
+  virtq_add_to_device(regs, priv->rxq, VIRTIO_NET_Q_RX);
+  virtq_add_to_device(regs, priv->txq, VIRTIO_NET_Q_TX);
+
+  /* Prepare packets for rxq */
+
+  virtnet_add_packets_to_rxq(priv);
+
+  priv->vnet_dev.d_buf     = (FAR uint8_t *)g_pktbuf[g_ninterfaces];  /* Single packet buffer */
+
+  priv->vnet_dev.d_ifup    = virtnet_ifup;               /* I/F up (new IP address) callback */
+  priv->vnet_dev.d_ifdown  = virtnet_ifdown;             /* I/F down callback */
+  priv->vnet_dev.d_txavail = virtnet_txavail;            /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->vnet_dev.d_addmac  = virtnet_addmac;             /* Add multicast MAC address */
+  priv->vnet_dev.d_rmmac   = virtnet_rmmac;              /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->vnet_dev.d_ioctl   = virtnet_ioctl;              /* Handle network IOCTL commands */
+#endif
+  priv->vnet_dev.d_private = g_virtnet;                  /* Used to recover private state from dev */
+
+#ifdef TODO
+  /* Put the interface in the down state.  This usually amounts to resetting
+   * the device and/or calling virtnet_ifdown().
+   */
+
+  /* Read the MAC address from the hardware into
+   * priv->vnet_dev.d_mac.ether.ether_addr_octet
+   * Applies only if the Ethernet MAC has its own internal address.
+   */
+#endif
+
+  /* Register the device with the OS so that socket IOCTLs can be performed */
+
+  netdev_register(&priv->vnet_dev, NET_LL_ETHERNET);
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_init
+ *
+ * Description:
+ *   Called from virtio-mmio.c to initialize virtnet
+ *
+ ****************************************************************************/
+
+int virtnet_init(FAR struct virtio_mmio_regs *regs, uint32_t irq)
+{
+  int ret = OK;
+
+  /* TODO: feature negotiation */
+
+  /* Set STATUS_FEATURE_OK */
+
+  virtio_putreg32(virtio_getreg32(&regs->status) | VIRTIO_STATUS_FEATURES_OK,
+           &regs->status);
+  mb();
+
+  ret = virtnet_initialize(regs, irq);
+
+  if (OK != ret)
+    {
+      vrterr("error: virtnet_initialize() returned %d \n", ret);
+      return ret;
+    }
+
+  /* Set STATUS_FRIVER_OK */
+
+  virtio_putreg32(virtio_getreg32(&regs->status) | VIRTIO_STATUS_DRIVER_OK,
+           &regs->status);

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034210527


##########
include/nuttx/virtio/virtio-net.h:
##########
@@ -0,0 +1,68 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-net.h

Review Comment:
   done
   



##########
include/nuttx/virtio/virtio-net.h:
##########
@@ -0,0 +1,68 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-net.h
+ *
+ * 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_VIRTIO_VIRTIO_NET_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_NET_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_init
+ *
+ * Description:
+ *   Called from virtio-mmio.c to initialize virtnet
+ *
+ ****************************************************************************/
+
+int virtnet_init(FAR struct virtio_mmio_regs *regs, uint32_t intid);

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034210302


##########
drivers/virtio/virtio-net.c:
##########
@@ -0,0 +1,1340 @@
+/****************************************************************************
+ * drivers/virtio/virtio-net.c

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#discussion_r1031883979


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,33 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	default n
+
+menuconfig DRIVERS_VIRTIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0

Review Comment:
   @xiaoxiang781216 @qinwei2004 
   OK, let me consider supporting multiple device instances.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1033246691


##########
include/nuttx/virtio/virtio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio.h

Review Comment:
   OK. I will rename the file name to virtio-mmio.h.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] xiaoxiang781216 merged pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged PR #7673:
URL: https://github.com/apache/nuttx/pull/7673


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] xiaoxiang781216 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1033086125


##########
include/nuttx/virtio/virtio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio.h
+ *
+ * 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_VIRTIO_VIRTIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()
+
+#define getreg32(a)         (*(volatile uint32_t *)(a))

Review Comment:
   should we add virtio_ prefix all mb/getreg32/putreg32 to avoid the potential conflict?



##########
drivers/virtio/virtio-mmio.c:
##########
@@ -0,0 +1,226 @@
+/****************************************************************************
+ * drivers/virtio/virtio-mmio.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/virtio/virtio.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+#  include <nuttx/virtio/virtio-net.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ptr_to_uint64(x)  ((uint64_t)(uintptr_t)(x))
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtq_create
+ ****************************************************************************/
+
+FAR struct virtqueue *virtq_create(uint32_t len)
+{
+  FAR struct virtqueue *virtq = (FAR struct virtqueue *)
+    kmm_zalloc(sizeof(struct virtqueue));
+  ASSERT(virtq);
+
+  virtq->len = len;
+
+  /* See: 2.6 Split Virtqueues */
+
+  virtq->desc  = (FAR struct virtqueue_desc *)
+    kmm_memalign(16, 16 * len);
+  ASSERT(virtq->desc);
+
+  virtq->avail = (FAR struct virtqueue_avail *)
+    kmm_memalign(2, 6 + 2 * len);
+  ASSERT(virtq->avail);
+
+  virtq->used  = (FAR struct virtqueue_used *)
+    kmm_memalign(4, 6 + 8 * len);
+  ASSERT(virtq->used);
+
+  virtq->desc_virt = (FAR void **)
+    kmm_memalign(16, sizeof(FAR void *) * len);
+  ASSERT(virtq->desc_virt);
+
+  _info("virtq=%p (len=%" PRId32 ")\n", virtq, len);
+  _info("virtq->desc=%p \n", virtq->desc);
+  _info("virtq->avail=%p \n", virtq->avail);
+  _info("virtq->used=%p \n", virtq->used);
+
+  virtq->avail->idx = 0;
+  virtq->used->idx = 0;
+  virtq->last_used_idx = 0;
+
+  return virtq;
+}
+
+/****************************************************************************
+ * Name: virtq_dev_init
+ ****************************************************************************/
+
+static int virtio_dev_init(uintptr_t virt, uint32_t irq)
+{
+  FAR struct virtio_mmio_regs *regs = (FAR struct virtio_mmio_regs *)virt;
+  int ret = -1;
+  uint32_t val;
+
+  _info("examine virtio at 0x%" PRIxPTR "\n", virt);
+
+  val = getreg32(&regs->magic_value);
+
+  if (VIRTIO_MAGIC != val)
+    {
+      _err("error: virtio at 0x%" PRIxPTR
+           " had wrong magic value 0x%" PRIx32 "\n", virt, val);
+      return ret;
+    }
+
+  val = getreg32(&regs->version);
+
+  if (VIRTIO_VERSION != val)
+    {
+      _err("error: virtio at 0x%" PRIxPTR
+           " had wrong version 0x%" PRIx32 "\n", virt, val);
+      return ret;
+    }
+
+  /* Reset */
+
+  putreg32(0, &regs->status);
+  mb();
+
+  /* Set ack */
+
+  val = getreg32(&regs->status) | VIRTIO_STATUS_ACKNOWLEDGE;
+  putreg32(val, &regs->status);
+  mb();
+
+  /* Set driver */
+
+  val = getreg32(&regs->status) | VIRTIO_STATUS_DRIVER;
+  putreg32(val, &regs->status);
+  mb();
+
+  /* Check the device_id */
+
+  val = getreg32(&regs->device_id);
+
+  switch (val)
+    {
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+    case VIRTIO_DEV_NET:
+      ret = virtnet_init(regs, irq);
+      break;
+#endif
+    default:
+      _warn("unsupported device_id 0x%" PRIx32 "\n", val);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtq_alloc_desc
+ ****************************************************************************/
+
+uint32_t virtq_alloc_desc(FAR struct virtqueue *virtq,
+                          uint32_t idx, FAR void *addr)
+{
+  uint32_t id = idx % virtq->len;
+
+  _info("virtq=%p, idx=%" PRId32 "\n", virtq, idx);
+
+  virtq->desc[id].addr = (uintptr_t)addr;
+  virtq->desc_virt[id] = addr;
+  return id;
+}
+
+/****************************************************************************
+ * Name: virtq_add_to_device
+ ****************************************************************************/
+
+void virtq_add_to_device(FAR struct virtio_mmio_regs *regs,
+                         FAR struct virtqueue *virtq,
+                         uint32_t queue_sel)
+{
+  _info("==== queue_sel=%" PRId32 ", virtq->len=%" PRId32 "\n",
+        queue_sel, virtq->len);
+
+  putreg32(queue_sel, &regs->queue_sel);
+  mb();
+
+  putreg32(virtq->len, &regs->queue_num);
+
+  putreg32((uint32_t)(ptr_to_uint64(virtq->desc)),
+           &regs->queue_desc_low);
+  putreg32((uint32_t)(ptr_to_uint64(virtq->desc) >> 32),
+           &regs->queue_desc_high);
+
+  putreg32((uint32_t)(ptr_to_uint64(virtq->avail)),
+           &regs->queue_avail_low);
+  putreg32((uint32_t)(ptr_to_uint64(virtq->avail) >> 32),
+           &regs->queue_avail_high);
+
+  putreg32((uint32_t)(ptr_to_uint64(virtq->used)),
+           &regs->queue_used_low);
+  putreg32((uint32_t)(ptr_to_uint64(virtq->used) >> 32),
+           &regs->queue_used_high);
+
+  mb();
+  putreg32(1, &regs->queue_ready);
+}
+
+/****************************************************************************
+ * Name: virtio_init
+ ****************************************************************************/
+
+void virtio_init(void)
+{
+  uintptr_t virtio = (uintptr_t)CONFIG_DRIVERS_VIRTIO_BASE;
+  uint32_t  irq = CONFIG_DRIVERS_VIRTIO_IRQ;
+  uint32_t  size = CONFIG_DRIVERS_VIRTIO_REGSIZE;
+  uint32_t  i;
+
+  for (i = 0; i < CONFIG_DRIVERS_VIRTIO_NUM; i++)
+    {
+      virtio_dev_init(virtio + size * i, irq + i);

Review Comment:
   Can all devices share this rule for address and irq?



##########
include/nuttx/virtio/virtio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio.h
+ *
+ * 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_VIRTIO_VIRTIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()
+
+#define getreg32(a)         (*(volatile uint32_t *)(a))
+#define putreg32(v,a)       (*(volatile uint32_t *)(a) = (v))

Review Comment:
   add FAR for getreg32 and putreg32



##########
include/nuttx/virtio/virtio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio.h
+ *
+ * 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_VIRTIO_VIRTIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()
+
+#define getreg32(a)         (*(volatile uint32_t *)(a))
+#define putreg32(v,a)       (*(volatile uint32_t *)(a) = (v))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Table 4.1 MMIO Device Register Layout */
+
+struct virtio_mmio_regs
+{
+  uint32_t magic_value;           /* offset:0x000 */
+  uint32_t version;               /* offset:0x004 */
+  uint32_t device_id;             /* offset:0x008 */
+  uint32_t vendor_id;             /* offset:0x00c */
+  uint32_t device_features;       /* offset:0x010 */
+  uint32_t device_features_sel;   /* offset:0x014 */
+  uint32_t _reserved0[2];
+  uint32_t driver_features;       /* offset:0x020 */
+  uint32_t driver_features_sel;   /* offset:0x024 */
+  uint32_t _reserved1[2];
+  uint32_t queue_sel;             /* offset:0x030 */
+  uint32_t queue_num_max;         /* offset:0x034 */
+  uint32_t queue_num;             /* offset:0x038 */
+  uint32_t _reserved2[2];
+  uint32_t queue_ready;           /* offset:0x044 */
+  uint32_t _reserved3[2];
+  uint32_t queue_notify;          /* offset:0x050 */
+  uint32_t _reserved4[3];
+  uint32_t interrupt_status;      /* offset:0x060 */
+  uint32_t interrupt_ack;         /* offset:0x064 */
+  uint32_t _reserved5[2];
+  uint32_t status;                /* offset:0x070 */
+  uint32_t _reserved6[3];
+  uint32_t queue_desc_low;        /* offset:0x080 */
+  uint32_t queue_desc_high;       /* offset:0x084 */
+  uint32_t _reserved7[2];
+  uint32_t queue_avail_low;       /* offset:0x090 */
+  uint32_t queue_avail_high;      /* offset:0x094 */
+  uint32_t _reserved8[2];
+  uint32_t queue_used_low;        /* offset:0x0a0 */
+  uint32_t queue_used_high;       /* offset:0x0a4 */
+  uint32_t _reserved9[21];
+  uint32_t config_generation;     /* offset:0x0fc */
+  uint32_t config[0];
+};
+
+struct virtqueue_desc
+{
+  uint64_t addr;
+  uint32_t len;
+  uint16_t flags;
+  uint16_t next;
+};
+
+struct virtqueue_avail
+{
+  uint16_t flags;
+  uint16_t idx;
+  uint16_t ring[0];
+};
+
+struct virtqueue_used_elem
+{
+  uint32_t id;
+  uint32_t len;
+};
+
+struct virtqueue_used
+{
+  uint16_t flags;
+  uint16_t idx;
+  struct virtqueue_used_elem ring[0];
+};
+
+struct virtqueue
+{
+  uint32_t len;
+  uint16_t last_used_idx;
+
+  FAR struct virtqueue_desc *desc;
+  FAR struct virtqueue_avail *avail;
+  FAR struct virtqueue_used *used;
+  FAR uint16_t *avail_event;
+  FAR void **desc_virt;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+FAR struct virtqueue *virtq_create(uint32_t len);
+
+uint32_t virtq_alloc_desc(FAR struct virtqueue *virtq,
+                          uint32_t id,
+                          FAR void *addr);
+
+void virtq_add_to_device(FAR struct virtio_mmio_regs *regs,
+                         FAR struct virtqueue *virtq,
+                         uint32_t queue_sel);
+
+void virtio_init(void);

Review Comment:
   let's unify the prefix to vrito_mmio_xxx?



##########
drivers/virtio/virtio-mmio.c:
##########
@@ -0,0 +1,226 @@
+/****************************************************************************
+ * drivers/virtio/virtio-mmio.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/virtio/virtio.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+#  include <nuttx/virtio/virtio-net.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ptr_to_uint64(x)  ((uint64_t)(uintptr_t)(x))
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtq_create
+ ****************************************************************************/
+
+FAR struct virtqueue *virtq_create(uint32_t len)
+{
+  FAR struct virtqueue *virtq = (FAR struct virtqueue *)
+    kmm_zalloc(sizeof(struct virtqueue));
+  ASSERT(virtq);
+
+  virtq->len = len;
+
+  /* See: 2.6 Split Virtqueues */
+
+  virtq->desc  = (FAR struct virtqueue_desc *)
+    kmm_memalign(16, 16 * len);
+  ASSERT(virtq->desc);
+
+  virtq->avail = (FAR struct virtqueue_avail *)
+    kmm_memalign(2, 6 + 2 * len);
+  ASSERT(virtq->avail);
+
+  virtq->used  = (FAR struct virtqueue_used *)
+    kmm_memalign(4, 6 + 8 * len);
+  ASSERT(virtq->used);
+
+  virtq->desc_virt = (FAR void **)
+    kmm_memalign(16, sizeof(FAR void *) * len);
+  ASSERT(virtq->desc_virt);
+
+  _info("virtq=%p (len=%" PRId32 ")\n", virtq, len);

Review Comment:
   should we add new macro to debug.h



##########
drivers/virtio/virtio-mmio.c:
##########
@@ -0,0 +1,226 @@
+/****************************************************************************
+ * drivers/virtio/virtio-mmio.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/virtio/virtio.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+#  include <nuttx/virtio/virtio-net.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ptr_to_uint64(x)  ((uint64_t)(uintptr_t)(x))
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtq_create
+ ****************************************************************************/
+
+FAR struct virtqueue *virtq_create(uint32_t len)
+{
+  FAR struct virtqueue *virtq = (FAR struct virtqueue *)
+    kmm_zalloc(sizeof(struct virtqueue));
+  ASSERT(virtq);
+
+  virtq->len = len;
+
+  /* See: 2.6 Split Virtqueues */
+
+  virtq->desc  = (FAR struct virtqueue_desc *)
+    kmm_memalign(16, 16 * len);
+  ASSERT(virtq->desc);
+
+  virtq->avail = (FAR struct virtqueue_avail *)
+    kmm_memalign(2, 6 + 2 * len);
+  ASSERT(virtq->avail);
+
+  virtq->used  = (FAR struct virtqueue_used *)
+    kmm_memalign(4, 6 + 8 * len);
+  ASSERT(virtq->used);
+
+  virtq->desc_virt = (FAR void **)
+    kmm_memalign(16, sizeof(FAR void *) * len);
+  ASSERT(virtq->desc_virt);
+
+  _info("virtq=%p (len=%" PRId32 ")\n", virtq, len);
+  _info("virtq->desc=%p \n", virtq->desc);
+  _info("virtq->avail=%p \n", virtq->avail);
+  _info("virtq->used=%p \n", virtq->used);
+
+  virtq->avail->idx = 0;
+  virtq->used->idx = 0;
+  virtq->last_used_idx = 0;
+
+  return virtq;
+}
+
+/****************************************************************************
+ * Name: virtq_dev_init
+ ****************************************************************************/
+
+static int virtio_dev_init(uintptr_t virt, uint32_t irq)
+{
+  FAR struct virtio_mmio_regs *regs = (FAR struct virtio_mmio_regs *)virt;
+  int ret = -1;

Review Comment:
   -1 to ENODEV



##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,50 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+menuconfig DRIVERS_VIRTIO_NUM

Review Comment:
   should we use more specific name(e.g. DRIVERS_VIRTIO_MMIO_NUM)? since all similar setting is specific for virtio-mmio transport(there are many other transport).



##########
include/nuttx/virtio/virtio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio.h

Review Comment:
   should we change file name to virito-mmio.h since it's tightly couple with mmio transport? Or i's better:
   
   1. Split mmio specific macro and function to virtio-mmio.h
   2. Keep the common virtio macro and function in virtio.h
   3. Ensure virto net driver only use virtio macro and function not virtio mmio.



##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,50 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+menuconfig DRIVERS_VIRTIO_NUM

Review Comment:
   let's use:
   ```
   if DRIVERS_VIRTIO
   endif
   ```
   to avoid the duplication of "depends on DRIVERS_VIRTIO".



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] masayuki2009 commented on pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#issuecomment-1326027131

   >@masayuki2009 since https://github.com/apache/incubator-nuttx/tree/master/openamp also contain virtio implementation, >we need consider how to unify both part. BTW, OpenAMP is adding virito driver support:
   
   @xiaoxiang781216 
   Hmm, this PR needs to work without OpenAMP until we unify both.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#discussion_r1031197614


##########
boards/arm64/qemu/qemu-armv8a/src/qemu_virtio.c:
##########
@@ -0,0 +1,41 @@
+/****************************************************************************
+ * boards/arm64/qemu/qemu-armv8a/src/qemu_virtio.c
+ *
+ * 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 <syslog.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/board.h>
+#include <nuttx/virtio/virtio.h>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void arm64_netinitialize(void)
+{
+  virtio_init();

Review Comment:
   or let' call virtnet_init directly and let's virtnet_init call virtio_devinit instead.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034254117


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,45 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+if DRIVERS_VIRTIO
+menuconfig DRIVERS_VIRTIO_MMIO_NUM
+	int "The number of virtio mmio devices"
+	default 1
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_BASE
+	hex "Virtio-mmio base address"
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_REGSIZE
+	hex "Virtio-mmio register size"

Review Comment:
   done
   



##########
include/nuttx/virtio/virtio-mmio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-mmio.h
+ *
+ * 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_VIRTIO_VIRTIO_MMIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_MMIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] xiaoxiang781216 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034243330


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,45 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+if DRIVERS_VIRTIO
+menuconfig DRIVERS_VIRTIO_MMIO_NUM
+	int "The number of virtio mmio devices"
+	default 1
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_BASE
+	hex "Virtio-mmio base address"
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_REGSIZE
+	hex "Virtio-mmio register size"

Review Comment:
   could add the default value 0 since it is suitable for single device



##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,45 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+if DRIVERS_VIRTIO
+menuconfig DRIVERS_VIRTIO_MMIO_NUM
+	int "The number of virtio mmio devices"
+	default 1
+	---help---
+

Review Comment:
   ```
   if DRIVERS_VIRTIO_MMIO_NUM != 0
   all mmio related config
   endif
   ```
   



##########
include/nuttx/virtio/virtio-mmio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-mmio.h
+ *
+ * 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_VIRTIO_VIRTIO_MMIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_MMIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()
+
+#define virtio_getreg32(a)    (FAR *(volatile FAR uint32_t *)(a))
+#define virtio_putreg32(v,a)  (FAR *(volatile FAR uint32_t *)(a) = (v))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Table 4.1 MMIO Device Register Layout */
+
+struct virtio_mmio_regs
+{
+  uint32_t magic_value;           /* offset:0x000 */
+  uint32_t version;               /* offset:0x004 */
+  uint32_t device_id;             /* offset:0x008 */
+  uint32_t vendor_id;             /* offset:0x00c */
+  uint32_t device_features;       /* offset:0x010 */
+  uint32_t device_features_sel;   /* offset:0x014 */
+  uint32_t _reserved0[2];
+  uint32_t driver_features;       /* offset:0x020 */
+  uint32_t driver_features_sel;   /* offset:0x024 */
+  uint32_t _reserved1[2];
+  uint32_t queue_sel;             /* offset:0x030 */
+  uint32_t queue_num_max;         /* offset:0x034 */
+  uint32_t queue_num;             /* offset:0x038 */
+  uint32_t _reserved2[2];
+  uint32_t queue_ready;           /* offset:0x044 */
+  uint32_t _reserved3[2];
+  uint32_t queue_notify;          /* offset:0x050 */
+  uint32_t _reserved4[3];
+  uint32_t interrupt_status;      /* offset:0x060 */
+  uint32_t interrupt_ack;         /* offset:0x064 */
+  uint32_t _reserved5[2];
+  uint32_t status;                /* offset:0x070 */
+  uint32_t _reserved6[3];
+  uint32_t queue_desc_low;        /* offset:0x080 */
+  uint32_t queue_desc_high;       /* offset:0x084 */
+  uint32_t _reserved7[2];
+  uint32_t queue_avail_low;       /* offset:0x090 */
+  uint32_t queue_avail_high;      /* offset:0x094 */
+  uint32_t _reserved8[2];
+  uint32_t queue_used_low;        /* offset:0x0a0 */
+  uint32_t queue_used_high;       /* offset:0x0a4 */
+  uint32_t _reserved9[21];
+  uint32_t config_generation;     /* offset:0x0fc */
+  uint32_t config[0];
+};
+
+struct virtqueue_desc
+{
+  uint64_t addr;
+  uint32_t len;
+  uint16_t flags;
+  uint16_t next;
+};
+
+struct virtqueue_avail
+{
+  uint16_t flags;
+  uint16_t idx;
+  uint16_t ring[0];
+};
+
+struct virtqueue_used_elem
+{
+  uint32_t id;
+  uint32_t len;
+};
+
+struct virtqueue_used
+{
+  uint16_t flags;
+  uint16_t idx;
+  struct virtqueue_used_elem ring[0];
+};
+
+struct virtqueue
+{
+  uint32_t len;
+  uint16_t last_used_idx;
+
+  FAR struct virtqueue_desc *desc;
+  FAR struct virtqueue_avail *avail;
+  FAR struct virtqueue_used *used;
+  FAR uint16_t *avail_event;
+  FAR void **desc_virt;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+FAR struct virtqueue *virtq_create(uint32_t len);
+
+uint32_t virtq_alloc_desc(FAR struct virtqueue *virtq,
+                          uint32_t id,
+                          FAR void *addr);
+
+void virtq_add_to_device(FAR struct virtio_mmio_regs *regs,

Review Comment:
   virtq_add_to_mmio_device



##########
include/nuttx/virtio/virtio-mmio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-mmio.h
+ *
+ * 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_VIRTIO_VIRTIO_MMIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_MMIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()

Review Comment:
   add virtio_ prefix



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] qinwei2004 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
qinwei2004 commented on code in PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#discussion_r1031488979


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,33 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	default n
+
+menuconfig DRIVERS_VIRTIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0

Review Comment:
   @masayuki2009 add multiple mmio device instance support is important for the virtio architecture and maybe we need to consider it early. otherwise we will rework the driver if the architecture is change 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1033347617


##########
include/nuttx/virtio/virtio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio.h
+ *
+ * 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_VIRTIO_VIRTIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()
+
+#define getreg32(a)         (*(volatile uint32_t *)(a))
+#define putreg32(v,a)       (*(volatile uint32_t *)(a) = (v))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Table 4.1 MMIO Device Register Layout */
+
+struct virtio_mmio_regs
+{
+  uint32_t magic_value;           /* offset:0x000 */
+  uint32_t version;               /* offset:0x004 */
+  uint32_t device_id;             /* offset:0x008 */
+  uint32_t vendor_id;             /* offset:0x00c */
+  uint32_t device_features;       /* offset:0x010 */
+  uint32_t device_features_sel;   /* offset:0x014 */
+  uint32_t _reserved0[2];
+  uint32_t driver_features;       /* offset:0x020 */
+  uint32_t driver_features_sel;   /* offset:0x024 */
+  uint32_t _reserved1[2];
+  uint32_t queue_sel;             /* offset:0x030 */
+  uint32_t queue_num_max;         /* offset:0x034 */
+  uint32_t queue_num;             /* offset:0x038 */
+  uint32_t _reserved2[2];
+  uint32_t queue_ready;           /* offset:0x044 */
+  uint32_t _reserved3[2];
+  uint32_t queue_notify;          /* offset:0x050 */
+  uint32_t _reserved4[3];
+  uint32_t interrupt_status;      /* offset:0x060 */
+  uint32_t interrupt_ack;         /* offset:0x064 */
+  uint32_t _reserved5[2];
+  uint32_t status;                /* offset:0x070 */
+  uint32_t _reserved6[3];
+  uint32_t queue_desc_low;        /* offset:0x080 */
+  uint32_t queue_desc_high;       /* offset:0x084 */
+  uint32_t _reserved7[2];
+  uint32_t queue_avail_low;       /* offset:0x090 */
+  uint32_t queue_avail_high;      /* offset:0x094 */
+  uint32_t _reserved8[2];
+  uint32_t queue_used_low;        /* offset:0x0a0 */
+  uint32_t queue_used_high;       /* offset:0x0a4 */
+  uint32_t _reserved9[21];
+  uint32_t config_generation;     /* offset:0x0fc */
+  uint32_t config[0];
+};
+
+struct virtqueue_desc
+{
+  uint64_t addr;
+  uint32_t len;
+  uint16_t flags;
+  uint16_t next;
+};
+
+struct virtqueue_avail
+{
+  uint16_t flags;
+  uint16_t idx;
+  uint16_t ring[0];
+};
+
+struct virtqueue_used_elem
+{
+  uint32_t id;
+  uint32_t len;
+};
+
+struct virtqueue_used
+{
+  uint16_t flags;
+  uint16_t idx;
+  struct virtqueue_used_elem ring[0];
+};
+
+struct virtqueue
+{
+  uint32_t len;
+  uint16_t last_used_idx;
+
+  FAR struct virtqueue_desc *desc;
+  FAR struct virtqueue_avail *avail;
+  FAR struct virtqueue_used *used;
+  FAR uint16_t *avail_event;
+  FAR void **desc_virt;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+FAR struct virtqueue *virtq_create(uint32_t len);
+
+uint32_t virtq_alloc_desc(FAR struct virtqueue *virtq,
+                          uint32_t id,
+                          FAR void *addr);
+
+void virtq_add_to_device(FAR struct virtio_mmio_regs *regs,
+                         FAR struct virtqueue *virtq,
+                         uint32_t queue_sel);
+
+void virtio_init(void);

Review Comment:
   done
   



##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,50 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+menuconfig DRIVERS_VIRTIO_NUM

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1033347269


##########
drivers/virtio/virtio-mmio.c:
##########
@@ -0,0 +1,226 @@
+/****************************************************************************
+ * drivers/virtio/virtio-mmio.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/virtio/virtio.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+#  include <nuttx/virtio/virtio-net.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ptr_to_uint64(x)  ((uint64_t)(uintptr_t)(x))
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtq_create
+ ****************************************************************************/
+
+FAR struct virtqueue *virtq_create(uint32_t len)
+{
+  FAR struct virtqueue *virtq = (FAR struct virtqueue *)
+    kmm_zalloc(sizeof(struct virtqueue));
+  ASSERT(virtq);
+
+  virtq->len = len;
+
+  /* See: 2.6 Split Virtqueues */
+
+  virtq->desc  = (FAR struct virtqueue_desc *)
+    kmm_memalign(16, 16 * len);
+  ASSERT(virtq->desc);
+
+  virtq->avail = (FAR struct virtqueue_avail *)
+    kmm_memalign(2, 6 + 2 * len);
+  ASSERT(virtq->avail);
+
+  virtq->used  = (FAR struct virtqueue_used *)
+    kmm_memalign(4, 6 + 8 * len);
+  ASSERT(virtq->used);
+
+  virtq->desc_virt = (FAR void **)
+    kmm_memalign(16, sizeof(FAR void *) * len);
+  ASSERT(virtq->desc_virt);
+
+  _info("virtq=%p (len=%" PRId32 ")\n", virtq, len);

Review Comment:
   done
   



##########
drivers/virtio/virtio-mmio.c:
##########
@@ -0,0 +1,226 @@
+/****************************************************************************
+ * drivers/virtio/virtio-mmio.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/virtio/virtio.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+#  include <nuttx/virtio/virtio-net.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ptr_to_uint64(x)  ((uint64_t)(uintptr_t)(x))
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtq_create
+ ****************************************************************************/
+
+FAR struct virtqueue *virtq_create(uint32_t len)
+{
+  FAR struct virtqueue *virtq = (FAR struct virtqueue *)
+    kmm_zalloc(sizeof(struct virtqueue));
+  ASSERT(virtq);
+
+  virtq->len = len;
+
+  /* See: 2.6 Split Virtqueues */
+
+  virtq->desc  = (FAR struct virtqueue_desc *)
+    kmm_memalign(16, 16 * len);
+  ASSERT(virtq->desc);
+
+  virtq->avail = (FAR struct virtqueue_avail *)
+    kmm_memalign(2, 6 + 2 * len);
+  ASSERT(virtq->avail);
+
+  virtq->used  = (FAR struct virtqueue_used *)
+    kmm_memalign(4, 6 + 8 * len);
+  ASSERT(virtq->used);
+
+  virtq->desc_virt = (FAR void **)
+    kmm_memalign(16, sizeof(FAR void *) * len);
+  ASSERT(virtq->desc_virt);
+
+  _info("virtq=%p (len=%" PRId32 ")\n", virtq, len);
+  _info("virtq->desc=%p \n", virtq->desc);
+  _info("virtq->avail=%p \n", virtq->avail);
+  _info("virtq->used=%p \n", virtq->used);
+
+  virtq->avail->idx = 0;
+  virtq->used->idx = 0;
+  virtq->last_used_idx = 0;
+
+  return virtq;
+}
+
+/****************************************************************************
+ * Name: virtq_dev_init
+ ****************************************************************************/
+
+static int virtio_dev_init(uintptr_t virt, uint32_t irq)
+{
+  FAR struct virtio_mmio_regs *regs = (FAR struct virtio_mmio_regs *)virt;
+  int ret = -1;

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034210697


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,47 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+if DRIVERS_VIRTIO
+menuconfig DRIVERS_VIRTIO_MMIO_NUM
+	int "The number of virtio mmio devices"
+	default 1
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_REGSIZE
+	hex "Virtio-mmio register size"
+	default 0x0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_IRQ
+	int "Virtio-mmio irq number"
+	default 0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_NET

Review Comment:
   done
   



##########
drivers/virtio/Make.defs:
##########
@@ -0,0 +1,34 @@
+############################################################################
+# drivers/virtio/Make.defs
+#
+# 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.
+#
+############################################################################
+
+# Include virtio support
+
+ifeq ($(CONFIG_DRIVERS_VIRTIO),y)

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#discussion_r1031098455


##########
boards/risc-v/qemu-rv/rv-virt/src/qemu_rv_virtio.c:
##########
@@ -0,0 +1,41 @@
+/****************************************************************************
+ * boards/risc-v/qemu-rv/rv-virt/src/qemu_rv_virtio.c
+ *
+ * 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 <syslog.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/board.h>
+#include <nuttx/virtio/virtio.h>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void riscv_netinitialize(void)
+{
+  virtio_init();

Review Comment:
   should we move to arch/risc-v/src/qemu-rv?



##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,33 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	default n
+
+menuconfig DRIVERS_VIRTIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0

Review Comment:
   how to support multiple mmio device instance?



##########
boards/arm64/qemu/qemu-armv8a/src/qemu_virtio.c:
##########
@@ -0,0 +1,41 @@
+/****************************************************************************
+ * boards/arm64/qemu/qemu-armv8a/src/qemu_virtio.c

Review Comment:
   should we move to arch/arm64/src/qemu?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/incubator-nuttx/pull/7673#discussion_r1031111380


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,33 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	default n
+
+menuconfig DRIVERS_VIRTIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0

Review Comment:
   @xiaoxiang781216 
   I think multiple mmio devices support is future work.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034210403


##########
drivers/virtio/virtio-net.c:
##########
@@ -0,0 +1,1340 @@
+/****************************************************************************
+ * drivers/virtio/virtio-net.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/virtio/virtio-mmio.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEBUG
+
+/* 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 ETHWORK LPWORK
+
+/* VIRTIO_NET_NINTERFACES determines the number of
+ * physical interfaces that will be supported.
+ */
+
+#ifndef VIRTIO_NET_NINTERFACES
+# define VIRTIO_NET_NINTERFACES 1
+#endif
+
+/* TX timeout = 1 minute */
+
+#define VIRTNET_TXTIMEOUT (60*CLK_TCK)
+
+/* Packet buffer size */
+
+#define PKTBUF_SIZE (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE)
+
+/* This is a helper pointer for accessing the contents of Ethernet header */
+
+#define BUF ((struct eth_hdr_s *)priv->vnet_dev.d_buf)
+
+/* virtio net related definition */
+
+#define VIRTIO_NET_HDRLEN 10
+
+#define VIRTIO_NET_Q_RX 0
+#define VIRTIO_NET_Q_TX 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct virtnet_hdr_s
+{
+  uint8_t  flags;
+  uint8_t  gso_type;
+  uint16_t hdr_len;
+  uint16_t gso_size;
+  uint16_t csum_start;
+  uint16_t csum_offset;
+};
+
+/* The virtnet_driver_s encapsulates all state information for
+ * a single hardware interface
+ */
+
+struct virtnet_driver_s
+{
+  bool vnet_bifup;               /* true:ifup false:ifdown */
+  int  irq;
+
+  FAR struct virtio_mmio_regs *regs; /* virtio_mmio registers */
+  FAR struct virtqueue   *txq;       /* TX queue */
+  FAR struct virtqueue   *rxq;       /* RX queue */
+
+  struct wdog_s vnet_txtimeout;  /* TX timeout timer */
+  struct work_s vnet_irqwork;    /* For deferring interrupt work to the work queue */
+  struct work_s vnet_pollwork;   /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s vnet_dev;  /* Interface understood by the network */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* These statically allocated structures would mean that only a single
+ * instance of the device could be supported.  In order to support multiple
+ * devices instances, this data would have to be allocated dynamically.
+ */
+
+/* A single packet buffer per device is used in this example.  There might
+ * be multiple packet buffers in a more complex, pipelined design.  Many
+ * contemporary Ethernet interfaces, for example,  use multiple, linked DMA
+ * descriptors in rings to implement such a pipeline.  This example assumes
+ * much simpler hardware that simply handles one packet at a time.
+ *
+ * NOTE that if VIRTIO_NET_NINTERFACES were greater than 1,
+ * you would need a minimum on one packet buffer per instance.
+ * Much better to be allocated dynamically in cases where more than
+ * one are needed.
+ */
+
+static uint16_t
+  g_pktbuf[VIRTIO_NET_NINTERFACES][(PKTBUF_SIZE + 1) / 2];

Review Comment:
   done
   



##########
drivers/virtio/virtio-net.c:
##########
@@ -0,0 +1,1340 @@
+/****************************************************************************
+ * drivers/virtio/virtio-net.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/virtio/virtio-mmio.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEBUG
+
+/* 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 ETHWORK LPWORK
+
+/* VIRTIO_NET_NINTERFACES determines the number of
+ * physical interfaces that will be supported.
+ */
+
+#ifndef VIRTIO_NET_NINTERFACES
+# define VIRTIO_NET_NINTERFACES 1
+#endif
+
+/* TX timeout = 1 minute */
+
+#define VIRTNET_TXTIMEOUT (60*CLK_TCK)
+
+/* Packet buffer size */
+
+#define PKTBUF_SIZE (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE)
+
+/* This is a helper pointer for accessing the contents of Ethernet header */
+
+#define BUF ((struct eth_hdr_s *)priv->vnet_dev.d_buf)
+
+/* virtio net related definition */
+
+#define VIRTIO_NET_HDRLEN 10
+
+#define VIRTIO_NET_Q_RX 0
+#define VIRTIO_NET_Q_TX 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct virtnet_hdr_s
+{
+  uint8_t  flags;
+  uint8_t  gso_type;
+  uint16_t hdr_len;
+  uint16_t gso_size;
+  uint16_t csum_start;
+  uint16_t csum_offset;
+};
+
+/* The virtnet_driver_s encapsulates all state information for
+ * a single hardware interface
+ */
+
+struct virtnet_driver_s
+{
+  bool vnet_bifup;               /* true:ifup false:ifdown */
+  int  irq;
+
+  FAR struct virtio_mmio_regs *regs; /* virtio_mmio registers */
+  FAR struct virtqueue   *txq;       /* TX queue */
+  FAR struct virtqueue   *rxq;       /* RX queue */
+
+  struct wdog_s vnet_txtimeout;  /* TX timeout timer */
+  struct work_s vnet_irqwork;    /* For deferring interrupt work to the work queue */
+  struct work_s vnet_pollwork;   /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s vnet_dev;  /* Interface understood by the network */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* These statically allocated structures would mean that only a single
+ * instance of the device could be supported.  In order to support multiple
+ * devices instances, this data would have to be allocated dynamically.
+ */
+
+/* A single packet buffer per device is used in this example.  There might
+ * be multiple packet buffers in a more complex, pipelined design.  Many
+ * contemporary Ethernet interfaces, for example,  use multiple, linked DMA
+ * descriptors in rings to implement such a pipeline.  This example assumes
+ * much simpler hardware that simply handles one packet at a time.
+ *
+ * NOTE that if VIRTIO_NET_NINTERFACES were greater than 1,
+ * you would need a minimum on one packet buffer per instance.
+ * Much better to be allocated dynamically in cases where more than
+ * one are needed.
+ */
+
+static uint16_t
+  g_pktbuf[VIRTIO_NET_NINTERFACES][(PKTBUF_SIZE + 1) / 2];
+
+/* Driver state structure */
+
+static struct virtnet_driver_s g_virtnet[VIRTIO_NET_NINTERFACES];
+
+static uint32_t g_ninterfaces = 0;
+
+/* Common virtnet header */
+
+static struct virtnet_hdr_s g_hdr;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int  virtnet_transmit(FAR struct virtnet_driver_s *priv);
+static int  virtnet_txpoll(FAR struct net_driver_s *dev);
+
+/* Interrupt handling */
+
+static void virtnet_reply(FAR struct virtnet_driver_s *priv);
+static void virtnet_receive(FAR struct virtnet_driver_s *priv);
+
+#ifdef TODO
+static void virtnet_txdone(FAR struct virtnet_driver_s *priv);
+#endif
+
+static void virtnet_interrupt_work(FAR void *arg);
+static int  virtnet_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/* Watchdog timer expirations */
+
+static void virtnet_txtimeout_work(FAR void *arg);
+static void virtnet_txtimeout_expiry(FAR wdparm_t arg);
+
+/* NuttX callback functions */
+
+static int  virtnet_ifup(FAR struct net_driver_s *dev);
+static int  virtnet_ifdown(FAR struct net_driver_s *dev);
+
+static void virtnet_txavail_work(FAR void *arg);
+static int  virtnet_txavail(FAR struct net_driver_s *dev);
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int  virtnet_addmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac);
+#ifdef CONFIG_NET_MCASTGROUP
+static int  virtnet_rmmac(FAR struct net_driver_s *dev,
+                          FAR const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_ICMPv6
+static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv);
+#endif
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+static int  virtnet_ioctl(FAR struct net_driver_s *dev, int cmd,
+                          unsigned long arg);
+#endif
+
+static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_cmd_status
+ ****************************************************************************/
+
+#ifdef DEBUG
+static void virtnet_cmd_status(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct virtio_mmio_regs *regs = priv->regs;
+
+  vrtinfo("virtnet at %p\n", regs);
+  vrtinfo("  status=0x%" PRIx32 "\n",
+          virtio_getreg32(&regs->status));
+  vrtinfo("  interrupt_status=0x%" PRIx32 "\n",
+          virtio_getreg32(&regs->interrupt_status));
+
+  virtio_putreg32(VIRTIO_NET_Q_TX, &regs->queue_sel);
+  mb();
+
+  vrtinfo("  txq: avail->idx=%d, used->idx=%d, ready=0x%" PRIx32 "\n",
+          priv->txq->avail->idx, priv->txq->used->idx,
+          virtio_getreg32(&regs->queue_ready));
+
+  virtio_putreg32(VIRTIO_NET_Q_RX, &regs->queue_sel);
+  mb();
+
+  vrtinfo("  rxq: avail->idx=%d used->idx=%d ready=0x%" PRIx32 "\n",
+          priv->rxq->avail->idx, priv->rxq->used->idx,
+          virtio_getreg32(&regs->queue_ready));
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_add_packets_to_rxq
+ ****************************************************************************/
+
+static void virtnet_add_packets_to_rxq(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct virtqueue *rxq = priv->rxq;
+  FAR uint8_t  *pkt;
+  uint16_t d1;
+  uint16_t d2;
+  uint32_t i;
+
+  vrtinfo("+++ rxq->len=%" PRId32 "  rxq->avail-idx=%d \n",
+          rxq->len, rxq->avail->idx);
+  DEBUGASSERT(rxq->avail->idx == 0);
+
+  for (i = 0; i < rxq->len / 2; i++)
+    {
+      pkt = kmm_memalign(16, PKTBUF_SIZE);
+      ASSERT(pkt);
+
+      /* Allocate new descriptors for header and packet */
+
+      d1 = virtq_alloc_desc(rxq, (i * 2),  (FAR void *)&g_hdr);
+      d2 = virtq_alloc_desc(rxq, (i * 2) + 1, pkt);
+
+      /* Set up the descriptor for header */
+
+      rxq->desc[d1].len   = VIRTIO_NET_HDRLEN;
+      rxq->desc[d1].flags = VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT;
+      rxq->desc[d1].next  = d2;
+
+      /* Set up the descriptor for packet */
+
+      rxq->desc[d2].len   = PKTBUF_SIZE;
+      rxq->desc[d2].flags = VIRTQ_DESC_F_WRITE;
+
+      /* Set the first descriptor to the avail->ring */
+
+      rxq->avail->ring[i] = d1;
+
+      /* Increment the avail->idx for each two descriptors */
+
+      mb();
+      rxq->avail->idx += 1;
+    }
+
+  vrtinfo("+++ virtq->avail-idx=%d \n", rxq->avail->idx);
+  vrtinfo("+++ virtq->used->idx=%d \n", rxq->used->idx);
+}
+
+/****************************************************************************
+ * Name: virtnet_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the txdone interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_transmit(FAR struct virtnet_driver_s *priv)
+{
+  FAR void *pkt = priv->vnet_dev.d_buf;
+  uint32_t len  = priv->vnet_dev.d_len;
+  uint16_t idx  = priv->txq->avail->idx;
+  uint16_t d1;
+  uint16_t d2;
+
+  /* Send the packet:
+   * address=priv->vnet_dev.d_buf, length=priv->vnet_dev.d_len
+   */
+
+  /* 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.
+   */
+
+  /* Increment statistics */
+
+  NETDEV_TXPACKETS(priv->vnet_dev);
+
+  vrtinfo("=== Sending packet, length: %d\n", priv->vnet_dev.d_len);
+
+#ifdef DEBUG
+  virtnet_cmd_status(priv);
+#endif
+
+  /* Allocate new descriptors for header and packet */
+
+  d1 = virtq_alloc_desc(priv->txq, idx, (FAR void *)&g_hdr);
+  d2 = virtq_alloc_desc(priv->txq, idx + 1, pkt);
+  DEBUGASSERT(d1 != d2);
+
+  /* Set up the descriptor for header */
+
+  priv->txq->desc[d2].len   = len;
+  priv->txq->desc[d2].flags = 0;
+
+  /* Set up the descriptor for packet */
+
+  priv->txq->desc[d1].len   = VIRTIO_NET_HDRLEN;
+  priv->txq->desc[d1].flags = VIRTQ_DESC_F_NEXT;
+  priv->txq->desc[d1].next  = d2;
+
+  /* Set the first descriptor to the avail->ring */
+
+  priv->txq->avail->ring[d1] = d1;
+
+  /* Increment the avail->idx for each two descriptors */
+
+  mb();
+  priv->txq->avail->idx += 1;
+
+  vrtinfo("*** d1=%d, d2=%d, txq->avail->idx=%d\n",
+          d1, d2, priv->txq->avail->idx);
+
+  virtio_putreg32(VIRTIO_NET_Q_TX, &priv->regs->queue_notify);
+
+  vrtinfo("*** finish updating queue_notify\n");
+
+#ifdef TODO
+  /* Enable Tx interrupts */
+#endif
+
+  /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->vnet_txtimeout, VIRTNET_TXTIMEOUT,
+           virtnet_txtimeout_expiry, (wdparm_t)priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txpoll
+ *
+ * Description:
+ *   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,
+ *   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
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_txpoll(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  vrtinfo("Poll result: d_len=%d\n", priv->vnet_dev.d_len);
+
+  /* Send the packet */
+
+  virtnet_reply(priv);
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: virtnet_reply
+ *
+ * Description:
+ *   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:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void virtnet_reply(FAR struct virtnet_driver_s *priv)
+{
+  /* 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 (priv->vnet_dev.d_len > 0)
+    {
+      /* And send the packet */
+
+      virtnet_transmit(priv);
+    }
+}
+
+/****************************************************************************
+ * Function: virtnet_rxdispatch
+ *
+ * Description:
+ *   A new Rx packet was received; dispatch that packet to the network layer
+ *   as necessary.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv)
+{
+  /* Update statistics */
+
+  NETDEV_RXPACKETS(&priv->dev);
+
+#ifdef CONFIG_NET_PKT
+  /* When packet sockets are enabled, feed the frame into the tap */
+
+  pkt_input(&priv->vnet_dev);
+#endif
+
+#ifdef CONFIG_NET_IPv4
+  /* Check for an IPv4 packet */
+
+  if (BUF->type == HTONS(ETHTYPE_IP))
+    {
+      vrtinfo("IPv4 frame\n");
+      NETDEV_RXIPV4(&priv->vnet_dev);
+
+      /* Receive an IPv4 packet from the network device */
+
+      vrtinfo("+++ call ipv4_input() d_len=%d\n", priv->vnet_dev.d_len);
+      ipv4_input(&priv->vnet_dev);
+
+      /* Check for a reply to the IPv4 packet */
+
+      virtnet_reply(priv);
+    }
+  else
+#endif
+#ifdef CONFIG_NET_IPv6
+  /* Check for an IPv6 packet */
+
+  if (BUF->type == HTONS(ETHTYPE_IP6))
+    {
+      vrtinfo("IPv6 frame\n");
+      NETDEV_RXIPV6(&priv->vnet_dev);
+
+      /* Dispatch IPv6 packet to the network layer */
+
+      ipv6_input(&priv->vnet_dev);
+
+      /* Check for a reply to the IPv6 packet */
+
+      virtnet_reply(priv);
+    }
+  else
+#endif
+#ifdef CONFIG_NET_ARP
+  /* Check for an ARP packet */
+
+  if (BUF->type == HTONS(ETHTYPE_ARP))
+    {
+      /* Dispatch ARP packet to the network layer */
+
+      arp_arpin(&priv->vnet_dev);
+      NETDEV_RXARP(&priv->vnet_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.
+       */
+
+      if (priv->vnet_dev.d_len > 0)
+        {
+          virtnet_transmit(priv);
+        }
+    }
+  else
+#endif
+    {
+      NETDEV_RXDROPPED(&priv->vnet_dev);
+
+      vrtwarn("+++ dropped BUF->type=0x%x \n", BUF->type);
+      DEBUGASSERT(0 != BUF->type);
+    }
+}
+
+/****************************************************************************
+ * Name: virtnet_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of a new RX packet
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void virtnet_receive(FAR struct virtnet_driver_s *priv)
+{
+  uint16_t idx;
+
+  vrtinfo("+++ rxq->last_used_idx=%d: rxq->used->idx=%d\n",
+          priv->rxq->last_used_idx, priv->rxq->used->idx);
+
+  for (idx = priv->rxq->last_used_idx; idx != priv->rxq->used->idx; idx++)
+    {
+      uint16_t id  = idx % priv->rxq->len;
+      uint16_t d1  = priv->rxq->used->ring[id].id; /* index for header */
+      uint16_t d2  = priv->rxq->desc[d1].next;     /* index for packet */
+      uint32_t len = priv->rxq->used->ring[id].len;
+      DEBUGASSERT(d2 == (d1 + 1));
+
+      vrtinfo("+++ idx=%d id=%d used->idx=%d: d1=%d d2=%d len=%" PRId32 "\n",
+              idx, id, priv->rxq->used->idx, d1, d2, len);
+
+      /* Set the packet info to d_buf and set d_len */
+
+      priv->vnet_dev.d_buf = priv->rxq->desc_virt[d2];
+      priv->vnet_dev.d_len = len - VIRTIO_NET_HDRLEN;
+
+      vrtinfo("Receiving packet, pktlen: %d\n", priv->vnet_dev.d_len);
+
+      /* Dispatch the incoming packet */
+
+      virtnet_rxdispatch(priv);
+
+      /* Set the descriptor back into the avail queue */
+
+      id = priv->rxq->avail->idx % priv->rxq->len;
+      priv->rxq->avail->ring[id] = d1;
+
+      mb();
+      priv->rxq->avail->idx += 1;
+
+      vrtinfo("+++ rxq->avail->idx=%d\n", priv->rxq->avail->idx);
+    }
+
+  priv->rxq->last_used_idx = priv->rxq->used->idx;
+  vrtinfo("+++ rxq->last_used_idx=%d\n",  priv->rxq->last_used_idx);
+}
+
+/****************************************************************************
+ * Name: virtnet_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef TODO
+static void virtnet_txdone(FAR struct virtnet_driver_s *priv)
+{
+  /* Check for errors and update statistics */
+
+  NETDEV_TXDONE(priv->vnet_dev);
+
+  /* Check if there are pending transmissions */
+
+  /* If no further transmissions are pending, then cancel the TX timeout and
+   * disable further Tx interrupts.
+   */
+
+  wd_cancel(&priv->vnet_txtimeout);
+
+  /* And disable further TX interrupts. */
+
+  /* In any event, poll the network for new TX data */
+
+  devif_poll(&priv->vnet_dev, virtnet_txpoll);
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Runs on a worker thread.
+ *
+ ****************************************************************************/
+
+static void virtnet_interrupt_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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();
+
+  /* Process pending Ethernet interrupts */
+
+  /* Handle interrupts according to status bit settings */
+
+  /* Check if we received an incoming packet, if so, call virtnet_receive() */
+
+  virtnet_receive(priv);
+
+  /* Check if a packet transmission just completed.
+   * If so, call virtnet_txdone.
+   * This may disable further Tx interrupts if there are no pending
+   * transmissions.
+   */
+
+#ifdef TODO
+  virtnet_txdone(priv);
+#endif
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  priv->rxq->avail->flags = 0;
+  priv->txq->avail->flags = 0;
+  mb();
+}
+
+/****************************************************************************
+ * Name: virtnet_interrupt
+ *
+ * Description:
+ *   Hardware interrupt handler
+ *
+ * Input Parameters:
+ *   irq     - Number of the IRQ that generated the interrupt
+ *   context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Runs in the context of a the Ethernet interrupt handler.  Local
+ *   interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static int virtnet_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg;
+  uint32_t stat;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Get and clear interrupt status bits */
+
+  stat = virtio_getreg32(&priv->regs->interrupt_status);
+  virtio_putreg32(stat, &priv->regs->interrupt_ack);
+  vrtinfo("+++ called (stat=0x%" PRIx32 ")\n", stat);
+
+  /* Disable further Ethernet interrupts.  Because Ethernet interrupts are
+   * also disabled if the TX timeout event occurs, there can be no race
+   * condition here.
+   */
+
+  priv->rxq->avail->flags = 1;
+  priv->txq->avail->flags = 1;
+  mb();
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->vnet_irqwork, virtnet_interrupt_work, priv, 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ ****************************************************************************/
+
+static void virtnet_txtimeout_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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();
+
+  /* Increment statistics and dump debug info */
+
+  NETDEV_TXTIMEOUTS(priv->vnet_dev);
+
+  /* Then reset the hardware */
+
+  /* Then poll the network for new XMIT data */
+
+  devif_poll(&priv->vnet_dev, virtnet_txpoll);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: virtnet_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs in the context of a the timer interrupt handler.  Local
+ *   interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static void virtnet_txtimeout_expiry(wdparm_t arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg;
+
+#ifdef TODO
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+#endif
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->vnet_irqwork, virtnet_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Name: virtnet_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  vrtinfo("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
+  vrtinfo("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
+
+  /* Initialize PHYs, Ethernet interface, and setup up Ethernet interrupts */
+
+  /* Instantiate MAC address from
+   * priv->vnet_dev.d_mac.ether.ether_addr_octet
+   */
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  virtnet_ipv6multicast(priv);
+#endif
+
+  /* Enable the Ethernet interrupt */
+
+  priv->vnet_bifup = true;
+  up_enable_irq(priv->irq);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->irq);
+
+  /* Cancel the TX timeout timers */
+
+  wd_cancel(&priv->vnet_txtimeout);
+
+  /* Put the EMAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the virtnet_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  /* Mark the device "down" */
+
+  priv->vnet_bifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void virtnet_txavail_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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->vnet_bifup)
+    {
+      /* Check if there is room in the hardware to hold another packet. */
+
+      /* If so, then poll the network for new XMIT data */
+
+      devif_poll(&priv->vnet_dev, virtnet_txpoll);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: virtnet_txavail
+ *
+ * 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.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* 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.
+   */
+
+  if (work_available(&priv->vnet_pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->vnet_pollwork,
+                 virtnet_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int virtnet_addmac(FAR struct net_driver_s *dev,
+                          FAR const uint8_t *mac)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  UNUSED(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_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:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int virtnet_rmmac(FAR struct net_driver_s *dev,
+                         FAR const uint8_t *mac)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  UNUSED(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t tmp16;
+  uint8_t mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  vrtinfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  virtnet_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  virtnet_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  virtnet_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Name: virtnet_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int virtnet_ioctl(FAR struct net_driver_s *dev, int cmd,
+                         unsigned long arg)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+  int ret;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+      /* Add cases here to support the IOCTL commands */
+
+      default:
+        vrterr("ERROR: Unrecognized IOCTL command: %d\n", command);
+        return -ENOTTY;  /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_initialize
+ *
+ * Description:
+ *   Initialize the virt-net driver
+ *
+ * Input Parameters:
+ *
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called early in initialization before multi-tasking is initiated.
+ *
+ ****************************************************************************/
+
+static int virtnet_initialize(FAR struct virtio_mmio_regs *regs, int irq)
+{
+  FAR struct virtnet_driver_s *priv;
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(g_ninterfaces < VIRTIO_NET_NINTERFACES);
+  priv = &g_virtnet[g_ninterfaces];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct virtnet_driver_s));
+
+  /* Check if a Ethernet chip is recognized at its I/O base */
+
+  /* Attach the IRQ to the driver */
+
+  priv->irq = irq;
+
+  if (irq_attach(priv->irq, virtnet_interrupt, priv))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  /* Setup virtio related */
+
+  priv->regs = regs;
+
+  priv->txq  = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN);
+  priv->rxq  = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN);
+
+  virtq_add_to_device(regs, priv->rxq, VIRTIO_NET_Q_RX);
+  virtq_add_to_device(regs, priv->txq, VIRTIO_NET_Q_TX);
+
+  /* Prepare packets for rxq */
+
+  virtnet_add_packets_to_rxq(priv);
+
+  priv->vnet_dev.d_buf     = (FAR uint8_t *)g_pktbuf[g_ninterfaces];  /* Single packet buffer */
+
+  priv->vnet_dev.d_ifup    = virtnet_ifup;               /* I/F up (new IP address) callback */
+  priv->vnet_dev.d_ifdown  = virtnet_ifdown;             /* I/F down callback */
+  priv->vnet_dev.d_txavail = virtnet_txavail;            /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->vnet_dev.d_addmac  = virtnet_addmac;             /* Add multicast MAC address */
+  priv->vnet_dev.d_rmmac   = virtnet_rmmac;              /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->vnet_dev.d_ioctl   = virtnet_ioctl;              /* Handle network IOCTL commands */
+#endif
+  priv->vnet_dev.d_private = g_virtnet;                  /* Used to recover private state from dev */
+
+#ifdef TODO
+  /* Put the interface in the down state.  This usually amounts to resetting
+   * the device and/or calling virtnet_ifdown().
+   */
+
+  /* Read the MAC address from the hardware into
+   * priv->vnet_dev.d_mac.ether.ether_addr_octet
+   * Applies only if the Ethernet MAC has its own internal address.
+   */
+#endif
+
+  /* Register the device with the OS so that socket IOCTLs can be performed */
+
+  netdev_register(&priv->vnet_dev, NET_LL_ETHERNET);
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_init
+ *
+ * Description:
+ *   Called from virtio-mmio.c to initialize virtnet
+ *
+ ****************************************************************************/
+
+int virtnet_init(FAR struct virtio_mmio_regs *regs, uint32_t irq)
+{
+  int ret = OK;
+
+  /* TODO: feature negotiation */
+
+  /* Set STATUS_FEATURE_OK */
+
+  virtio_putreg32(virtio_getreg32(&regs->status) | VIRTIO_STATUS_FEATURES_OK,
+           &regs->status);

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1033346936


##########
include/nuttx/virtio/virtio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio.h
+ *
+ * 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_VIRTIO_VIRTIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()
+
+#define getreg32(a)         (*(volatile uint32_t *)(a))

Review Comment:
   done



##########
include/nuttx/virtio/virtio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio.h
+ *
+ * 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_VIRTIO_VIRTIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()
+
+#define getreg32(a)         (*(volatile uint32_t *)(a))
+#define putreg32(v,a)       (*(volatile uint32_t *)(a) = (v))

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#issuecomment-1328825603

   @xiaoxiang781216 
   Could you please review the latest commits again?
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] xiaoxiang781216 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1033764173


##########
drivers/virtio/virtio-net.c:
##########
@@ -0,0 +1,1340 @@
+/****************************************************************************
+ * drivers/virtio/virtio-net.c

Review Comment:
   should we change to virtio-mmio-net.c since it moidfy virtio mmio register directly



##########
drivers/virtio/virtio-net.c:
##########
@@ -0,0 +1,1340 @@
+/****************************************************************************
+ * drivers/virtio/virtio-net.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/virtio/virtio-mmio.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEBUG
+
+/* 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 ETHWORK LPWORK
+
+/* VIRTIO_NET_NINTERFACES determines the number of
+ * physical interfaces that will be supported.
+ */
+
+#ifndef VIRTIO_NET_NINTERFACES
+# define VIRTIO_NET_NINTERFACES 1
+#endif
+
+/* TX timeout = 1 minute */
+
+#define VIRTNET_TXTIMEOUT (60*CLK_TCK)
+
+/* Packet buffer size */
+
+#define PKTBUF_SIZE (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE)
+
+/* This is a helper pointer for accessing the contents of Ethernet header */
+
+#define BUF ((struct eth_hdr_s *)priv->vnet_dev.d_buf)
+
+/* virtio net related definition */
+
+#define VIRTIO_NET_HDRLEN 10
+
+#define VIRTIO_NET_Q_RX 0
+#define VIRTIO_NET_Q_TX 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct virtnet_hdr_s
+{
+  uint8_t  flags;
+  uint8_t  gso_type;
+  uint16_t hdr_len;
+  uint16_t gso_size;
+  uint16_t csum_start;
+  uint16_t csum_offset;
+};
+
+/* The virtnet_driver_s encapsulates all state information for
+ * a single hardware interface
+ */
+
+struct virtnet_driver_s
+{
+  bool vnet_bifup;               /* true:ifup false:ifdown */
+  int  irq;
+
+  FAR struct virtio_mmio_regs *regs; /* virtio_mmio registers */
+  FAR struct virtqueue   *txq;       /* TX queue */
+  FAR struct virtqueue   *rxq;       /* RX queue */
+
+  struct wdog_s vnet_txtimeout;  /* TX timeout timer */
+  struct work_s vnet_irqwork;    /* For deferring interrupt work to the work queue */
+  struct work_s vnet_pollwork;   /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s vnet_dev;  /* Interface understood by the network */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* These statically allocated structures would mean that only a single
+ * instance of the device could be supported.  In order to support multiple
+ * devices instances, this data would have to be allocated dynamically.
+ */
+
+/* A single packet buffer per device is used in this example.  There might
+ * be multiple packet buffers in a more complex, pipelined design.  Many
+ * contemporary Ethernet interfaces, for example,  use multiple, linked DMA
+ * descriptors in rings to implement such a pipeline.  This example assumes
+ * much simpler hardware that simply handles one packet at a time.
+ *
+ * NOTE that if VIRTIO_NET_NINTERFACES were greater than 1,
+ * you would need a minimum on one packet buffer per instance.
+ * Much better to be allocated dynamically in cases where more than
+ * one are needed.
+ */
+
+static uint16_t
+  g_pktbuf[VIRTIO_NET_NINTERFACES][(PKTBUF_SIZE + 1) / 2];

Review Comment:
   remove space before g_pktbuf



##########
include/nuttx/virtio/virtio-net.h:
##########
@@ -0,0 +1,68 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-net.h

Review Comment:
   could move to drivers/virtio since it is only called by virtio-mmio.c



##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,47 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+if DRIVERS_VIRTIO
+menuconfig DRIVERS_VIRTIO_MMIO_NUM
+	int "The number of virtio mmio devices"
+	default 1
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_REGSIZE
+	hex "Virtio-mmio register size"
+	default 0x0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_IRQ
+	int "Virtio-mmio irq number"
+	default 0

Review Comment:
   should we remove the default value of DRIVERS_VIRTIO_MMIO_BASE and DRIVERS_VIRTIO_MMIO_IRQ since the default value is most likely unsuitable.



##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,47 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+if DRIVERS_VIRTIO
+menuconfig DRIVERS_VIRTIO_MMIO_NUM
+	int "The number of virtio mmio devices"
+	default 1
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_BASE
+	hex "Virtio-mmio base address"
+	default 0x0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_REGSIZE
+	hex "Virtio-mmio register size"
+	default 0x0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_MMIO_IRQ
+	int "Virtio-mmio irq number"
+	default 0
+	---help---
+
+menuconfig DRIVERS_VIRTIO_NET

Review Comment:
   DRIVERS_VIRTIO_MMIO_NET and depends on DRIVERS_VIRTIO_MMIO_NUM > 0



##########
include/nuttx/virtio/virtio-net.h:
##########
@@ -0,0 +1,68 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-net.h
+ *
+ * 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_VIRTIO_VIRTIO_NET_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_NET_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_init
+ *
+ * Description:
+ *   Called from virtio-mmio.c to initialize virtnet
+ *
+ ****************************************************************************/
+
+int virtnet_init(FAR struct virtio_mmio_regs *regs, uint32_t intid);

Review Comment:
   virtio_mmio_net_init? since it depends on viritio mmio?



##########
drivers/virtio/Make.defs:
##########
@@ -0,0 +1,34 @@
+############################################################################
+# drivers/virtio/Make.defs
+#
+# 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.
+#
+############################################################################
+
+# Include virtio support
+
+ifeq ($(CONFIG_DRIVERS_VIRTIO),y)

Review Comment:
   ifneq ($(DRIVERS_VIRTIO_MMIO_NUM,0)



##########
drivers/virtio/virtio-net.c:
##########
@@ -0,0 +1,1340 @@
+/****************************************************************************
+ * drivers/virtio/virtio-net.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/virtio/virtio-mmio.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEBUG
+
+/* 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 ETHWORK LPWORK
+
+/* VIRTIO_NET_NINTERFACES determines the number of
+ * physical interfaces that will be supported.
+ */
+
+#ifndef VIRTIO_NET_NINTERFACES
+# define VIRTIO_NET_NINTERFACES 1
+#endif
+
+/* TX timeout = 1 minute */
+
+#define VIRTNET_TXTIMEOUT (60*CLK_TCK)
+
+/* Packet buffer size */
+
+#define PKTBUF_SIZE (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE)
+
+/* This is a helper pointer for accessing the contents of Ethernet header */
+
+#define BUF ((struct eth_hdr_s *)priv->vnet_dev.d_buf)
+
+/* virtio net related definition */
+
+#define VIRTIO_NET_HDRLEN 10
+
+#define VIRTIO_NET_Q_RX 0
+#define VIRTIO_NET_Q_TX 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct virtnet_hdr_s
+{
+  uint8_t  flags;
+  uint8_t  gso_type;
+  uint16_t hdr_len;
+  uint16_t gso_size;
+  uint16_t csum_start;
+  uint16_t csum_offset;
+};
+
+/* The virtnet_driver_s encapsulates all state information for
+ * a single hardware interface
+ */
+
+struct virtnet_driver_s
+{
+  bool vnet_bifup;               /* true:ifup false:ifdown */
+  int  irq;
+
+  FAR struct virtio_mmio_regs *regs; /* virtio_mmio registers */
+  FAR struct virtqueue   *txq;       /* TX queue */
+  FAR struct virtqueue   *rxq;       /* RX queue */
+
+  struct wdog_s vnet_txtimeout;  /* TX timeout timer */
+  struct work_s vnet_irqwork;    /* For deferring interrupt work to the work queue */
+  struct work_s vnet_pollwork;   /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s vnet_dev;  /* Interface understood by the network */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* These statically allocated structures would mean that only a single
+ * instance of the device could be supported.  In order to support multiple
+ * devices instances, this data would have to be allocated dynamically.
+ */
+
+/* A single packet buffer per device is used in this example.  There might
+ * be multiple packet buffers in a more complex, pipelined design.  Many
+ * contemporary Ethernet interfaces, for example,  use multiple, linked DMA
+ * descriptors in rings to implement such a pipeline.  This example assumes
+ * much simpler hardware that simply handles one packet at a time.
+ *
+ * NOTE that if VIRTIO_NET_NINTERFACES were greater than 1,
+ * you would need a minimum on one packet buffer per instance.
+ * Much better to be allocated dynamically in cases where more than
+ * one are needed.
+ */
+
+static uint16_t
+  g_pktbuf[VIRTIO_NET_NINTERFACES][(PKTBUF_SIZE + 1) / 2];
+
+/* Driver state structure */
+
+static struct virtnet_driver_s g_virtnet[VIRTIO_NET_NINTERFACES];
+
+static uint32_t g_ninterfaces = 0;
+
+/* Common virtnet header */
+
+static struct virtnet_hdr_s g_hdr;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int  virtnet_transmit(FAR struct virtnet_driver_s *priv);
+static int  virtnet_txpoll(FAR struct net_driver_s *dev);
+
+/* Interrupt handling */
+
+static void virtnet_reply(FAR struct virtnet_driver_s *priv);
+static void virtnet_receive(FAR struct virtnet_driver_s *priv);
+
+#ifdef TODO
+static void virtnet_txdone(FAR struct virtnet_driver_s *priv);
+#endif
+
+static void virtnet_interrupt_work(FAR void *arg);
+static int  virtnet_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/* Watchdog timer expirations */
+
+static void virtnet_txtimeout_work(FAR void *arg);
+static void virtnet_txtimeout_expiry(FAR wdparm_t arg);
+
+/* NuttX callback functions */
+
+static int  virtnet_ifup(FAR struct net_driver_s *dev);
+static int  virtnet_ifdown(FAR struct net_driver_s *dev);
+
+static void virtnet_txavail_work(FAR void *arg);
+static int  virtnet_txavail(FAR struct net_driver_s *dev);
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int  virtnet_addmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac);
+#ifdef CONFIG_NET_MCASTGROUP
+static int  virtnet_rmmac(FAR struct net_driver_s *dev,
+                          FAR const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_ICMPv6
+static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv);
+#endif
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+static int  virtnet_ioctl(FAR struct net_driver_s *dev, int cmd,
+                          unsigned long arg);
+#endif
+
+static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_cmd_status
+ ****************************************************************************/
+
+#ifdef DEBUG
+static void virtnet_cmd_status(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct virtio_mmio_regs *regs = priv->regs;
+
+  vrtinfo("virtnet at %p\n", regs);
+  vrtinfo("  status=0x%" PRIx32 "\n",
+          virtio_getreg32(&regs->status));
+  vrtinfo("  interrupt_status=0x%" PRIx32 "\n",
+          virtio_getreg32(&regs->interrupt_status));
+
+  virtio_putreg32(VIRTIO_NET_Q_TX, &regs->queue_sel);
+  mb();
+
+  vrtinfo("  txq: avail->idx=%d, used->idx=%d, ready=0x%" PRIx32 "\n",
+          priv->txq->avail->idx, priv->txq->used->idx,
+          virtio_getreg32(&regs->queue_ready));
+
+  virtio_putreg32(VIRTIO_NET_Q_RX, &regs->queue_sel);
+  mb();
+
+  vrtinfo("  rxq: avail->idx=%d used->idx=%d ready=0x%" PRIx32 "\n",
+          priv->rxq->avail->idx, priv->rxq->used->idx,
+          virtio_getreg32(&regs->queue_ready));
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_add_packets_to_rxq
+ ****************************************************************************/
+
+static void virtnet_add_packets_to_rxq(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct virtqueue *rxq = priv->rxq;
+  FAR uint8_t  *pkt;
+  uint16_t d1;
+  uint16_t d2;
+  uint32_t i;
+
+  vrtinfo("+++ rxq->len=%" PRId32 "  rxq->avail-idx=%d \n",
+          rxq->len, rxq->avail->idx);
+  DEBUGASSERT(rxq->avail->idx == 0);
+
+  for (i = 0; i < rxq->len / 2; i++)
+    {
+      pkt = kmm_memalign(16, PKTBUF_SIZE);
+      ASSERT(pkt);
+
+      /* Allocate new descriptors for header and packet */
+
+      d1 = virtq_alloc_desc(rxq, (i * 2),  (FAR void *)&g_hdr);
+      d2 = virtq_alloc_desc(rxq, (i * 2) + 1, pkt);
+
+      /* Set up the descriptor for header */
+
+      rxq->desc[d1].len   = VIRTIO_NET_HDRLEN;
+      rxq->desc[d1].flags = VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT;
+      rxq->desc[d1].next  = d2;
+
+      /* Set up the descriptor for packet */
+
+      rxq->desc[d2].len   = PKTBUF_SIZE;
+      rxq->desc[d2].flags = VIRTQ_DESC_F_WRITE;
+
+      /* Set the first descriptor to the avail->ring */
+
+      rxq->avail->ring[i] = d1;
+
+      /* Increment the avail->idx for each two descriptors */
+
+      mb();
+      rxq->avail->idx += 1;
+    }
+
+  vrtinfo("+++ virtq->avail-idx=%d \n", rxq->avail->idx);
+  vrtinfo("+++ virtq->used->idx=%d \n", rxq->used->idx);
+}
+
+/****************************************************************************
+ * Name: virtnet_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the txdone interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_transmit(FAR struct virtnet_driver_s *priv)
+{
+  FAR void *pkt = priv->vnet_dev.d_buf;
+  uint32_t len  = priv->vnet_dev.d_len;
+  uint16_t idx  = priv->txq->avail->idx;
+  uint16_t d1;
+  uint16_t d2;
+
+  /* Send the packet:
+   * address=priv->vnet_dev.d_buf, length=priv->vnet_dev.d_len
+   */
+
+  /* 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.
+   */
+
+  /* Increment statistics */
+
+  NETDEV_TXPACKETS(priv->vnet_dev);
+
+  vrtinfo("=== Sending packet, length: %d\n", priv->vnet_dev.d_len);
+
+#ifdef DEBUG
+  virtnet_cmd_status(priv);
+#endif
+
+  /* Allocate new descriptors for header and packet */
+
+  d1 = virtq_alloc_desc(priv->txq, idx, (FAR void *)&g_hdr);
+  d2 = virtq_alloc_desc(priv->txq, idx + 1, pkt);
+  DEBUGASSERT(d1 != d2);
+
+  /* Set up the descriptor for header */
+
+  priv->txq->desc[d2].len   = len;
+  priv->txq->desc[d2].flags = 0;
+
+  /* Set up the descriptor for packet */
+
+  priv->txq->desc[d1].len   = VIRTIO_NET_HDRLEN;
+  priv->txq->desc[d1].flags = VIRTQ_DESC_F_NEXT;
+  priv->txq->desc[d1].next  = d2;
+
+  /* Set the first descriptor to the avail->ring */
+
+  priv->txq->avail->ring[d1] = d1;
+
+  /* Increment the avail->idx for each two descriptors */
+
+  mb();
+  priv->txq->avail->idx += 1;
+
+  vrtinfo("*** d1=%d, d2=%d, txq->avail->idx=%d\n",
+          d1, d2, priv->txq->avail->idx);
+
+  virtio_putreg32(VIRTIO_NET_Q_TX, &priv->regs->queue_notify);
+
+  vrtinfo("*** finish updating queue_notify\n");
+
+#ifdef TODO
+  /* Enable Tx interrupts */
+#endif
+
+  /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->vnet_txtimeout, VIRTNET_TXTIMEOUT,
+           virtnet_txtimeout_expiry, (wdparm_t)priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txpoll
+ *
+ * Description:
+ *   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,
+ *   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
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_txpoll(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  vrtinfo("Poll result: d_len=%d\n", priv->vnet_dev.d_len);
+
+  /* Send the packet */
+
+  virtnet_reply(priv);
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: virtnet_reply
+ *
+ * Description:
+ *   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:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void virtnet_reply(FAR struct virtnet_driver_s *priv)
+{
+  /* 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 (priv->vnet_dev.d_len > 0)
+    {
+      /* And send the packet */
+
+      virtnet_transmit(priv);
+    }
+}
+
+/****************************************************************************
+ * Function: virtnet_rxdispatch
+ *
+ * Description:
+ *   A new Rx packet was received; dispatch that packet to the network layer
+ *   as necessary.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv)
+{
+  /* Update statistics */
+
+  NETDEV_RXPACKETS(&priv->dev);
+
+#ifdef CONFIG_NET_PKT
+  /* When packet sockets are enabled, feed the frame into the tap */
+
+  pkt_input(&priv->vnet_dev);
+#endif
+
+#ifdef CONFIG_NET_IPv4
+  /* Check for an IPv4 packet */
+
+  if (BUF->type == HTONS(ETHTYPE_IP))
+    {
+      vrtinfo("IPv4 frame\n");
+      NETDEV_RXIPV4(&priv->vnet_dev);
+
+      /* Receive an IPv4 packet from the network device */
+
+      vrtinfo("+++ call ipv4_input() d_len=%d\n", priv->vnet_dev.d_len);
+      ipv4_input(&priv->vnet_dev);
+
+      /* Check for a reply to the IPv4 packet */
+
+      virtnet_reply(priv);
+    }
+  else
+#endif
+#ifdef CONFIG_NET_IPv6
+  /* Check for an IPv6 packet */
+
+  if (BUF->type == HTONS(ETHTYPE_IP6))
+    {
+      vrtinfo("IPv6 frame\n");
+      NETDEV_RXIPV6(&priv->vnet_dev);
+
+      /* Dispatch IPv6 packet to the network layer */
+
+      ipv6_input(&priv->vnet_dev);
+
+      /* Check for a reply to the IPv6 packet */
+
+      virtnet_reply(priv);
+    }
+  else
+#endif
+#ifdef CONFIG_NET_ARP
+  /* Check for an ARP packet */
+
+  if (BUF->type == HTONS(ETHTYPE_ARP))
+    {
+      /* Dispatch ARP packet to the network layer */
+
+      arp_arpin(&priv->vnet_dev);
+      NETDEV_RXARP(&priv->vnet_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.
+       */
+
+      if (priv->vnet_dev.d_len > 0)
+        {
+          virtnet_transmit(priv);
+        }
+    }
+  else
+#endif
+    {
+      NETDEV_RXDROPPED(&priv->vnet_dev);
+
+      vrtwarn("+++ dropped BUF->type=0x%x \n", BUF->type);
+      DEBUGASSERT(0 != BUF->type);
+    }
+}
+
+/****************************************************************************
+ * Name: virtnet_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of a new RX packet
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void virtnet_receive(FAR struct virtnet_driver_s *priv)
+{
+  uint16_t idx;
+
+  vrtinfo("+++ rxq->last_used_idx=%d: rxq->used->idx=%d\n",
+          priv->rxq->last_used_idx, priv->rxq->used->idx);
+
+  for (idx = priv->rxq->last_used_idx; idx != priv->rxq->used->idx; idx++)
+    {
+      uint16_t id  = idx % priv->rxq->len;
+      uint16_t d1  = priv->rxq->used->ring[id].id; /* index for header */
+      uint16_t d2  = priv->rxq->desc[d1].next;     /* index for packet */
+      uint32_t len = priv->rxq->used->ring[id].len;
+      DEBUGASSERT(d2 == (d1 + 1));
+
+      vrtinfo("+++ idx=%d id=%d used->idx=%d: d1=%d d2=%d len=%" PRId32 "\n",
+              idx, id, priv->rxq->used->idx, d1, d2, len);
+
+      /* Set the packet info to d_buf and set d_len */
+
+      priv->vnet_dev.d_buf = priv->rxq->desc_virt[d2];
+      priv->vnet_dev.d_len = len - VIRTIO_NET_HDRLEN;
+
+      vrtinfo("Receiving packet, pktlen: %d\n", priv->vnet_dev.d_len);
+
+      /* Dispatch the incoming packet */
+
+      virtnet_rxdispatch(priv);
+
+      /* Set the descriptor back into the avail queue */
+
+      id = priv->rxq->avail->idx % priv->rxq->len;
+      priv->rxq->avail->ring[id] = d1;
+
+      mb();
+      priv->rxq->avail->idx += 1;
+
+      vrtinfo("+++ rxq->avail->idx=%d\n", priv->rxq->avail->idx);
+    }
+
+  priv->rxq->last_used_idx = priv->rxq->used->idx;
+  vrtinfo("+++ rxq->last_used_idx=%d\n",  priv->rxq->last_used_idx);
+}
+
+/****************************************************************************
+ * Name: virtnet_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef TODO
+static void virtnet_txdone(FAR struct virtnet_driver_s *priv)
+{
+  /* Check for errors and update statistics */
+
+  NETDEV_TXDONE(priv->vnet_dev);
+
+  /* Check if there are pending transmissions */
+
+  /* If no further transmissions are pending, then cancel the TX timeout and
+   * disable further Tx interrupts.
+   */
+
+  wd_cancel(&priv->vnet_txtimeout);
+
+  /* And disable further TX interrupts. */
+
+  /* In any event, poll the network for new TX data */
+
+  devif_poll(&priv->vnet_dev, virtnet_txpoll);
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Runs on a worker thread.
+ *
+ ****************************************************************************/
+
+static void virtnet_interrupt_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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();
+
+  /* Process pending Ethernet interrupts */
+
+  /* Handle interrupts according to status bit settings */
+
+  /* Check if we received an incoming packet, if so, call virtnet_receive() */
+
+  virtnet_receive(priv);
+
+  /* Check if a packet transmission just completed.
+   * If so, call virtnet_txdone.
+   * This may disable further Tx interrupts if there are no pending
+   * transmissions.
+   */
+
+#ifdef TODO
+  virtnet_txdone(priv);
+#endif
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  priv->rxq->avail->flags = 0;
+  priv->txq->avail->flags = 0;
+  mb();
+}
+
+/****************************************************************************
+ * Name: virtnet_interrupt
+ *
+ * Description:
+ *   Hardware interrupt handler
+ *
+ * Input Parameters:
+ *   irq     - Number of the IRQ that generated the interrupt
+ *   context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Runs in the context of a the Ethernet interrupt handler.  Local
+ *   interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static int virtnet_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg;
+  uint32_t stat;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Get and clear interrupt status bits */
+
+  stat = virtio_getreg32(&priv->regs->interrupt_status);
+  virtio_putreg32(stat, &priv->regs->interrupt_ack);
+  vrtinfo("+++ called (stat=0x%" PRIx32 ")\n", stat);
+
+  /* Disable further Ethernet interrupts.  Because Ethernet interrupts are
+   * also disabled if the TX timeout event occurs, there can be no race
+   * condition here.
+   */
+
+  priv->rxq->avail->flags = 1;
+  priv->txq->avail->flags = 1;
+  mb();
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->vnet_irqwork, virtnet_interrupt_work, priv, 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ ****************************************************************************/
+
+static void virtnet_txtimeout_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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();
+
+  /* Increment statistics and dump debug info */
+
+  NETDEV_TXTIMEOUTS(priv->vnet_dev);
+
+  /* Then reset the hardware */
+
+  /* Then poll the network for new XMIT data */
+
+  devif_poll(&priv->vnet_dev, virtnet_txpoll);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: virtnet_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs in the context of a the timer interrupt handler.  Local
+ *   interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static void virtnet_txtimeout_expiry(wdparm_t arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg;
+
+#ifdef TODO
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+#endif
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->vnet_irqwork, virtnet_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Name: virtnet_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  vrtinfo("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
+  vrtinfo("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
+
+  /* Initialize PHYs, Ethernet interface, and setup up Ethernet interrupts */
+
+  /* Instantiate MAC address from
+   * priv->vnet_dev.d_mac.ether.ether_addr_octet
+   */
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  virtnet_ipv6multicast(priv);
+#endif
+
+  /* Enable the Ethernet interrupt */
+
+  priv->vnet_bifup = true;
+  up_enable_irq(priv->irq);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->irq);
+
+  /* Cancel the TX timeout timers */
+
+  wd_cancel(&priv->vnet_txtimeout);
+
+  /* Put the EMAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the virtnet_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  /* Mark the device "down" */
+
+  priv->vnet_bifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void virtnet_txavail_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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->vnet_bifup)
+    {
+      /* Check if there is room in the hardware to hold another packet. */
+
+      /* If so, then poll the network for new XMIT data */
+
+      devif_poll(&priv->vnet_dev, virtnet_txpoll);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: virtnet_txavail
+ *
+ * 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.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* 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.
+   */
+
+  if (work_available(&priv->vnet_pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->vnet_pollwork,
+                 virtnet_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int virtnet_addmac(FAR struct net_driver_s *dev,
+                          FAR const uint8_t *mac)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  UNUSED(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_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:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int virtnet_rmmac(FAR struct net_driver_s *dev,
+                         FAR const uint8_t *mac)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  UNUSED(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t tmp16;
+  uint8_t mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  vrtinfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  virtnet_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  virtnet_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  virtnet_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Name: virtnet_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int virtnet_ioctl(FAR struct net_driver_s *dev, int cmd,
+                         unsigned long arg)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+  int ret;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+      /* Add cases here to support the IOCTL commands */
+
+      default:
+        vrterr("ERROR: Unrecognized IOCTL command: %d\n", command);
+        return -ENOTTY;  /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_initialize
+ *
+ * Description:
+ *   Initialize the virt-net driver
+ *
+ * Input Parameters:
+ *
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called early in initialization before multi-tasking is initiated.
+ *
+ ****************************************************************************/
+
+static int virtnet_initialize(FAR struct virtio_mmio_regs *regs, int irq)
+{
+  FAR struct virtnet_driver_s *priv;
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(g_ninterfaces < VIRTIO_NET_NINTERFACES);
+  priv = &g_virtnet[g_ninterfaces];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct virtnet_driver_s));
+
+  /* Check if a Ethernet chip is recognized at its I/O base */
+
+  /* Attach the IRQ to the driver */
+
+  priv->irq = irq;
+
+  if (irq_attach(priv->irq, virtnet_interrupt, priv))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  /* Setup virtio related */
+
+  priv->regs = regs;
+
+  priv->txq  = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN);
+  priv->rxq  = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN);
+
+  virtq_add_to_device(regs, priv->rxq, VIRTIO_NET_Q_RX);
+  virtq_add_to_device(regs, priv->txq, VIRTIO_NET_Q_TX);
+
+  /* Prepare packets for rxq */
+
+  virtnet_add_packets_to_rxq(priv);
+
+  priv->vnet_dev.d_buf     = (FAR uint8_t *)g_pktbuf[g_ninterfaces];  /* Single packet buffer */
+
+  priv->vnet_dev.d_ifup    = virtnet_ifup;               /* I/F up (new IP address) callback */
+  priv->vnet_dev.d_ifdown  = virtnet_ifdown;             /* I/F down callback */
+  priv->vnet_dev.d_txavail = virtnet_txavail;            /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->vnet_dev.d_addmac  = virtnet_addmac;             /* Add multicast MAC address */
+  priv->vnet_dev.d_rmmac   = virtnet_rmmac;              /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->vnet_dev.d_ioctl   = virtnet_ioctl;              /* Handle network IOCTL commands */
+#endif
+  priv->vnet_dev.d_private = g_virtnet;                  /* Used to recover private state from dev */
+
+#ifdef TODO
+  /* Put the interface in the down state.  This usually amounts to resetting
+   * the device and/or calling virtnet_ifdown().
+   */
+
+  /* Read the MAC address from the hardware into
+   * priv->vnet_dev.d_mac.ether.ether_addr_octet
+   * Applies only if the Ethernet MAC has its own internal address.
+   */
+#endif
+
+  /* Register the device with the OS so that socket IOCTLs can be performed */
+
+  netdev_register(&priv->vnet_dev, NET_LL_ETHERNET);
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_init
+ *
+ * Description:
+ *   Called from virtio-mmio.c to initialize virtnet
+ *
+ ****************************************************************************/
+
+int virtnet_init(FAR struct virtio_mmio_regs *regs, uint32_t irq)
+{
+  int ret = OK;
+
+  /* TODO: feature negotiation */
+
+  /* Set STATUS_FEATURE_OK */
+
+  virtio_putreg32(virtio_getreg32(&regs->status) | VIRTIO_STATUS_FEATURES_OK,
+           &regs->status);

Review Comment:
   align to (



##########
drivers/virtio/virtio-net.c:
##########
@@ -0,0 +1,1340 @@
+/****************************************************************************
+ * drivers/virtio/virtio-net.c
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/virtio/virtio-mmio.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#ifdef CONFIG_DRIVERS_VIRTIO_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEBUG
+
+/* 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 ETHWORK LPWORK
+
+/* VIRTIO_NET_NINTERFACES determines the number of
+ * physical interfaces that will be supported.
+ */
+
+#ifndef VIRTIO_NET_NINTERFACES
+# define VIRTIO_NET_NINTERFACES 1
+#endif
+
+/* TX timeout = 1 minute */
+
+#define VIRTNET_TXTIMEOUT (60*CLK_TCK)
+
+/* Packet buffer size */
+
+#define PKTBUF_SIZE (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE)
+
+/* This is a helper pointer for accessing the contents of Ethernet header */
+
+#define BUF ((struct eth_hdr_s *)priv->vnet_dev.d_buf)
+
+/* virtio net related definition */
+
+#define VIRTIO_NET_HDRLEN 10
+
+#define VIRTIO_NET_Q_RX 0
+#define VIRTIO_NET_Q_TX 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct virtnet_hdr_s
+{
+  uint8_t  flags;
+  uint8_t  gso_type;
+  uint16_t hdr_len;
+  uint16_t gso_size;
+  uint16_t csum_start;
+  uint16_t csum_offset;
+};
+
+/* The virtnet_driver_s encapsulates all state information for
+ * a single hardware interface
+ */
+
+struct virtnet_driver_s
+{
+  bool vnet_bifup;               /* true:ifup false:ifdown */
+  int  irq;
+
+  FAR struct virtio_mmio_regs *regs; /* virtio_mmio registers */
+  FAR struct virtqueue   *txq;       /* TX queue */
+  FAR struct virtqueue   *rxq;       /* RX queue */
+
+  struct wdog_s vnet_txtimeout;  /* TX timeout timer */
+  struct work_s vnet_irqwork;    /* For deferring interrupt work to the work queue */
+  struct work_s vnet_pollwork;   /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s vnet_dev;  /* Interface understood by the network */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* These statically allocated structures would mean that only a single
+ * instance of the device could be supported.  In order to support multiple
+ * devices instances, this data would have to be allocated dynamically.
+ */
+
+/* A single packet buffer per device is used in this example.  There might
+ * be multiple packet buffers in a more complex, pipelined design.  Many
+ * contemporary Ethernet interfaces, for example,  use multiple, linked DMA
+ * descriptors in rings to implement such a pipeline.  This example assumes
+ * much simpler hardware that simply handles one packet at a time.
+ *
+ * NOTE that if VIRTIO_NET_NINTERFACES were greater than 1,
+ * you would need a minimum on one packet buffer per instance.
+ * Much better to be allocated dynamically in cases where more than
+ * one are needed.
+ */
+
+static uint16_t
+  g_pktbuf[VIRTIO_NET_NINTERFACES][(PKTBUF_SIZE + 1) / 2];
+
+/* Driver state structure */
+
+static struct virtnet_driver_s g_virtnet[VIRTIO_NET_NINTERFACES];
+
+static uint32_t g_ninterfaces = 0;
+
+/* Common virtnet header */
+
+static struct virtnet_hdr_s g_hdr;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int  virtnet_transmit(FAR struct virtnet_driver_s *priv);
+static int  virtnet_txpoll(FAR struct net_driver_s *dev);
+
+/* Interrupt handling */
+
+static void virtnet_reply(FAR struct virtnet_driver_s *priv);
+static void virtnet_receive(FAR struct virtnet_driver_s *priv);
+
+#ifdef TODO
+static void virtnet_txdone(FAR struct virtnet_driver_s *priv);
+#endif
+
+static void virtnet_interrupt_work(FAR void *arg);
+static int  virtnet_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/* Watchdog timer expirations */
+
+static void virtnet_txtimeout_work(FAR void *arg);
+static void virtnet_txtimeout_expiry(FAR wdparm_t arg);
+
+/* NuttX callback functions */
+
+static int  virtnet_ifup(FAR struct net_driver_s *dev);
+static int  virtnet_ifdown(FAR struct net_driver_s *dev);
+
+static void virtnet_txavail_work(FAR void *arg);
+static int  virtnet_txavail(FAR struct net_driver_s *dev);
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int  virtnet_addmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac);
+#ifdef CONFIG_NET_MCASTGROUP
+static int  virtnet_rmmac(FAR struct net_driver_s *dev,
+                          FAR const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_ICMPv6
+static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv);
+#endif
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+static int  virtnet_ioctl(FAR struct net_driver_s *dev, int cmd,
+                          unsigned long arg);
+#endif
+
+static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_cmd_status
+ ****************************************************************************/
+
+#ifdef DEBUG
+static void virtnet_cmd_status(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct virtio_mmio_regs *regs = priv->regs;
+
+  vrtinfo("virtnet at %p\n", regs);
+  vrtinfo("  status=0x%" PRIx32 "\n",
+          virtio_getreg32(&regs->status));
+  vrtinfo("  interrupt_status=0x%" PRIx32 "\n",
+          virtio_getreg32(&regs->interrupt_status));
+
+  virtio_putreg32(VIRTIO_NET_Q_TX, &regs->queue_sel);
+  mb();
+
+  vrtinfo("  txq: avail->idx=%d, used->idx=%d, ready=0x%" PRIx32 "\n",
+          priv->txq->avail->idx, priv->txq->used->idx,
+          virtio_getreg32(&regs->queue_ready));
+
+  virtio_putreg32(VIRTIO_NET_Q_RX, &regs->queue_sel);
+  mb();
+
+  vrtinfo("  rxq: avail->idx=%d used->idx=%d ready=0x%" PRIx32 "\n",
+          priv->rxq->avail->idx, priv->rxq->used->idx,
+          virtio_getreg32(&regs->queue_ready));
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_add_packets_to_rxq
+ ****************************************************************************/
+
+static void virtnet_add_packets_to_rxq(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct virtqueue *rxq = priv->rxq;
+  FAR uint8_t  *pkt;
+  uint16_t d1;
+  uint16_t d2;
+  uint32_t i;
+
+  vrtinfo("+++ rxq->len=%" PRId32 "  rxq->avail-idx=%d \n",
+          rxq->len, rxq->avail->idx);
+  DEBUGASSERT(rxq->avail->idx == 0);
+
+  for (i = 0; i < rxq->len / 2; i++)
+    {
+      pkt = kmm_memalign(16, PKTBUF_SIZE);
+      ASSERT(pkt);
+
+      /* Allocate new descriptors for header and packet */
+
+      d1 = virtq_alloc_desc(rxq, (i * 2),  (FAR void *)&g_hdr);
+      d2 = virtq_alloc_desc(rxq, (i * 2) + 1, pkt);
+
+      /* Set up the descriptor for header */
+
+      rxq->desc[d1].len   = VIRTIO_NET_HDRLEN;
+      rxq->desc[d1].flags = VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT;
+      rxq->desc[d1].next  = d2;
+
+      /* Set up the descriptor for packet */
+
+      rxq->desc[d2].len   = PKTBUF_SIZE;
+      rxq->desc[d2].flags = VIRTQ_DESC_F_WRITE;
+
+      /* Set the first descriptor to the avail->ring */
+
+      rxq->avail->ring[i] = d1;
+
+      /* Increment the avail->idx for each two descriptors */
+
+      mb();
+      rxq->avail->idx += 1;
+    }
+
+  vrtinfo("+++ virtq->avail-idx=%d \n", rxq->avail->idx);
+  vrtinfo("+++ virtq->used->idx=%d \n", rxq->used->idx);
+}
+
+/****************************************************************************
+ * Name: virtnet_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the txdone interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_transmit(FAR struct virtnet_driver_s *priv)
+{
+  FAR void *pkt = priv->vnet_dev.d_buf;
+  uint32_t len  = priv->vnet_dev.d_len;
+  uint16_t idx  = priv->txq->avail->idx;
+  uint16_t d1;
+  uint16_t d2;
+
+  /* Send the packet:
+   * address=priv->vnet_dev.d_buf, length=priv->vnet_dev.d_len
+   */
+
+  /* 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.
+   */
+
+  /* Increment statistics */
+
+  NETDEV_TXPACKETS(priv->vnet_dev);
+
+  vrtinfo("=== Sending packet, length: %d\n", priv->vnet_dev.d_len);
+
+#ifdef DEBUG
+  virtnet_cmd_status(priv);
+#endif
+
+  /* Allocate new descriptors for header and packet */
+
+  d1 = virtq_alloc_desc(priv->txq, idx, (FAR void *)&g_hdr);
+  d2 = virtq_alloc_desc(priv->txq, idx + 1, pkt);
+  DEBUGASSERT(d1 != d2);
+
+  /* Set up the descriptor for header */
+
+  priv->txq->desc[d2].len   = len;
+  priv->txq->desc[d2].flags = 0;
+
+  /* Set up the descriptor for packet */
+
+  priv->txq->desc[d1].len   = VIRTIO_NET_HDRLEN;
+  priv->txq->desc[d1].flags = VIRTQ_DESC_F_NEXT;
+  priv->txq->desc[d1].next  = d2;
+
+  /* Set the first descriptor to the avail->ring */
+
+  priv->txq->avail->ring[d1] = d1;
+
+  /* Increment the avail->idx for each two descriptors */
+
+  mb();
+  priv->txq->avail->idx += 1;
+
+  vrtinfo("*** d1=%d, d2=%d, txq->avail->idx=%d\n",
+          d1, d2, priv->txq->avail->idx);
+
+  virtio_putreg32(VIRTIO_NET_Q_TX, &priv->regs->queue_notify);
+
+  vrtinfo("*** finish updating queue_notify\n");
+
+#ifdef TODO
+  /* Enable Tx interrupts */
+#endif
+
+  /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->vnet_txtimeout, VIRTNET_TXTIMEOUT,
+           virtnet_txtimeout_expiry, (wdparm_t)priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txpoll
+ *
+ * Description:
+ *   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,
+ *   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
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_txpoll(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  vrtinfo("Poll result: d_len=%d\n", priv->vnet_dev.d_len);
+
+  /* Send the packet */
+
+  virtnet_reply(priv);
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: virtnet_reply
+ *
+ * Description:
+ *   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:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void virtnet_reply(FAR struct virtnet_driver_s *priv)
+{
+  /* 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 (priv->vnet_dev.d_len > 0)
+    {
+      /* And send the packet */
+
+      virtnet_transmit(priv);
+    }
+}
+
+/****************************************************************************
+ * Function: virtnet_rxdispatch
+ *
+ * Description:
+ *   A new Rx packet was received; dispatch that packet to the network layer
+ *   as necessary.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv)
+{
+  /* Update statistics */
+
+  NETDEV_RXPACKETS(&priv->dev);
+
+#ifdef CONFIG_NET_PKT
+  /* When packet sockets are enabled, feed the frame into the tap */
+
+  pkt_input(&priv->vnet_dev);
+#endif
+
+#ifdef CONFIG_NET_IPv4
+  /* Check for an IPv4 packet */
+
+  if (BUF->type == HTONS(ETHTYPE_IP))
+    {
+      vrtinfo("IPv4 frame\n");
+      NETDEV_RXIPV4(&priv->vnet_dev);
+
+      /* Receive an IPv4 packet from the network device */
+
+      vrtinfo("+++ call ipv4_input() d_len=%d\n", priv->vnet_dev.d_len);
+      ipv4_input(&priv->vnet_dev);
+
+      /* Check for a reply to the IPv4 packet */
+
+      virtnet_reply(priv);
+    }
+  else
+#endif
+#ifdef CONFIG_NET_IPv6
+  /* Check for an IPv6 packet */
+
+  if (BUF->type == HTONS(ETHTYPE_IP6))
+    {
+      vrtinfo("IPv6 frame\n");
+      NETDEV_RXIPV6(&priv->vnet_dev);
+
+      /* Dispatch IPv6 packet to the network layer */
+
+      ipv6_input(&priv->vnet_dev);
+
+      /* Check for a reply to the IPv6 packet */
+
+      virtnet_reply(priv);
+    }
+  else
+#endif
+#ifdef CONFIG_NET_ARP
+  /* Check for an ARP packet */
+
+  if (BUF->type == HTONS(ETHTYPE_ARP))
+    {
+      /* Dispatch ARP packet to the network layer */
+
+      arp_arpin(&priv->vnet_dev);
+      NETDEV_RXARP(&priv->vnet_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.
+       */
+
+      if (priv->vnet_dev.d_len > 0)
+        {
+          virtnet_transmit(priv);
+        }
+    }
+  else
+#endif
+    {
+      NETDEV_RXDROPPED(&priv->vnet_dev);
+
+      vrtwarn("+++ dropped BUF->type=0x%x \n", BUF->type);
+      DEBUGASSERT(0 != BUF->type);
+    }
+}
+
+/****************************************************************************
+ * Name: virtnet_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of a new RX packet
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void virtnet_receive(FAR struct virtnet_driver_s *priv)
+{
+  uint16_t idx;
+
+  vrtinfo("+++ rxq->last_used_idx=%d: rxq->used->idx=%d\n",
+          priv->rxq->last_used_idx, priv->rxq->used->idx);
+
+  for (idx = priv->rxq->last_used_idx; idx != priv->rxq->used->idx; idx++)
+    {
+      uint16_t id  = idx % priv->rxq->len;
+      uint16_t d1  = priv->rxq->used->ring[id].id; /* index for header */
+      uint16_t d2  = priv->rxq->desc[d1].next;     /* index for packet */
+      uint32_t len = priv->rxq->used->ring[id].len;
+      DEBUGASSERT(d2 == (d1 + 1));
+
+      vrtinfo("+++ idx=%d id=%d used->idx=%d: d1=%d d2=%d len=%" PRId32 "\n",
+              idx, id, priv->rxq->used->idx, d1, d2, len);
+
+      /* Set the packet info to d_buf and set d_len */
+
+      priv->vnet_dev.d_buf = priv->rxq->desc_virt[d2];
+      priv->vnet_dev.d_len = len - VIRTIO_NET_HDRLEN;
+
+      vrtinfo("Receiving packet, pktlen: %d\n", priv->vnet_dev.d_len);
+
+      /* Dispatch the incoming packet */
+
+      virtnet_rxdispatch(priv);
+
+      /* Set the descriptor back into the avail queue */
+
+      id = priv->rxq->avail->idx % priv->rxq->len;
+      priv->rxq->avail->ring[id] = d1;
+
+      mb();
+      priv->rxq->avail->idx += 1;
+
+      vrtinfo("+++ rxq->avail->idx=%d\n", priv->rxq->avail->idx);
+    }
+
+  priv->rxq->last_used_idx = priv->rxq->used->idx;
+  vrtinfo("+++ rxq->last_used_idx=%d\n",  priv->rxq->last_used_idx);
+}
+
+/****************************************************************************
+ * Name: virtnet_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef TODO
+static void virtnet_txdone(FAR struct virtnet_driver_s *priv)
+{
+  /* Check for errors and update statistics */
+
+  NETDEV_TXDONE(priv->vnet_dev);
+
+  /* Check if there are pending transmissions */
+
+  /* If no further transmissions are pending, then cancel the TX timeout and
+   * disable further Tx interrupts.
+   */
+
+  wd_cancel(&priv->vnet_txtimeout);
+
+  /* And disable further TX interrupts. */
+
+  /* In any event, poll the network for new TX data */
+
+  devif_poll(&priv->vnet_dev, virtnet_txpoll);
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Runs on a worker thread.
+ *
+ ****************************************************************************/
+
+static void virtnet_interrupt_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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();
+
+  /* Process pending Ethernet interrupts */
+
+  /* Handle interrupts according to status bit settings */
+
+  /* Check if we received an incoming packet, if so, call virtnet_receive() */
+
+  virtnet_receive(priv);
+
+  /* Check if a packet transmission just completed.
+   * If so, call virtnet_txdone.
+   * This may disable further Tx interrupts if there are no pending
+   * transmissions.
+   */
+
+#ifdef TODO
+  virtnet_txdone(priv);
+#endif
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  priv->rxq->avail->flags = 0;
+  priv->txq->avail->flags = 0;
+  mb();
+}
+
+/****************************************************************************
+ * Name: virtnet_interrupt
+ *
+ * Description:
+ *   Hardware interrupt handler
+ *
+ * Input Parameters:
+ *   irq     - Number of the IRQ that generated the interrupt
+ *   context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Runs in the context of a the Ethernet interrupt handler.  Local
+ *   interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static int virtnet_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg;
+  uint32_t stat;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Get and clear interrupt status bits */
+
+  stat = virtio_getreg32(&priv->regs->interrupt_status);
+  virtio_putreg32(stat, &priv->regs->interrupt_ack);
+  vrtinfo("+++ called (stat=0x%" PRIx32 ")\n", stat);
+
+  /* Disable further Ethernet interrupts.  Because Ethernet interrupts are
+   * also disabled if the TX timeout event occurs, there can be no race
+   * condition here.
+   */
+
+  priv->rxq->avail->flags = 1;
+  priv->txq->avail->flags = 1;
+  mb();
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->vnet_irqwork, virtnet_interrupt_work, priv, 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ ****************************************************************************/
+
+static void virtnet_txtimeout_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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();
+
+  /* Increment statistics and dump debug info */
+
+  NETDEV_TXTIMEOUTS(priv->vnet_dev);
+
+  /* Then reset the hardware */
+
+  /* Then poll the network for new XMIT data */
+
+  devif_poll(&priv->vnet_dev, virtnet_txpoll);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: virtnet_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs in the context of a the timer interrupt handler.  Local
+ *   interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static void virtnet_txtimeout_expiry(wdparm_t arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg;
+
+#ifdef TODO
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+#endif
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->vnet_irqwork, virtnet_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Name: virtnet_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  vrtinfo("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
+  vrtinfo("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
+
+  /* Initialize PHYs, Ethernet interface, and setup up Ethernet interrupts */
+
+  /* Instantiate MAC address from
+   * priv->vnet_dev.d_mac.ether.ether_addr_octet
+   */
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  virtnet_ipv6multicast(priv);
+#endif
+
+  /* Enable the Ethernet interrupt */
+
+  priv->vnet_bifup = true;
+  up_enable_irq(priv->irq);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->irq);
+
+  /* Cancel the TX timeout timers */
+
+  wd_cancel(&priv->vnet_txtimeout);
+
+  /* Put the EMAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the virtnet_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  /* Mark the device "down" */
+
+  priv->vnet_bifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void virtnet_txavail_work(FAR void *arg)
+{
+  FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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->vnet_bifup)
+    {
+      /* Check if there is room in the hardware to hold another packet. */
+
+      /* If so, then poll the network for new XMIT data */
+
+      devif_poll(&priv->vnet_dev, virtnet_txpoll);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: virtnet_txavail
+ *
+ * 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.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int virtnet_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* 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.
+   */
+
+  if (work_available(&priv->vnet_pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->vnet_pollwork,
+                 virtnet_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtnet_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int virtnet_addmac(FAR struct net_driver_s *dev,
+                          FAR const uint8_t *mac)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  UNUSED(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_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:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int virtnet_rmmac(FAR struct net_driver_s *dev,
+                         FAR const uint8_t *mac)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  UNUSED(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t tmp16;
+  uint8_t mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  vrtinfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  virtnet_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  virtnet_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  virtnet_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Name: virtnet_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int virtnet_ioctl(FAR struct net_driver_s *dev, int cmd,
+                         unsigned long arg)
+{
+  FAR struct virtnet_driver_s *priv =
+    (FAR struct virtnet_driver_s *)dev->d_private;
+  int ret;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+      /* Add cases here to support the IOCTL commands */
+
+      default:
+        vrterr("ERROR: Unrecognized IOCTL command: %d\n", command);
+        return -ENOTTY;  /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtnet_initialize
+ *
+ * Description:
+ *   Initialize the virt-net driver
+ *
+ * Input Parameters:
+ *
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called early in initialization before multi-tasking is initiated.
+ *
+ ****************************************************************************/
+
+static int virtnet_initialize(FAR struct virtio_mmio_regs *regs, int irq)
+{
+  FAR struct virtnet_driver_s *priv;
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(g_ninterfaces < VIRTIO_NET_NINTERFACES);
+  priv = &g_virtnet[g_ninterfaces];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct virtnet_driver_s));
+
+  /* Check if a Ethernet chip is recognized at its I/O base */
+
+  /* Attach the IRQ to the driver */
+
+  priv->irq = irq;
+
+  if (irq_attach(priv->irq, virtnet_interrupt, priv))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  /* Setup virtio related */
+
+  priv->regs = regs;
+
+  priv->txq  = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN);
+  priv->rxq  = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN);
+
+  virtq_add_to_device(regs, priv->rxq, VIRTIO_NET_Q_RX);
+  virtq_add_to_device(regs, priv->txq, VIRTIO_NET_Q_TX);
+
+  /* Prepare packets for rxq */
+
+  virtnet_add_packets_to_rxq(priv);
+
+  priv->vnet_dev.d_buf     = (FAR uint8_t *)g_pktbuf[g_ninterfaces];  /* Single packet buffer */
+
+  priv->vnet_dev.d_ifup    = virtnet_ifup;               /* I/F up (new IP address) callback */
+  priv->vnet_dev.d_ifdown  = virtnet_ifdown;             /* I/F down callback */
+  priv->vnet_dev.d_txavail = virtnet_txavail;            /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->vnet_dev.d_addmac  = virtnet_addmac;             /* Add multicast MAC address */
+  priv->vnet_dev.d_rmmac   = virtnet_rmmac;              /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->vnet_dev.d_ioctl   = virtnet_ioctl;              /* Handle network IOCTL commands */
+#endif
+  priv->vnet_dev.d_private = g_virtnet;                  /* Used to recover private state from dev */
+
+#ifdef TODO
+  /* Put the interface in the down state.  This usually amounts to resetting
+   * the device and/or calling virtnet_ifdown().
+   */
+
+  /* Read the MAC address from the hardware into
+   * priv->vnet_dev.d_mac.ether.ether_addr_octet
+   * Applies only if the Ethernet MAC has its own internal address.
+   */
+#endif
+
+  /* Register the device with the OS so that socket IOCTLs can be performed */
+
+  netdev_register(&priv->vnet_dev, NET_LL_ETHERNET);
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtnet_init
+ *
+ * Description:
+ *   Called from virtio-mmio.c to initialize virtnet
+ *
+ ****************************************************************************/
+
+int virtnet_init(FAR struct virtio_mmio_regs *regs, uint32_t irq)
+{
+  int ret = OK;
+
+  /* TODO: feature negotiation */
+
+  /* Set STATUS_FEATURE_OK */
+
+  virtio_putreg32(virtio_getreg32(&regs->status) | VIRTIO_STATUS_FEATURES_OK,
+           &regs->status);
+  mb();
+
+  ret = virtnet_initialize(regs, irq);
+
+  if (OK != ret)
+    {
+      vrterr("error: virtnet_initialize() returned %d \n", ret);
+      return ret;
+    }
+
+  /* Set STATUS_FRIVER_OK */
+
+  virtio_putreg32(virtio_getreg32(&regs->status) | VIRTIO_STATUS_DRIVER_OK,
+           &regs->status);

Review Comment:
   align to (



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034254226


##########
include/nuttx/virtio/virtio-mmio.h:
##########
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-mmio.h
+ *
+ * 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_VIRTIO_VIRTIO_MMIO_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_MMIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arch/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* See virtio-v1.1-csprd01.pdf */
+
+#define VIRTIO_MAGIC    0x74726976
+#define VIRTIO_VERSION  0x2    /* NOTE: Legacy devices used 0x1 */
+
+#define VIRTIO_DEV_NET  0x1
+
+#define VIRTIO_STATUS_ACKNOWLEDGE   (1)
+#define VIRTIO_STATUS_DRIVER        (2)
+#define VIRTIO_STATUS_DRIVER_OK     (4)
+#define VIRTIO_STATUS_FEATURES_OK   (8)
+
+#define VIRTQ_DESC_F_NEXT  1   /* marks a buffer as continuing */
+#define VIRTQ_DESC_F_WRITE 2   /* marks a buffer as device write-only */
+
+#define mb() SP_DMB()
+
+#define virtio_getreg32(a)    (FAR *(volatile FAR uint32_t *)(a))
+#define virtio_putreg32(v,a)  (FAR *(volatile FAR uint32_t *)(a) = (v))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Table 4.1 MMIO Device Register Layout */
+
+struct virtio_mmio_regs
+{
+  uint32_t magic_value;           /* offset:0x000 */
+  uint32_t version;               /* offset:0x004 */
+  uint32_t device_id;             /* offset:0x008 */
+  uint32_t vendor_id;             /* offset:0x00c */
+  uint32_t device_features;       /* offset:0x010 */
+  uint32_t device_features_sel;   /* offset:0x014 */
+  uint32_t _reserved0[2];
+  uint32_t driver_features;       /* offset:0x020 */
+  uint32_t driver_features_sel;   /* offset:0x024 */
+  uint32_t _reserved1[2];
+  uint32_t queue_sel;             /* offset:0x030 */
+  uint32_t queue_num_max;         /* offset:0x034 */
+  uint32_t queue_num;             /* offset:0x038 */
+  uint32_t _reserved2[2];
+  uint32_t queue_ready;           /* offset:0x044 */
+  uint32_t _reserved3[2];
+  uint32_t queue_notify;          /* offset:0x050 */
+  uint32_t _reserved4[3];
+  uint32_t interrupt_status;      /* offset:0x060 */
+  uint32_t interrupt_ack;         /* offset:0x064 */
+  uint32_t _reserved5[2];
+  uint32_t status;                /* offset:0x070 */
+  uint32_t _reserved6[3];
+  uint32_t queue_desc_low;        /* offset:0x080 */
+  uint32_t queue_desc_high;       /* offset:0x084 */
+  uint32_t _reserved7[2];
+  uint32_t queue_avail_low;       /* offset:0x090 */
+  uint32_t queue_avail_high;      /* offset:0x094 */
+  uint32_t _reserved8[2];
+  uint32_t queue_used_low;        /* offset:0x0a0 */
+  uint32_t queue_used_high;       /* offset:0x0a4 */
+  uint32_t _reserved9[21];
+  uint32_t config_generation;     /* offset:0x0fc */
+  uint32_t config[0];
+};
+
+struct virtqueue_desc
+{
+  uint64_t addr;
+  uint32_t len;
+  uint16_t flags;
+  uint16_t next;
+};
+
+struct virtqueue_avail
+{
+  uint16_t flags;
+  uint16_t idx;
+  uint16_t ring[0];
+};
+
+struct virtqueue_used_elem
+{
+  uint32_t id;
+  uint32_t len;
+};
+
+struct virtqueue_used
+{
+  uint16_t flags;
+  uint16_t idx;
+  struct virtqueue_used_elem ring[0];
+};
+
+struct virtqueue
+{
+  uint32_t len;
+  uint16_t last_used_idx;
+
+  FAR struct virtqueue_desc *desc;
+  FAR struct virtqueue_avail *avail;
+  FAR struct virtqueue_used *used;
+  FAR uint16_t *avail_event;
+  FAR void **desc_virt;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+FAR struct virtqueue *virtq_create(uint32_t len);
+
+uint32_t virtq_alloc_desc(FAR struct virtqueue *virtq,
+                          uint32_t id,
+                          FAR void *addr);
+
+void virtq_add_to_device(FAR struct virtio_mmio_regs *regs,

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034253987


##########
drivers/virtio/Kconfig:
##########
@@ -0,0 +1,45 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig DRIVERS_VIRTIO
+	bool "Virtio Device Support"
+	depends on !OPENAMP
+	default n
+	---help---
+
+if DRIVERS_VIRTIO
+menuconfig DRIVERS_VIRTIO_MMIO_NUM
+	int "The number of virtio mmio devices"
+	default 1
+	---help---
+

Review Comment:
   done
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] jerpelea commented on pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
jerpelea commented on PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#issuecomment-1329983699

   virtio/virtio-mmio.c: In function 'virtio_mmio_init':
   Error: virtio/virtio-mmio.c:217:33: error: 'CONFIG_DRIVERS_VIRTIO_MMIO_BASE' undeclared (first use in this function)
     217 |   uintptr_t virtio = (uintptr_t)CONFIG_DRIVERS_VIRTIO_MMIO_BASE;
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   virtio/virtio-mmio.c:217:33: note: each undeclared identifier is reported only once for each function it appears in
   Error: virtio/virtio-mmio.c:218:19: error: 'CONFIG_DRIVERS_VIRTIO_MMIO_IRQ' undeclared (first use in this function)
     218 |   uint32_t  irq = CONFIG_DRIVERS_VIRTIO_MMIO_IRQ;
         |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   Error: virtio/virtio-mmio.c:219:20: error: 'CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE' undeclared (first use in this function)
     219 |   uint32_t  size = CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE;
         |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   Error: virtio/virtio-mmio.c:222:19: error: 'CONFIG_DRIVERS_VIRTIO_MMIO_NUM' undeclared (first use in this function)
     222 |   for (i = 0; i < CONFIG_DRIVERS_VIRTIO_MMIO_NUM; i++)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] masayuki2009 commented on a diff in pull request #7673: Add virtio-mmio and virtio-net drivers

Posted by GitBox <gi...@apache.org>.
masayuki2009 commented on code in PR #7673:
URL: https://github.com/apache/nuttx/pull/7673#discussion_r1034246872


##########
drivers/virtio/Make.defs:
##########
@@ -0,0 +1,34 @@
+############################################################################
+# drivers/virtio/Make.defs
+#
+# 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.
+#
+############################################################################
+
+# Include virtio support
+
+ifeq ($(CONFIG_DRIVERS_VIRTIO),y)

Review Comment:
   Hmm, I found that `ifneq ($(CONFIG_DRIVERS_VIRTIO_MMIO_NUM),0)` causes build errors if the config does not have virtio drivers. So I leave the above code.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org