xref: /minix3/minix/drivers/net/virtio_net/virtio_net.c (revision f7df02e7476731c31f12548e38bcadbaf0233f6a)
1433d6423SLionel Sambuc /* virtio net driver for MINIX 3
2433d6423SLionel Sambuc  *
3433d6423SLionel Sambuc  * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
4433d6423SLionel Sambuc  *
5433d6423SLionel Sambuc  * This software is released under the BSD license. See the LICENSE file
6433d6423SLionel Sambuc  * included in the main directory of this source distribution for the
7433d6423SLionel Sambuc  * license terms and conditions.
8433d6423SLionel Sambuc  */
9433d6423SLionel Sambuc 
10433d6423SLionel Sambuc #include <assert.h>
11433d6423SLionel Sambuc #include <sys/types.h>
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc #include <minix/drivers.h>
14433d6423SLionel Sambuc #include <minix/netdriver.h>
15433d6423SLionel Sambuc #include <minix/virtio.h>
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc #include <sys/queue.h>
18433d6423SLionel Sambuc 
19433d6423SLionel Sambuc #include "virtio_net.h"
20433d6423SLionel Sambuc 
215260f07cSDavid van Moolenbroek #define VERBOSE 0
225260f07cSDavid van Moolenbroek 
235260f07cSDavid van Moolenbroek #if VERBOSE
24433d6423SLionel Sambuc #define dput(s)		do { dprintf(s); printf("\n"); } while (0)
25433d6423SLionel Sambuc #define dprintf(s) do {						\
26*f7df02e7SDavid van Moolenbroek 	printf("%s: ", netdriver_name());			\
27433d6423SLionel Sambuc 	printf s;						\
28433d6423SLionel Sambuc } while (0)
295260f07cSDavid van Moolenbroek #else
305260f07cSDavid van Moolenbroek #define dput(s)
315260f07cSDavid van Moolenbroek #define dprintf(s)
325260f07cSDavid van Moolenbroek #endif
33433d6423SLionel Sambuc 
34433d6423SLionel Sambuc static struct virtio_device *net_dev;
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc enum queue {RX_Q, TX_Q, CTRL_Q};
37433d6423SLionel Sambuc 
38433d6423SLionel Sambuc /* Number of packets to work with */
39433d6423SLionel Sambuc /* TODO: This should be an argument to the driver and possibly also
40433d6423SLionel Sambuc  *       depend on the queue sizes offered by this device.
41433d6423SLionel Sambuc  */
42433d6423SLionel Sambuc #define BUF_PACKETS		64
43433d6423SLionel Sambuc /* Maximum size of a packet */
44*f7df02e7SDavid van Moolenbroek #define MAX_PACK_SIZE		NDEV_ETH_PACKET_MAX
45433d6423SLionel Sambuc /* Buffer size needed for the payload of BUF_PACKETS */
46433d6423SLionel Sambuc #define PACKET_BUF_SZ		(BUF_PACKETS * MAX_PACK_SIZE)
47433d6423SLionel Sambuc 
48433d6423SLionel Sambuc struct packet {
49433d6423SLionel Sambuc 	int idx;
50433d6423SLionel Sambuc 	struct virtio_net_hdr *vhdr;
51433d6423SLionel Sambuc 	phys_bytes phdr;
52433d6423SLionel Sambuc 	char *vdata;
53433d6423SLionel Sambuc 	phys_bytes pdata;
545260f07cSDavid van Moolenbroek 	size_t len;
55433d6423SLionel Sambuc 	STAILQ_ENTRY(packet) next;
56433d6423SLionel Sambuc };
57433d6423SLionel Sambuc 
58433d6423SLionel Sambuc /* Allocated data chunks */
59433d6423SLionel Sambuc static char *data_vir;
60433d6423SLionel Sambuc static phys_bytes data_phys;
61433d6423SLionel Sambuc static struct virtio_net_hdr *hdrs_vir;
62433d6423SLionel Sambuc static phys_bytes hdrs_phys;
63433d6423SLionel Sambuc static struct packet *packets;
64433d6423SLionel Sambuc static int in_rx;
65433d6423SLionel Sambuc 
66433d6423SLionel Sambuc /* Packets on this list can be given to the host */
67433d6423SLionel Sambuc static STAILQ_HEAD(free_list, packet) free_list;
68433d6423SLionel Sambuc 
69433d6423SLionel Sambuc /* Packets on this list are to be given to inet */
70433d6423SLionel Sambuc static STAILQ_HEAD(recv_list, packet) recv_list;
71433d6423SLionel Sambuc 
72433d6423SLionel Sambuc /* Various state data */
73433d6423SLionel Sambuc static int spurious_interrupt;
74433d6423SLionel Sambuc 
75433d6423SLionel Sambuc /* Prototypes */
765260f07cSDavid van Moolenbroek static int virtio_net_probe(unsigned int skip);
77*f7df02e7SDavid van Moolenbroek static void virtio_net_config(netdriver_addr_t *addr);
78433d6423SLionel Sambuc static int virtio_net_alloc_bufs(void);
79433d6423SLionel Sambuc static void virtio_net_init_queues(void);
80433d6423SLionel Sambuc 
81433d6423SLionel Sambuc static void virtio_net_refill_rx_queue(void);
82433d6423SLionel Sambuc static void virtio_net_check_queues(void);
83433d6423SLionel Sambuc static void virtio_net_check_pending(void);
84433d6423SLionel Sambuc 
85*f7df02e7SDavid van Moolenbroek static int virtio_net_init(unsigned int instance, netdriver_addr_t * addr,
86*f7df02e7SDavid van Moolenbroek 	uint32_t * caps, unsigned int * ticks);
875260f07cSDavid van Moolenbroek static void virtio_net_stop(void);
885260f07cSDavid van Moolenbroek static int virtio_net_send(struct netdriver_data *data, size_t len);
895260f07cSDavid van Moolenbroek static ssize_t virtio_net_recv(struct netdriver_data *data, size_t max);
905260f07cSDavid van Moolenbroek static void virtio_net_intr(unsigned int mask);
91433d6423SLionel Sambuc 
925260f07cSDavid van Moolenbroek static const struct netdriver virtio_net_table = {
93*f7df02e7SDavid van Moolenbroek 	.ndr_name	= "vio",
945260f07cSDavid van Moolenbroek 	.ndr_init	= virtio_net_init,
955260f07cSDavid van Moolenbroek 	.ndr_stop	= virtio_net_stop,
965260f07cSDavid van Moolenbroek 	.ndr_recv	= virtio_net_recv,
975260f07cSDavid van Moolenbroek 	.ndr_send	= virtio_net_send,
985260f07cSDavid van Moolenbroek 	.ndr_intr	= virtio_net_intr,
995260f07cSDavid van Moolenbroek };
100433d6423SLionel Sambuc 
101433d6423SLionel Sambuc /* TODO: Features are pretty much ignored */
1025260f07cSDavid van Moolenbroek static struct virtio_feature netf[] = {
103433d6423SLionel Sambuc 	{ "partial csum",	VIRTIO_NET_F_CSUM,	0,	0	},
104433d6423SLionel Sambuc 	{ "given mac",		VIRTIO_NET_F_MAC,	0,	1	},
105433d6423SLionel Sambuc 	{ "status ",		VIRTIO_NET_F_STATUS,	0,	0	},
106433d6423SLionel Sambuc 	{ "control channel",	VIRTIO_NET_F_CTRL_VQ,	0,	1	},
107433d6423SLionel Sambuc 	{ "control channel rx",	VIRTIO_NET_F_CTRL_RX,	0,	0	}
108433d6423SLionel Sambuc };
109433d6423SLionel Sambuc 
110433d6423SLionel Sambuc static int
virtio_net_probe(unsigned int skip)1115260f07cSDavid van Moolenbroek virtio_net_probe(unsigned int skip)
112433d6423SLionel Sambuc {
113433d6423SLionel Sambuc 	/* virtio-net has at least 2 queues */
114433d6423SLionel Sambuc 	int queues = 2;
115*f7df02e7SDavid van Moolenbroek 	net_dev= virtio_setup_device(0x00001, netdriver_name(), netf,
116433d6423SLionel Sambuc 				     sizeof(netf) / sizeof(netf[0]),
117433d6423SLionel Sambuc 				     1 /* threads */, skip);
118433d6423SLionel Sambuc 	if (net_dev == NULL)
119433d6423SLionel Sambuc 		return ENXIO;
120433d6423SLionel Sambuc 
121433d6423SLionel Sambuc 	/* If the host supports the control queue, allocate it as well */
122433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
123433d6423SLionel Sambuc 		queues += 1;
124433d6423SLionel Sambuc 
125433d6423SLionel Sambuc 	if (virtio_alloc_queues(net_dev, queues) != OK) {
126433d6423SLionel Sambuc 		virtio_free_device(net_dev);
127433d6423SLionel Sambuc 		return ENOMEM;
128433d6423SLionel Sambuc 	}
129433d6423SLionel Sambuc 
130433d6423SLionel Sambuc 	return OK;
131433d6423SLionel Sambuc }
132433d6423SLionel Sambuc 
1335260f07cSDavid van Moolenbroek static void
virtio_net_config(netdriver_addr_t * addr)134*f7df02e7SDavid van Moolenbroek virtio_net_config(netdriver_addr_t * addr)
135433d6423SLionel Sambuc {
136433d6423SLionel Sambuc 	u32_t mac14;
137433d6423SLionel Sambuc 	u32_t mac56;
138433d6423SLionel Sambuc 	int i;
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) {
141433d6423SLionel Sambuc 		dprintf(("Mac set by host: "));
142433d6423SLionel Sambuc 		mac14 = virtio_sread32(net_dev, 0);
143433d6423SLionel Sambuc 		mac56 = virtio_sread32(net_dev, 4);
144*f7df02e7SDavid van Moolenbroek 		memcpy(&addr->na_addr[0], &mac14, 4);
145*f7df02e7SDavid van Moolenbroek 		memcpy(&addr->na_addr[4], &mac56, 2);
146433d6423SLionel Sambuc 
147433d6423SLionel Sambuc 		for (i = 0; i < 6; i++)
148*f7df02e7SDavid van Moolenbroek 			dprintf(("%02x%s", addr->na_addr[i],
1495260f07cSDavid van Moolenbroek 					 i == 5 ? "\n" : ":"));
150433d6423SLionel Sambuc 	} else {
151433d6423SLionel Sambuc 		dput(("No mac"));
152433d6423SLionel Sambuc 	}
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) {
155433d6423SLionel Sambuc 		dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6)));
156433d6423SLionel Sambuc 	} else {
157433d6423SLionel Sambuc 		dput(("No status"));
158433d6423SLionel Sambuc 	}
159433d6423SLionel Sambuc 
160433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
161433d6423SLionel Sambuc 		dput(("Host supports control channel"));
162433d6423SLionel Sambuc 
163433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX))
164433d6423SLionel Sambuc 		dput(("Host supports control channel for RX"));
165433d6423SLionel Sambuc }
166433d6423SLionel Sambuc 
167433d6423SLionel Sambuc static int
virtio_net_alloc_bufs(void)168433d6423SLionel Sambuc virtio_net_alloc_bufs(void)
169433d6423SLionel Sambuc {
170433d6423SLionel Sambuc 	data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys);
171433d6423SLionel Sambuc 
172433d6423SLionel Sambuc 	if (!data_vir)
173433d6423SLionel Sambuc 		return ENOMEM;
174433d6423SLionel Sambuc 
175433d6423SLionel Sambuc 	hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]),
176433d6423SLionel Sambuc 				 0, &hdrs_phys);
177433d6423SLionel Sambuc 
178433d6423SLionel Sambuc 	if (!hdrs_vir) {
179433d6423SLionel Sambuc 		free_contig(data_vir, PACKET_BUF_SZ);
180433d6423SLionel Sambuc 		return ENOMEM;
181433d6423SLionel Sambuc 	}
182433d6423SLionel Sambuc 
183433d6423SLionel Sambuc 	packets = malloc(BUF_PACKETS * sizeof(packets[0]));
184433d6423SLionel Sambuc 
185433d6423SLionel Sambuc 	if (!packets) {
186433d6423SLionel Sambuc 		free_contig(data_vir, PACKET_BUF_SZ);
187433d6423SLionel Sambuc 		free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
188433d6423SLionel Sambuc 		return ENOMEM;
189433d6423SLionel Sambuc 	}
190433d6423SLionel Sambuc 
191433d6423SLionel Sambuc 	memset(data_vir, 0, PACKET_BUF_SZ);
192433d6423SLionel Sambuc 	memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0]));
193433d6423SLionel Sambuc 	memset(packets, 0, BUF_PACKETS * sizeof(packets[0]));
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc 	return OK;
196433d6423SLionel Sambuc }
197433d6423SLionel Sambuc 
198433d6423SLionel Sambuc static void
virtio_net_init_queues(void)199433d6423SLionel Sambuc virtio_net_init_queues(void)
200433d6423SLionel Sambuc {
201433d6423SLionel Sambuc 	int i;
202433d6423SLionel Sambuc 	STAILQ_INIT(&free_list);
203433d6423SLionel Sambuc 	STAILQ_INIT(&recv_list);
204433d6423SLionel Sambuc 
205433d6423SLionel Sambuc 	for (i = 0; i < BUF_PACKETS; i++) {
206433d6423SLionel Sambuc 		packets[i].idx = i;
207433d6423SLionel Sambuc 		packets[i].vhdr = &hdrs_vir[i];
208433d6423SLionel Sambuc 		packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]);
209433d6423SLionel Sambuc 		packets[i].vdata = data_vir + i * MAX_PACK_SIZE;
210433d6423SLionel Sambuc 		packets[i].pdata = data_phys + i * MAX_PACK_SIZE;
211433d6423SLionel Sambuc 		STAILQ_INSERT_HEAD(&free_list, &packets[i], next);
212433d6423SLionel Sambuc 	}
213433d6423SLionel Sambuc }
214433d6423SLionel Sambuc 
215433d6423SLionel Sambuc static void
virtio_net_refill_rx_queue(void)216433d6423SLionel Sambuc virtio_net_refill_rx_queue(void)
217433d6423SLionel Sambuc {
218433d6423SLionel Sambuc 	struct vumap_phys phys[2];
219433d6423SLionel Sambuc 	struct packet *p;
220433d6423SLionel Sambuc 
221433d6423SLionel Sambuc 	while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) {
222433d6423SLionel Sambuc 		/* peek */
223433d6423SLionel Sambuc 		p = STAILQ_FIRST(&free_list);
224433d6423SLionel Sambuc 		/* remove */
225433d6423SLionel Sambuc 		STAILQ_REMOVE_HEAD(&free_list, next);
226433d6423SLionel Sambuc 
227433d6423SLionel Sambuc 		phys[0].vp_addr = p->phdr;
228433d6423SLionel Sambuc 		assert(!(phys[0].vp_addr & 1));
229433d6423SLionel Sambuc 		phys[0].vp_size = sizeof(struct virtio_net_hdr);
230433d6423SLionel Sambuc 
231433d6423SLionel Sambuc 		phys[1].vp_addr = p->pdata;
232433d6423SLionel Sambuc 		assert(!(phys[1].vp_addr & 1));
233433d6423SLionel Sambuc 		phys[1].vp_size = MAX_PACK_SIZE;
234433d6423SLionel Sambuc 
235433d6423SLionel Sambuc 		/* RX queue needs write */
236433d6423SLionel Sambuc 		phys[0].vp_addr |= 1;
237433d6423SLionel Sambuc 		phys[1].vp_addr |= 1;
238433d6423SLionel Sambuc 
239433d6423SLionel Sambuc 		virtio_to_queue(net_dev, RX_Q, phys, 2, p);
240433d6423SLionel Sambuc 		in_rx++;
241433d6423SLionel Sambuc 	}
242433d6423SLionel Sambuc 
243*f7df02e7SDavid van Moolenbroek 	if (in_rx == 0 && STAILQ_EMPTY(&free_list))
244433d6423SLionel Sambuc 		dput(("warning: rx queue underflow!"));
245433d6423SLionel Sambuc }
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc static void
virtio_net_check_queues(void)248433d6423SLionel Sambuc virtio_net_check_queues(void)
249433d6423SLionel Sambuc {
250433d6423SLionel Sambuc 	struct packet *p;
2515260f07cSDavid van Moolenbroek 	size_t len;
252433d6423SLionel Sambuc 
253433d6423SLionel Sambuc 	/* Put the received packets into the recv list */
2545260f07cSDavid van Moolenbroek 	while (virtio_from_queue(net_dev, RX_Q, (void **)&p, &len) == 0) {
2555260f07cSDavid van Moolenbroek 		p->len = len;
256433d6423SLionel Sambuc 		STAILQ_INSERT_TAIL(&recv_list, p, next);
257433d6423SLionel Sambuc 		in_rx--;
258433d6423SLionel Sambuc 	}
259433d6423SLionel Sambuc 
2605260f07cSDavid van Moolenbroek 	/*
2615260f07cSDavid van Moolenbroek 	 * Packets from the TX queue just indicated they are free to
262433d6423SLionel Sambuc 	 * be reused now. inet already knows about them as being sent.
263433d6423SLionel Sambuc 	 */
264d1db724fSDavid van Moolenbroek 	while (virtio_from_queue(net_dev, TX_Q, (void **)&p, NULL) == 0) {
265433d6423SLionel Sambuc 		memset(p->vhdr, 0, sizeof(*p->vhdr));
266433d6423SLionel Sambuc 		memset(p->vdata, 0, MAX_PACK_SIZE);
267433d6423SLionel Sambuc 		STAILQ_INSERT_HEAD(&free_list, p, next);
268433d6423SLionel Sambuc 	}
269433d6423SLionel Sambuc }
270433d6423SLionel Sambuc 
271433d6423SLionel Sambuc static void
virtio_net_check_pending(void)272433d6423SLionel Sambuc virtio_net_check_pending(void)
273433d6423SLionel Sambuc {
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc 	/* Pending read and something in recv_list? */
2765260f07cSDavid van Moolenbroek 	if (!STAILQ_EMPTY(&recv_list))
2775260f07cSDavid van Moolenbroek 		netdriver_recv();
278433d6423SLionel Sambuc 
2795260f07cSDavid van Moolenbroek 	if (!STAILQ_EMPTY(&free_list))
2805260f07cSDavid van Moolenbroek 		netdriver_send();
281433d6423SLionel Sambuc }
282433d6423SLionel Sambuc 
283433d6423SLionel Sambuc static void
virtio_net_intr(unsigned int __unused mask)2845260f07cSDavid van Moolenbroek virtio_net_intr(unsigned int __unused mask)
285433d6423SLionel Sambuc {
286433d6423SLionel Sambuc 
287433d6423SLionel Sambuc 	/* Check and clear interrupt flag */
288433d6423SLionel Sambuc 	if (virtio_had_irq(net_dev)) {
289433d6423SLionel Sambuc 		virtio_net_check_queues();
290433d6423SLionel Sambuc 	} else {
291433d6423SLionel Sambuc 		if (!spurious_interrupt)
292433d6423SLionel Sambuc 			dput(("Spurious interrupt"));
293433d6423SLionel Sambuc 
294433d6423SLionel Sambuc 		spurious_interrupt = 1;
295433d6423SLionel Sambuc 	}
296433d6423SLionel Sambuc 
297433d6423SLionel Sambuc 	virtio_net_check_pending();
298433d6423SLionel Sambuc 
299433d6423SLionel Sambuc 	virtio_irq_enable(net_dev);
3005260f07cSDavid van Moolenbroek 
3015260f07cSDavid van Moolenbroek 	/* Readd packets to the receive queue as necessary. */
3025260f07cSDavid van Moolenbroek 	virtio_net_refill_rx_queue();
303433d6423SLionel Sambuc }
304433d6423SLionel Sambuc 
3055260f07cSDavid van Moolenbroek /*
3065260f07cSDavid van Moolenbroek  * Put user bytes into a free packet buffer, forward this packet to the TX
3075260f07cSDavid van Moolenbroek  * queue, and return OK.  If there are no free packet buffers, return SUSPEND.
308433d6423SLionel Sambuc  */
3095260f07cSDavid van Moolenbroek static int
virtio_net_send(struct netdriver_data * data,size_t len)3105260f07cSDavid van Moolenbroek virtio_net_send(struct netdriver_data * data, size_t len)
311433d6423SLionel Sambuc {
3125260f07cSDavid van Moolenbroek 	struct vumap_phys phys[2];
3135260f07cSDavid van Moolenbroek 	struct packet *p;
314433d6423SLionel Sambuc 
3155260f07cSDavid van Moolenbroek 	if (STAILQ_EMPTY(&free_list))
3165260f07cSDavid van Moolenbroek 		return SUSPEND;
317433d6423SLionel Sambuc 
3185260f07cSDavid van Moolenbroek 	p = STAILQ_FIRST(&free_list);
3195260f07cSDavid van Moolenbroek 	STAILQ_REMOVE_HEAD(&free_list, next);
320433d6423SLionel Sambuc 
3215260f07cSDavid van Moolenbroek 	if (len > MAX_PACK_SIZE)
322*f7df02e7SDavid van Moolenbroek 		panic("%s: packet too large to send: %zu",
323*f7df02e7SDavid van Moolenbroek 		    netdriver_name(), len);
324433d6423SLionel Sambuc 
3255260f07cSDavid van Moolenbroek 	netdriver_copyin(data, 0, p->vdata, len);
3265260f07cSDavid van Moolenbroek 
3275260f07cSDavid van Moolenbroek 	phys[0].vp_addr = p->phdr;
3285260f07cSDavid van Moolenbroek 	assert(!(phys[0].vp_addr & 1));
3295260f07cSDavid van Moolenbroek 	phys[0].vp_size = sizeof(struct virtio_net_hdr);
3305260f07cSDavid van Moolenbroek 	phys[1].vp_addr = p->pdata;
3315260f07cSDavid van Moolenbroek 	assert(!(phys[1].vp_addr & 1));
3325260f07cSDavid van Moolenbroek 	phys[1].vp_size = len;
3335260f07cSDavid van Moolenbroek 	virtio_to_queue(net_dev, TX_Q, phys, 2, p);
3345260f07cSDavid van Moolenbroek 
3355260f07cSDavid van Moolenbroek 	return OK;
336433d6423SLionel Sambuc }
337433d6423SLionel Sambuc 
3385260f07cSDavid van Moolenbroek /*
3395260f07cSDavid van Moolenbroek  * Put a packet receive from the RX queue into a user buffer, and return the
3405260f07cSDavid van Moolenbroek  * packet length.  If there are no received packets, return SUSPEND.
3415260f07cSDavid van Moolenbroek  */
3425260f07cSDavid van Moolenbroek static ssize_t
virtio_net_recv(struct netdriver_data * data,size_t max)3435260f07cSDavid van Moolenbroek virtio_net_recv(struct netdriver_data * data, size_t max)
344433d6423SLionel Sambuc {
3455260f07cSDavid van Moolenbroek 	struct packet *p;
3465260f07cSDavid van Moolenbroek 	ssize_t len;
347433d6423SLionel Sambuc 
3485260f07cSDavid van Moolenbroek 	/* Get the first received packet, if any. */
3495260f07cSDavid van Moolenbroek 	if (STAILQ_EMPTY(&recv_list))
3505260f07cSDavid van Moolenbroek 		return SUSPEND;
351433d6423SLionel Sambuc 
3525260f07cSDavid van Moolenbroek 	p = STAILQ_FIRST(&recv_list);
3535260f07cSDavid van Moolenbroek 	STAILQ_REMOVE_HEAD(&recv_list, next);
354433d6423SLionel Sambuc 
3555260f07cSDavid van Moolenbroek 	/* Copy out the packet contents. */
356*f7df02e7SDavid van Moolenbroek 	if (p->len < sizeof(struct virtio_net_hdr))
357*f7df02e7SDavid van Moolenbroek 		panic("received packet does not have virtio header");
3585260f07cSDavid van Moolenbroek 	len = p->len - sizeof(struct virtio_net_hdr);
359*f7df02e7SDavid van Moolenbroek 	if ((size_t)len > max)
360*f7df02e7SDavid van Moolenbroek 		len = (ssize_t)max;
361433d6423SLionel Sambuc 
3625260f07cSDavid van Moolenbroek 	/*
3635260f07cSDavid van Moolenbroek 	 * HACK: due to lack of padding, received packets may in fact be
364*f7df02e7SDavid van Moolenbroek 	 * smaller than the minimum ethernet packet size.  The TCP/IP service
365*f7df02e7SDavid van Moolenbroek 	 * will accept the packets just fine if we increase the length to its
366*f7df02e7SDavid van Moolenbroek 	 * minimum.  We already zeroed out the rest of the packet data, so this
367*f7df02e7SDavid van Moolenbroek 	 * is safe.
3685260f07cSDavid van Moolenbroek 	 */
369*f7df02e7SDavid van Moolenbroek 	if (len < NDEV_ETH_PACKET_MIN)
370*f7df02e7SDavid van Moolenbroek 		len = NDEV_ETH_PACKET_MIN;
3715260f07cSDavid van Moolenbroek 
3725260f07cSDavid van Moolenbroek 	netdriver_copyout(data, 0, p->vdata, len);
3735260f07cSDavid van Moolenbroek 
3745260f07cSDavid van Moolenbroek 	/* Clean the packet. */
3755260f07cSDavid van Moolenbroek 	memset(p->vhdr, 0, sizeof(*p->vhdr));
3765260f07cSDavid van Moolenbroek 	memset(p->vdata, 0, MAX_PACK_SIZE);
3775260f07cSDavid van Moolenbroek 	STAILQ_INSERT_HEAD(&free_list, p, next);
3785260f07cSDavid van Moolenbroek 
3795260f07cSDavid van Moolenbroek 	/* Readd packets to the receive queue as necessary. */
380433d6423SLionel Sambuc 	virtio_net_refill_rx_queue();
381433d6423SLionel Sambuc 
3825260f07cSDavid van Moolenbroek 	return len;
383433d6423SLionel Sambuc }
384433d6423SLionel Sambuc 
3855260f07cSDavid van Moolenbroek /*
3865260f07cSDavid van Moolenbroek  * Initialize the driver and the virtual hardware.
3875260f07cSDavid van Moolenbroek  */
388433d6423SLionel Sambuc static int
virtio_net_init(unsigned int instance,netdriver_addr_t * addr,uint32_t * caps,unsigned int * ticks __unused)389*f7df02e7SDavid van Moolenbroek virtio_net_init(unsigned int instance, netdriver_addr_t * addr,
390*f7df02e7SDavid van Moolenbroek 	uint32_t * caps, unsigned int * ticks __unused)
391433d6423SLionel Sambuc {
3925260f07cSDavid van Moolenbroek 	int r;
393433d6423SLionel Sambuc 
3945260f07cSDavid van Moolenbroek 	if ((r = virtio_net_probe(instance)) != OK)
3955260f07cSDavid van Moolenbroek 		return r;
396433d6423SLionel Sambuc 
3975260f07cSDavid van Moolenbroek 	virtio_net_config(addr);
398433d6423SLionel Sambuc 
399433d6423SLionel Sambuc 	if (virtio_net_alloc_bufs() != OK)
400*f7df02e7SDavid van Moolenbroek 		panic("%s: Buffer allocation failed", netdriver_name());
401433d6423SLionel Sambuc 
402433d6423SLionel Sambuc 	virtio_net_init_queues();
403433d6423SLionel Sambuc 
4045260f07cSDavid van Moolenbroek 	/* Add packets to the receive queue. */
4055260f07cSDavid van Moolenbroek 	virtio_net_refill_rx_queue();
4065260f07cSDavid van Moolenbroek 
4075260f07cSDavid van Moolenbroek 	virtio_device_ready(net_dev);
4085260f07cSDavid van Moolenbroek 
4095260f07cSDavid van Moolenbroek 	virtio_irq_enable(net_dev);
410433d6423SLionel Sambuc 
411*f7df02e7SDavid van Moolenbroek 	*caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST;
412*f7df02e7SDavid van Moolenbroek 	return OK;
413433d6423SLionel Sambuc }
414433d6423SLionel Sambuc 
4155260f07cSDavid van Moolenbroek /*
4165260f07cSDavid van Moolenbroek  * The driver is terminating.  Clean up.
4175260f07cSDavid van Moolenbroek  */
418433d6423SLionel Sambuc static void
virtio_net_stop(void)4195260f07cSDavid van Moolenbroek virtio_net_stop(void)
420433d6423SLionel Sambuc {
421433d6423SLionel Sambuc 
422433d6423SLionel Sambuc 	dput(("Terminating"));
423433d6423SLionel Sambuc 
424433d6423SLionel Sambuc 	free_contig(data_vir, PACKET_BUF_SZ);
425433d6423SLionel Sambuc 	free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
426433d6423SLionel Sambuc 	free(packets);
427433d6423SLionel Sambuc 
428433d6423SLionel Sambuc 	virtio_reset_device(net_dev);
429433d6423SLionel Sambuc 	virtio_free_queues(net_dev);
430433d6423SLionel Sambuc 	virtio_free_device(net_dev);
431433d6423SLionel Sambuc 	net_dev = NULL;
4325260f07cSDavid van Moolenbroek }
433433d6423SLionel Sambuc 
4345260f07cSDavid van Moolenbroek /*
4355260f07cSDavid van Moolenbroek  * The virtio-net device driver.
4365260f07cSDavid van Moolenbroek  */
4375260f07cSDavid van Moolenbroek int
main(int argc,char * argv[])4385260f07cSDavid van Moolenbroek main(int argc, char *argv[])
4395260f07cSDavid van Moolenbroek {
4405260f07cSDavid van Moolenbroek 
4415260f07cSDavid van Moolenbroek 	env_setargs(argc, argv);
4425260f07cSDavid van Moolenbroek 
4435260f07cSDavid van Moolenbroek 	netdriver_task(&virtio_net_table);
4445260f07cSDavid van Moolenbroek 
4455260f07cSDavid van Moolenbroek 	return 0;
446433d6423SLionel Sambuc }
447