]> Zhao Yanbai Git Server - minix.git/commitdiff
virtio: virtio-net driver
authorArne Welzel <arne.welzel@gmail.com>
Sat, 15 Dec 2012 12:55:07 +0000 (13:55 +0100)
committerBen Gras <ben@minix3.org>
Wed, 23 Jan 2013 09:03:03 +0000 (09:03 +0000)
drivers/Makefile
drivers/virtio_net/Makefile [new file with mode: 0644]
drivers/virtio_net/virtio_net.c [new file with mode: 0644]
drivers/virtio_net/virtio_net.conf [new file with mode: 0644]
drivers/virtio_net/virtio_net.h [new file with mode: 0644]

index 8e3e66e4c6fc22077052e0f864b1bb4c868c242c..38009216735715ed74ef8e8338211d860f2d9c6d 100644 (file)
@@ -19,7 +19,7 @@ SUBDIR= tty
 SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
        e1000 fbd filter floppy fxp hello lance log mmc orinoco pci printer \
        random readclock rtl8139 rtl8169 ti1225 tty vbox acpi \
-       virtio_blk
+       virtio_blk virtio_net
 .endif
 
 .if ${MACHINE_ARCH} == "earm"
diff --git a/drivers/virtio_net/Makefile b/drivers/virtio_net/Makefile
new file mode 100644 (file)
index 0000000..bc2f150
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# Makefile for virtio_net
+#
+PROG=  virtio_net
+SRCS=  virtio_net.c
+
+FILES=$(PROG).conf
+FILESNAME=$(PROG)
+FILESDIR= /etc/system.conf.d
+
+DPADD+=        ${LIBNETDRIVER} ${LIBSYS} ${LIBVIRTIO}
+LDADD+=        -lnetdriver -lsys -lvirtio
+
+MAN=
+
+BINDIR?= /usr/sbin
+
+.include <minix.service.mk>
diff --git a/drivers/virtio_net/virtio_net.c b/drivers/virtio_net/virtio_net.c
new file mode 100644 (file)
index 0000000..9f17495
--- /dev/null
@@ -0,0 +1,680 @@
+/* virtio net driver for MINIX 3
+ *
+ * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
+ *
+ * This software is released under the BSD license. See the LICENSE file
+ * included in the main directory of this source distribution for the
+ * license terms and conditions.
+ */
+
+#include <assert.h>
+#include <sys/types.h>
+
+#include <net/gen/ether.h>
+#include <net/gen/eth_io.h>
+
+#include <minix/drivers.h>
+#include <minix/netdriver.h>
+#include <minix/sysutil.h>
+#include <minix/virtio.h>
+
+#include <sys/queue.h>
+
+#include "virtio_net.h"
+
+#define dput(s)                do { dprintf(s); printf("\n"); } while (0)
+#define dprintf(s) do {                                                \
+       printf("%s: ", name);                                   \
+       printf s;                                               \
+} while (0)
+
+static struct virtio_device *net_dev;
+
+static const char *const name = "virtio-net";
+
+enum queue {RX_Q, TX_Q, CTRL_Q};
+
+/* Number of packets to work with */
+/* TODO: This should be an argument to the driver and possibly also
+ *       depend on the queue sizes offered by this device.
+ */
+#define BUF_PACKETS            64
+/* Maximum size of a packet */
+#define MAX_PACK_SIZE          ETH_MAX_PACK_SIZE
+/* Buffer size needed for the payload of BUF_PACKETS */
+#define PACKET_BUF_SZ          (BUF_PACKETS * MAX_PACK_SIZE)
+
+struct packet {
+       int idx;
+       struct virtio_net_hdr *vhdr;
+       phys_bytes phdr;
+       char *vdata;
+       phys_bytes pdata;
+       STAILQ_ENTRY(packet) next;
+};
+
+/* Allocated data chunks */
+static char *data_vir;
+static phys_bytes data_phys;
+static struct virtio_net_hdr *hdrs_vir;
+static phys_bytes hdrs_phys;
+static struct packet *packets;
+static int in_rx;
+static int started;
+
+/* Packets on this list can be given to the host */
+static STAILQ_HEAD(free_list, packet) free_list;
+
+/* Packets on this list are to be given to inet */
+static STAILQ_HEAD(recv_list, packet) recv_list;
+
+/* State about pending inet messages */
+static int rx_pending;
+static message pending_rx_msg;
+static int tx_pending;
+static message pending_tx_msg;
+
+/* Various state data */
+static u8_t virtio_net_mac[6];
+static eth_stat_t virtio_net_stats;
+static int spurious_interrupt;
+
+
+/* Prototypes */
+static int virtio_net_probe(int skip);
+static int virtio_net_config(void);
+static int virtio_net_alloc_bufs(void);
+static void virtio_net_init_queues(void);
+
+static void virtio_net_refill_rx_queue(void);
+static void virtio_net_check_queues(void);
+static void virtio_net_check_pending(void);
+
+static void virtio_net_fetch_iovec(iovec_s_t *iov, message *m);
+static int virtio_net_cpy_to_user(message *m);
+static int virtio_net_cpy_from_user(message *m);
+
+static void virtio_net_intr(message *m);
+static void virtio_net_write(message *m);
+static void virtio_net_read(message *m);
+static void virtio_net_conf(message *m);
+static void virtio_net_getstat(message *m);
+
+static void virtio_net_notify(message *m);
+static void virtio_net_msg(message *m);
+static void virtio_net_main_loop(void);
+
+static void sef_local_startup(void);
+static int sef_cb_init_fresh(int type, sef_init_info_t *info);
+static void sef_cb_signal_handler(int signo);
+
+
+/* TODO: Features are pretty much ignored */
+struct virtio_feature netf[] = {
+       { "partial csum",       VIRTIO_NET_F_CSUM,      0,      0       },
+       { "given mac",          VIRTIO_NET_F_MAC,       0,      0       },
+       { "status ",            VIRTIO_NET_F_STATUS,    0,      0       },
+       { "control channel",    VIRTIO_NET_F_CTRL_VQ,   0,      0       },
+       { "control channel rx", VIRTIO_NET_F_CTRL_RX,   0,      0       }
+};
+
+static int
+virtio_net_probe(int skip)
+{
+       /* virtio-net has at least 2 queues */
+       int queues = 2;
+       net_dev= virtio_setup_device(0x00001, name, netf,
+                                    sizeof(netf) / sizeof(netf[0]),
+                                    1 /* threads */, skip);
+       if (net_dev == NULL)
+               return ENXIO;
+
+       /* If the host supports the control queue, allocate it as well */
+       if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
+               queues += 1;
+
+       if (virtio_alloc_queues(net_dev, queues) != OK) {
+               virtio_free_device(net_dev);
+               return ENOMEM;
+       }
+
+       return OK;
+}
+
+static int
+virtio_net_config(void)
+{
+       u32_t mac14;
+       u32_t mac56;
+       int i;
+
+       if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) {
+               dprintf(("Mac set by host: "));
+               mac14 = virtio_sread32(net_dev, 0);
+               mac56 = virtio_sread32(net_dev, 4);
+               *(u32_t*)virtio_net_mac = mac14;
+               *(u16_t*)(virtio_net_mac + 4) = mac56;
+
+               for (i = 0; i < 6; i++)
+                       printf("%02x%s", virtio_net_mac[i],
+                                        i == 5 ? "\n" : ":");
+       } else {
+               dput(("No mac"));
+       }
+
+       if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) {
+               dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6)));
+       } else {
+               dput(("No status"));
+       }
+
+       if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
+               dput(("Host supports control channel"));
+
+       if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX))
+               dput(("Host supports control channel for RX"));
+
+       return OK;
+}
+
+static int
+virtio_net_alloc_bufs(void)
+{
+       data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys);
+
+       if (!data_vir)
+               return ENOMEM;
+
+       hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]),
+                                0, &hdrs_phys);
+
+       if (!hdrs_vir) {
+               free_contig(data_vir, PACKET_BUF_SZ);
+               return ENOMEM;
+       }
+
+       packets = malloc(BUF_PACKETS * sizeof(packets[0]));
+
+       if (!packets) {
+               free_contig(data_vir, PACKET_BUF_SZ);
+               free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
+               return ENOMEM;
+       }
+
+       memset(data_vir, 0, PACKET_BUF_SZ);
+       memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0]));
+       memset(packets, 0, BUF_PACKETS * sizeof(packets[0]));
+
+       return OK;
+}
+
+static void
+virtio_net_init_queues(void)
+{
+       int i;
+       STAILQ_INIT(&free_list);
+       STAILQ_INIT(&recv_list);
+
+       for (i = 0; i < BUF_PACKETS; i++) {
+               packets[i].idx = i;
+               packets[i].vhdr = &hdrs_vir[i];
+               packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]);
+               packets[i].vdata = data_vir + i * MAX_PACK_SIZE;
+               packets[i].pdata = data_phys + i * MAX_PACK_SIZE;
+               STAILQ_INSERT_HEAD(&free_list, &packets[i], next);
+       }
+}
+
+static void
+virtio_net_refill_rx_queue(void)
+{
+       struct vumap_phys phys[2];
+       struct packet *p;
+
+       while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) {
+
+               /* peek */
+               p = STAILQ_FIRST(&free_list);
+               /* remove */
+               STAILQ_REMOVE_HEAD(&free_list, next);
+
+               phys[0].vp_addr = p->phdr;
+               assert(!(phys[0].vp_addr & 1));
+               phys[0].vp_size = sizeof(struct virtio_net_hdr);
+
+               phys[1].vp_addr = p->pdata;
+               assert(!(phys[1].vp_addr & 1));
+               phys[1].vp_size = MAX_PACK_SIZE;
+
+               /* RX queue needs write */
+               phys[0].vp_addr |= 1;
+               phys[1].vp_addr |= 1;
+
+               virtio_to_queue(net_dev, RX_Q, phys, 2, p);
+               in_rx++;
+
+       }
+
+       if (in_rx == 0 && STAILQ_EMPTY(&free_list)) {
+               dput(("warning: rx queue underflow!"));
+               virtio_net_stats.ets_fifoUnder++;
+       }
+}
+
+static void
+virtio_net_check_queues(void)
+{
+       struct packet *p;
+
+       /* Put the received packets into the recv list */
+       while (virtio_from_queue(net_dev, RX_Q, (void **)&p) == 0) {
+               STAILQ_INSERT_TAIL(&recv_list, p, next);
+               in_rx--;
+               virtio_net_stats.ets_packetR++;
+       }
+
+       /* Packets from the TX queue just indicated they are free to
+        * be reused now. inet already knows about them as being sent.
+        */
+       while (virtio_from_queue(net_dev, TX_Q, (void **)&p) == 0) {
+               memset(p->vhdr, 0, sizeof(*p->vhdr));
+               memset(p->vdata, 0, MAX_PACK_SIZE);
+               STAILQ_INSERT_HEAD(&free_list, p, next);
+               virtio_net_stats.ets_packetT++;
+       }
+}
+
+static void
+virtio_net_check_pending(void)
+{
+       int dst = 0xDEAD;
+       int r;
+
+       message reply;
+       reply.m_type = DL_TASK_REPLY;
+       reply.DL_FLAGS = DL_NOFLAGS;
+       reply.DL_COUNT = 0;
+
+       /* Pending read and something in recv_list? */
+       if (!STAILQ_EMPTY(&recv_list) && rx_pending) {
+               dst = pending_rx_msg.m_source;
+               reply.DL_COUNT = virtio_net_cpy_to_user(&pending_rx_msg);
+               reply.DL_FLAGS |= DL_PACK_RECV;
+               rx_pending = 0;
+       }
+
+       if (!STAILQ_EMPTY(&free_list) && tx_pending) {
+               dst = pending_tx_msg.m_source;
+               virtio_net_cpy_from_user(&pending_tx_msg);
+               reply.DL_FLAGS |= DL_PACK_SEND;
+               tx_pending = 0;
+       }
+
+       /* Only reply if a pending request was handled */
+       if (reply.DL_FLAGS != DL_NOFLAGS)
+               if ((r = send(dst, &reply)) != OK)
+                       panic("%s: send to %d failed (%d)", name, dst, r);
+}
+
+static void
+virtio_net_fetch_iovec(iovec_s_t *iov, message *m)
+{
+       int r;
+       r = sys_safecopyfrom(m->m_source, m->DL_GRANT, 0, (vir_bytes)iov,
+                            m->DL_COUNT * sizeof(iov[0]));
+
+       if (r != OK)
+               panic("%s: iovec fail for %d (%d)", name, m->m_source, r);
+}
+
+static int
+virtio_net_cpy_to_user(message *m)
+{
+       /* Hmm, this looks so similar to cpy_from_user... TODO */
+       int i, r, size, ivsz;
+       int left = MAX_PACK_SIZE;       /* Try copying the whole packet */
+       int bytes = 0;
+       iovec_s_t iovec[NR_IOREQS];
+       struct packet *p;
+
+       /* This should only be called if recv_list has some entries */
+       assert(!STAILQ_EMPTY(&recv_list));
+
+       p = STAILQ_FIRST(&recv_list);
+       STAILQ_REMOVE_HEAD(&recv_list, next);
+
+       virtio_net_fetch_iovec(iovec, m);
+
+       for (i = 0; i < m->DL_COUNT && left > 0; i++) {
+               ivsz = iovec[i].iov_size;
+               size = left > ivsz ? ivsz : left;
+               r = sys_safecopyto(m->m_source, iovec[i].iov_grant, 0,
+                                  (vir_bytes) p->vdata + bytes, size);
+
+               if (r != OK)
+                       panic("%s: copy to %d failed (%d)", name,
+                                                           m->m_source,
+                                                           r);
+
+               left -= size;
+               bytes += size;
+       }
+
+       if (left != 0)
+               dput(("Uhm... left=%d", left));
+
+       /* Clean the packet */
+       memset(p->vhdr, 0, sizeof(*p->vhdr));
+       memset(p->vdata, 0, MAX_PACK_SIZE);
+       STAILQ_INSERT_HEAD(&free_list, p, next);
+
+       return bytes;
+}
+
+static int
+sys_easy_vsafecopy_from(endpoint_t src_proc, iovec_s_t *iov, int count,
+                       vir_bytes dst, size_t max, size_t *copied)
+{
+       int i, r;
+       size_t left = max;
+       vir_bytes cur_off = 0;
+       struct vscp_vec vv[NR_IOREQS];
+
+       for (i = 0; i < count && left > 0; i++) {
+               vv[i].v_from = src_proc;
+               vv[i].v_to = SELF;
+               vv[i].v_gid = iov[i].iov_grant;
+               vv[i].v_offset = 0;
+               vv[i].v_addr = dst + cur_off;
+               vv[i].v_bytes = iov[i].iov_size;
+
+               /* More data in iov than the buffer can hold, this should be
+                * manageable by the caller.
+                */
+               if (left - vv[i].v_bytes > left) {
+                       printf("sys_easy_vsafecopy_from: buf too small!\n");
+                       return ENOMEM;
+               }
+
+               left -= iov[i].iov_size;
+               cur_off += iov[i].iov_size;
+       }
+
+       /* Now that we prepared the vscp_vec, we can call vsafecopy() */
+       if ((r = sys_vsafecopy(vv, count)) != OK)
+               printf("sys_vsafecopy: failed: (%d)\n", r);
+
+       if (copied)
+               *copied = cur_off;
+
+       return OK;
+}
+
+static int
+virtio_net_cpy_from_user(message *m)
+{
+       /* Put user bytes into a a free packet buffer and
+        * then forward this packet to the TX queue.
+        */
+       int r;
+       iovec_s_t iovec[NR_IOREQS];
+       struct vumap_phys phys[2];
+       struct packet *p;
+       size_t bytes;
+
+       /* This should only be called if free_list has some entries */
+       assert(!STAILQ_EMPTY(&free_list));
+
+       p = STAILQ_FIRST(&free_list);
+       STAILQ_REMOVE_HEAD(&free_list, next);
+
+       virtio_net_fetch_iovec(iovec, m);
+
+       r = sys_easy_vsafecopy_from(m->m_source, iovec, m->DL_COUNT,
+                                   (vir_bytes)p->vdata, MAX_PACK_SIZE,
+                                   &bytes);
+
+       if (r != OK)
+               panic("%s: copy from %d failed", name, m->m_source);
+
+
+       phys[0].vp_addr = p->phdr;
+       assert(!(phys[0].vp_addr & 1));
+       phys[0].vp_size = sizeof(struct virtio_net_hdr);
+       phys[1].vp_addr = p->pdata;
+       assert(!(phys[1].vp_addr & 1));
+       phys[1].vp_size = bytes;
+       virtio_to_queue(net_dev, TX_Q, phys, 2, p);
+       return bytes;
+}
+
+static void
+virtio_net_intr(message *m)
+{
+       /* Check and clear interrupt flag */
+       if (virtio_had_irq(net_dev)) {
+               virtio_net_check_queues();
+       } else {
+               if (!spurious_interrupt)
+                       dput(("Spurious interrupt"));
+
+               spurious_interrupt = 1;
+       }
+
+       virtio_net_check_pending();
+
+       virtio_irq_enable(net_dev);
+}
+
+static void
+virtio_net_write(message *m)
+{
+       int r;
+       message reply;
+
+       reply.m_type = DL_TASK_REPLY;
+       reply.DL_FLAGS = DL_NOFLAGS;
+       reply.DL_COUNT = 0;
+
+
+       if (!STAILQ_EMPTY(&free_list)) {
+               /* free_list contains at least one  packet, use it */
+               reply.DL_COUNT = virtio_net_cpy_from_user(m);
+               reply.DL_FLAGS = DL_PACK_SEND;
+       } else {
+               pending_tx_msg = *m;
+               tx_pending = 1;
+       }
+
+       if ((r = send(m->m_source, &reply)) != OK)
+               panic("%s: send to %d failed (%d)", name, m->m_source, r);
+}
+
+static void
+virtio_net_read(message *m)
+{
+       int r;
+       message reply;
+
+       reply.m_type = DL_TASK_REPLY;
+       reply.DL_FLAGS = DL_NOFLAGS;
+       reply.DL_COUNT = 0;
+
+       if (!STAILQ_EMPTY(&recv_list)) {
+               /* recv_list contains at least one  packet, copy it */
+               reply.DL_COUNT = virtio_net_cpy_to_user(m);
+               reply.DL_FLAGS = DL_PACK_RECV;
+       } else {
+               rx_pending = 1;
+               pending_rx_msg = *m;
+       }
+
+       if ((r = send(m->m_source, &reply)) != OK)
+               panic("%s: send to %d failed (%d)", name, m->m_source, r);
+}
+
+static void
+virtio_net_conf(message *m)
+{
+       /* TODO: Add the multicast, broadcast filtering etc. */
+       int i, r;
+
+       message reply;
+
+       /* If this is the first CONF message we see, fully initialize
+        * the device now.
+        */
+       if (!started) {
+               started = 1;
+               virtio_device_ready(net_dev);
+               virtio_irq_enable(net_dev);
+       }
+
+       /* Prepare reply */
+       for (i = 0; i < sizeof(virtio_net_mac); i++)
+               ((u8_t*)reply.DL_HWADDR)[i] = virtio_net_mac[i];
+
+       reply.m_type = DL_CONF_REPLY;
+       reply.DL_STAT = OK;
+       reply.DL_COUNT = 0;
+
+       if ((r = send(m->m_source, &reply)) != OK)
+               panic("%s: send to %d failed (%d)", name, m->m_source, r);
+}
+
+static void
+virtio_net_getstat(message *m)
+{
+       int r;
+       message reply;
+
+       reply.m_type = DL_STAT_REPLY;
+       reply.DL_STAT = OK;
+       reply.DL_COUNT = 0;
+
+
+       r = sys_safecopyto(m->m_source, m->DL_GRANT, 0,
+                          (vir_bytes)&virtio_net_stats,
+                          sizeof(virtio_net_stats));
+
+       if (r != OK)
+               panic("%s: copy to %d failed (%d)", name, m->m_source, r);
+
+       if ((r = send(m->m_source, &reply)) != OK)
+               panic("%s: send to %d failed (%d)", name, m->m_source, r);
+}
+
+static void
+virtio_net_notify(message *m)
+{
+       if (_ENDPOINT_P(m->m_source) == HARDWARE)
+               virtio_net_intr(m);
+}
+
+static void
+virtio_net_msg(message *m)
+{
+       switch (m->m_type) {
+       case DL_WRITEV_S:
+               virtio_net_write(m);
+               break;
+       case DL_READV_S:
+               virtio_net_read(m);
+               break;
+       case DL_CONF:
+               virtio_net_conf(m);
+               break;
+       case DL_GETSTAT_S:
+               virtio_net_getstat(m);
+               break;
+       default:
+               panic("%s: illegal message: %d", name, m->m_type);
+       }
+}
+
+static void
+virtio_net_main_loop(void)
+{
+       message m;
+       int ipc_status;
+       int r;
+
+       while (TRUE) {
+
+               virtio_net_refill_rx_queue();
+
+               if ((r = netdriver_receive(ANY, &m, &ipc_status)) != OK)
+                       panic("%s: netdriver_receive failed: %d", name, r);
+
+               if (is_ipc_notify(ipc_status))
+                       virtio_net_notify(&m);
+               else
+                       virtio_net_msg(&m);
+       }
+}
+
+int
+main(int argc, char *argv[])
+{
+       env_setargs(argc, argv);
+       sef_local_startup();
+
+       virtio_net_main_loop();
+}
+
+static void
+sef_local_startup()
+{
+       sef_setcb_init_fresh(sef_cb_init_fresh);
+       sef_setcb_init_lu(sef_cb_init_fresh);
+       sef_setcb_init_restart(sef_cb_init_fresh);
+
+       sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
+       sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree);
+
+       sef_setcb_signal_handler(sef_cb_signal_handler);
+
+       sef_startup();
+}
+
+static int
+sef_cb_init_fresh(int type, sef_init_info_t *info)
+{
+       long instance = 0;
+       env_parse("instance", "d", 0, &instance, 0, 255);
+
+       if (virtio_net_probe((int)instance) != OK)
+               panic("%s: No device found", name);
+
+       if (virtio_net_config() != OK)
+               panic("%s: No device found", name);
+
+       if (virtio_net_alloc_bufs() != OK)
+               panic("%s: Buffer allocation failed", name);
+
+       virtio_net_init_queues();
+
+       netdriver_announce();
+
+       return(OK);
+}
+
+static void
+sef_cb_signal_handler(int signo)
+{
+       if (signo != SIGTERM)
+               return;
+
+       dput(("Terminating"));
+
+       free_contig(data_vir, PACKET_BUF_SZ);
+       free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
+       free(packets);
+
+       virtio_reset_device(net_dev);
+       virtio_free_queues(net_dev);
+       virtio_free_device(net_dev);
+       net_dev = NULL;
+
+       exit(1);
+}
diff --git a/drivers/virtio_net/virtio_net.conf b/drivers/virtio_net/virtio_net.conf
new file mode 100644 (file)
index 0000000..46daa1e
--- /dev/null
@@ -0,0 +1,13 @@
+service virtio_net
+{
+       type    net;
+       descr   "Virtio network device";
+       system
+               UMAP
+               VUMAP
+               IRQCTL
+               DEVIO
+       ;
+
+       pci device      1af4/1000;
+}
diff --git a/drivers/virtio_net/virtio_net.h b/drivers/virtio_net/virtio_net.h
new file mode 100644 (file)
index 0000000..5edcf1c
--- /dev/null
@@ -0,0 +1,172 @@
+#ifndef _LINUX_VIRTIO_NET_H
+#define _LINUX_VIRTIO_NET_H
+/* This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+#if 0
+#include <linux/types.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/if_ether.h>
+#endif
+
+/* The feature bitmap for virtio net */
+#define VIRTIO_NET_F_CSUM      0       /* Host handles pkts w/ partial csum */
+#define VIRTIO_NET_F_GUEST_CSUM        1       /* Guest handles pkts w/ partial csum */
+#define VIRTIO_NET_F_MAC       5       /* Host has given MAC address. */
+#define VIRTIO_NET_F_GSO       6       /* Host handles pkts w/ any GSO type */
+#define VIRTIO_NET_F_GUEST_TSO4        7       /* Guest can handle TSOv4 in. */
+#define VIRTIO_NET_F_GUEST_TSO6        8       /* Guest can handle TSOv6 in. */
+#define VIRTIO_NET_F_GUEST_ECN 9       /* Guest can handle TSO[6] w/ ECN in. */
+#define VIRTIO_NET_F_GUEST_UFO 10      /* Guest can handle UFO in. */
+#define VIRTIO_NET_F_HOST_TSO4 11      /* Host can handle TSOv4 in. */
+#define VIRTIO_NET_F_HOST_TSO6 12      /* Host can handle TSOv6 in. */
+#define VIRTIO_NET_F_HOST_ECN  13      /* Host can handle TSO[6] w/ ECN in. */
+#define VIRTIO_NET_F_HOST_UFO  14      /* Host can handle UFO in. */
+#define VIRTIO_NET_F_MRG_RXBUF 15      /* Host can merge receive buffers. */
+#define VIRTIO_NET_F_STATUS    16      /* virtio_net_config.status available */
+#define VIRTIO_NET_F_CTRL_VQ   17      /* Control channel available */
+#define VIRTIO_NET_F_CTRL_RX   18      /* Control channel RX mode support */
+#define VIRTIO_NET_F_CTRL_VLAN 19      /* Control channel VLAN filtering */
+#define VIRTIO_NET_F_CTRL_RX_EXTRA 20  /* Extra RX mode control support */
+#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the
+                                        * network */
+
+#define VIRTIO_NET_S_LINK_UP   1       /* Link is up */
+#define VIRTIO_NET_S_ANNOUNCE  2       /* Announcement is needed */
+
+struct virtio_net_config {
+       /* The config defining mac address (if VIRTIO_NET_F_MAC) */
+       u8_t mac[6];
+       /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */
+       u16_t status;
+} __attribute__((packed));
+
+/* This is the first element of the scatter-gather list.  If you don't
+ * specify GSO or CSUM features, you can simply ignore the header. */
+struct virtio_net_hdr {
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM    1       // Use csum_start, csum_offset
+#define VIRTIO_NET_HDR_F_DATA_VALID    2       // Csum is valid
+       u8_t flags;
+#define VIRTIO_NET_HDR_GSO_NONE                0       // Not a GSO frame
+#define VIRTIO_NET_HDR_GSO_TCPV4       1       // GSO frame, IPv4 TCP (TSO)
+#define VIRTIO_NET_HDR_GSO_UDP         3       // GSO frame, IPv4 UDP (UFO)
+#define VIRTIO_NET_HDR_GSO_TCPV6       4       // GSO frame, IPv6 TCP
+#define VIRTIO_NET_HDR_GSO_ECN         0x80    // TCP has ECN set
+       u8_t gso_type;
+       u16_t hdr_len;          /* Ethernet + IP + tcp/udp hdrs */
+       u16_t gso_size;         /* Bytes to append to hdr_len per frame */
+       u16_t csum_start;       /* Position to start checksumming from */
+       u16_t csum_offset;      /* Offset after that to place checksum */
+};
+
+/* This is the version of the header to use when the MRG_RXBUF
+ * feature has been negotiated. */
+struct virtio_net_hdr_mrg_rxbuf {
+       struct virtio_net_hdr hdr;
+       u16_t num_buffers;      /* Number of merged rx buffers */
+};
+
+/*
+ * Control virtqueue data structures
+ *
+ * The control virtqueue expects a header in the first sg entry
+ * and an ack/status response in the last entry.  Data for the
+ * command goes in between.
+ */
+struct virtio_net_ctrl_hdr {
+       u8_t class;
+       u8_t cmd;
+} __attribute__((packed));
+
+typedef u8_t virtio_net_ctrl_ack;
+
+#define VIRTIO_NET_OK     0
+#define VIRTIO_NET_ERR    1
+
+/*
+ * Control the RX mode, ie. promisucous, allmulti, etc...
+ * All commands require an "out" sg entry containing a 1 byte
+ * state value, zero = disable, non-zero = enable.  Commands
+ * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA.
+ */
+#define VIRTIO_NET_CTRL_RX    0
+ #define VIRTIO_NET_CTRL_RX_PROMISC      0
+ #define VIRTIO_NET_CTRL_RX_ALLMULTI     1
+ #define VIRTIO_NET_CTRL_RX_ALLUNI       2
+ #define VIRTIO_NET_CTRL_RX_NOMULTI      3
+ #define VIRTIO_NET_CTRL_RX_NOUNI        4
+ #define VIRTIO_NET_CTRL_RX_NOBCAST      5
+
+/*
+ * Control the MAC filter table.
+ *
+ * The MAC filter table is managed by the hypervisor, the guest should
+ * assume the size is infinite.  Filtering should be considered
+ * non-perfect, ie. based on hypervisor resources, the guest may
+ * received packets from sources not specified in the filter list.
+ *
+ * In addition to the class/cmd header, the TABLE_SET command requires
+ * two out scatterlists.  Each contains a 4 byte count of entries followed
+ * by a concatenated byte stream of the ETH_ALEN MAC addresses.  The
+ * first sg list contains unicast addresses, the second is for multicast.
+ * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
+ * is available.
+ */
+struct virtio_net_ctrl_mac {
+       u32_t entries;
+#define ETH_ALEN 6
+       u8_t macs[][ETH_ALEN];
+} __attribute__((packed));
+
+#define VIRTIO_NET_CTRL_MAC    1
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET        0
+
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface.
+ * VLAN IDs not added may be filterd by the hypervisor.  Del is the
+ * opposite of add.  Both commands expect an out entry containing a 2
+ * byte VLAN ID.  VLAN filterting is available with the
+ * VIRTIO_NET_F_CTRL_VLAN feature bit.
+ */
+#define VIRTIO_NET_CTRL_VLAN       2
+ #define VIRTIO_NET_CTRL_VLAN_ADD             0
+ #define VIRTIO_NET_CTRL_VLAN_DEL             1
+
+/*
+ * Control link announce acknowledgement
+ *
+ * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that
+ * driver has recevied the notification; device would clear the
+ * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives
+ * this command.
+ */
+#define VIRTIO_NET_CTRL_ANNOUNCE       3
+ #define VIRTIO_NET_CTRL_ANNOUNCE_ACK         0
+
+#endif /* _LINUX_VIRTIO_NET_H */