Remove building with NOCRYPTO option
[minix3.git] / minix / drivers / net / virtio_net / virtio_net.c
blob5f2dd679e42e25cfe64588f6fb316a7a74fc8712
1 /* virtio net driver for MINIX 3
3 * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
5 * This software is released under the BSD license. See the LICENSE file
6 * included in the main directory of this source distribution for the
7 * license terms and conditions.
8 */
10 #include <assert.h>
11 #include <sys/types.h>
13 #include <minix/drivers.h>
14 #include <minix/netdriver.h>
15 #include <minix/virtio.h>
17 #include <sys/queue.h>
19 #include "virtio_net.h"
21 #define VERBOSE 0
23 #if VERBOSE
24 #define dput(s) do { dprintf(s); printf("\n"); } while (0)
25 #define dprintf(s) do { \
26 printf("%s: ", netdriver_name()); \
27 printf s; \
28 } while (0)
29 #else
30 #define dput(s)
31 #define dprintf(s)
32 #endif
34 static struct virtio_device *net_dev;
36 enum queue {RX_Q, TX_Q, CTRL_Q};
38 /* Number of packets to work with */
39 /* TODO: This should be an argument to the driver and possibly also
40 * depend on the queue sizes offered by this device.
42 #define BUF_PACKETS 64
43 /* Maximum size of a packet */
44 #define MAX_PACK_SIZE NDEV_ETH_PACKET_MAX
45 /* Buffer size needed for the payload of BUF_PACKETS */
46 #define PACKET_BUF_SZ (BUF_PACKETS * MAX_PACK_SIZE)
48 struct packet {
49 int idx;
50 struct virtio_net_hdr *vhdr;
51 phys_bytes phdr;
52 char *vdata;
53 phys_bytes pdata;
54 size_t len;
55 STAILQ_ENTRY(packet) next;
58 /* Allocated data chunks */
59 static char *data_vir;
60 static phys_bytes data_phys;
61 static struct virtio_net_hdr *hdrs_vir;
62 static phys_bytes hdrs_phys;
63 static struct packet *packets;
64 static int in_rx;
66 /* Packets on this list can be given to the host */
67 static STAILQ_HEAD(free_list, packet) free_list;
69 /* Packets on this list are to be given to inet */
70 static STAILQ_HEAD(recv_list, packet) recv_list;
72 /* Various state data */
73 static int spurious_interrupt;
75 /* Prototypes */
76 static int virtio_net_probe(unsigned int skip);
77 static void virtio_net_config(netdriver_addr_t *addr);
78 static int virtio_net_alloc_bufs(void);
79 static void virtio_net_init_queues(void);
81 static void virtio_net_refill_rx_queue(void);
82 static void virtio_net_check_queues(void);
83 static void virtio_net_check_pending(void);
85 static int virtio_net_init(unsigned int instance, netdriver_addr_t * addr,
86 uint32_t * caps, unsigned int * ticks);
87 static void virtio_net_stop(void);
88 static int virtio_net_send(struct netdriver_data *data, size_t len);
89 static ssize_t virtio_net_recv(struct netdriver_data *data, size_t max);
90 static void virtio_net_intr(unsigned int mask);
92 static const struct netdriver virtio_net_table = {
93 .ndr_name = "vio",
94 .ndr_init = virtio_net_init,
95 .ndr_stop = virtio_net_stop,
96 .ndr_recv = virtio_net_recv,
97 .ndr_send = virtio_net_send,
98 .ndr_intr = virtio_net_intr,
101 /* TODO: Features are pretty much ignored */
102 static struct virtio_feature netf[] = {
103 { "partial csum", VIRTIO_NET_F_CSUM, 0, 0 },
104 { "given mac", VIRTIO_NET_F_MAC, 0, 1 },
105 { "status ", VIRTIO_NET_F_STATUS, 0, 0 },
106 { "control channel", VIRTIO_NET_F_CTRL_VQ, 0, 1 },
107 { "control channel rx", VIRTIO_NET_F_CTRL_RX, 0, 0 }
110 static int
111 virtio_net_probe(unsigned int skip)
113 /* virtio-net has at least 2 queues */
114 int queues = 2;
115 net_dev= virtio_setup_device(0x00001, netdriver_name(), netf,
116 sizeof(netf) / sizeof(netf[0]),
117 1 /* threads */, skip);
118 if (net_dev == NULL)
119 return ENXIO;
121 /* If the host supports the control queue, allocate it as well */
122 if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
123 queues += 1;
125 if (virtio_alloc_queues(net_dev, queues) != OK) {
126 virtio_free_device(net_dev);
127 return ENOMEM;
130 return OK;
133 static void
134 virtio_net_config(netdriver_addr_t * addr)
136 u32_t mac14;
137 u32_t mac56;
138 int i;
140 if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) {
141 dprintf(("Mac set by host: "));
142 mac14 = virtio_sread32(net_dev, 0);
143 mac56 = virtio_sread32(net_dev, 4);
144 memcpy(&addr->na_addr[0], &mac14, 4);
145 memcpy(&addr->na_addr[4], &mac56, 2);
147 for (i = 0; i < 6; i++)
148 dprintf(("%02x%s", addr->na_addr[i],
149 i == 5 ? "\n" : ":"));
150 } else {
151 dput(("No mac"));
154 if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) {
155 dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6)));
156 } else {
157 dput(("No status"));
160 if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
161 dput(("Host supports control channel"));
163 if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX))
164 dput(("Host supports control channel for RX"));
167 static int
168 virtio_net_alloc_bufs(void)
170 data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys);
172 if (!data_vir)
173 return ENOMEM;
175 hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]),
176 0, &hdrs_phys);
178 if (!hdrs_vir) {
179 free_contig(data_vir, PACKET_BUF_SZ);
180 return ENOMEM;
183 packets = malloc(BUF_PACKETS * sizeof(packets[0]));
185 if (!packets) {
186 free_contig(data_vir, PACKET_BUF_SZ);
187 free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
188 return ENOMEM;
191 memset(data_vir, 0, PACKET_BUF_SZ);
192 memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0]));
193 memset(packets, 0, BUF_PACKETS * sizeof(packets[0]));
195 return OK;
198 static void
199 virtio_net_init_queues(void)
201 int i;
202 STAILQ_INIT(&free_list);
203 STAILQ_INIT(&recv_list);
205 for (i = 0; i < BUF_PACKETS; i++) {
206 packets[i].idx = i;
207 packets[i].vhdr = &hdrs_vir[i];
208 packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]);
209 packets[i].vdata = data_vir + i * MAX_PACK_SIZE;
210 packets[i].pdata = data_phys + i * MAX_PACK_SIZE;
211 STAILQ_INSERT_HEAD(&free_list, &packets[i], next);
215 static void
216 virtio_net_refill_rx_queue(void)
218 struct vumap_phys phys[2];
219 struct packet *p;
221 while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) {
222 /* peek */
223 p = STAILQ_FIRST(&free_list);
224 /* remove */
225 STAILQ_REMOVE_HEAD(&free_list, next);
227 phys[0].vp_addr = p->phdr;
228 assert(!(phys[0].vp_addr & 1));
229 phys[0].vp_size = sizeof(struct virtio_net_hdr);
231 phys[1].vp_addr = p->pdata;
232 assert(!(phys[1].vp_addr & 1));
233 phys[1].vp_size = MAX_PACK_SIZE;
235 /* RX queue needs write */
236 phys[0].vp_addr |= 1;
237 phys[1].vp_addr |= 1;
239 virtio_to_queue(net_dev, RX_Q, phys, 2, p);
240 in_rx++;
243 if (in_rx == 0 && STAILQ_EMPTY(&free_list))
244 dput(("warning: rx queue underflow!"));
247 static void
248 virtio_net_check_queues(void)
250 struct packet *p;
251 size_t len;
253 /* Put the received packets into the recv list */
254 while (virtio_from_queue(net_dev, RX_Q, (void **)&p, &len) == 0) {
255 p->len = len;
256 STAILQ_INSERT_TAIL(&recv_list, p, next);
257 in_rx--;
261 * Packets from the TX queue just indicated they are free to
262 * be reused now. inet already knows about them as being sent.
264 while (virtio_from_queue(net_dev, TX_Q, (void **)&p, NULL) == 0) {
265 memset(p->vhdr, 0, sizeof(*p->vhdr));
266 memset(p->vdata, 0, MAX_PACK_SIZE);
267 STAILQ_INSERT_HEAD(&free_list, p, next);
271 static void
272 virtio_net_check_pending(void)
275 /* Pending read and something in recv_list? */
276 if (!STAILQ_EMPTY(&recv_list))
277 netdriver_recv();
279 if (!STAILQ_EMPTY(&free_list))
280 netdriver_send();
283 static void
284 virtio_net_intr(unsigned int __unused mask)
287 /* Check and clear interrupt flag */
288 if (virtio_had_irq(net_dev)) {
289 virtio_net_check_queues();
290 } else {
291 if (!spurious_interrupt)
292 dput(("Spurious interrupt"));
294 spurious_interrupt = 1;
297 virtio_net_check_pending();
299 virtio_irq_enable(net_dev);
301 /* Readd packets to the receive queue as necessary. */
302 virtio_net_refill_rx_queue();
306 * Put user bytes into a free packet buffer, forward this packet to the TX
307 * queue, and return OK. If there are no free packet buffers, return SUSPEND.
309 static int
310 virtio_net_send(struct netdriver_data * data, size_t len)
312 struct vumap_phys phys[2];
313 struct packet *p;
315 if (STAILQ_EMPTY(&free_list))
316 return SUSPEND;
318 p = STAILQ_FIRST(&free_list);
319 STAILQ_REMOVE_HEAD(&free_list, next);
321 if (len > MAX_PACK_SIZE)
322 panic("%s: packet too large to send: %zu",
323 netdriver_name(), len);
325 netdriver_copyin(data, 0, p->vdata, len);
327 phys[0].vp_addr = p->phdr;
328 assert(!(phys[0].vp_addr & 1));
329 phys[0].vp_size = sizeof(struct virtio_net_hdr);
330 phys[1].vp_addr = p->pdata;
331 assert(!(phys[1].vp_addr & 1));
332 phys[1].vp_size = len;
333 virtio_to_queue(net_dev, TX_Q, phys, 2, p);
335 return OK;
339 * Put a packet receive from the RX queue into a user buffer, and return the
340 * packet length. If there are no received packets, return SUSPEND.
342 static ssize_t
343 virtio_net_recv(struct netdriver_data * data, size_t max)
345 struct packet *p;
346 ssize_t len;
348 /* Get the first received packet, if any. */
349 if (STAILQ_EMPTY(&recv_list))
350 return SUSPEND;
352 p = STAILQ_FIRST(&recv_list);
353 STAILQ_REMOVE_HEAD(&recv_list, next);
355 /* Copy out the packet contents. */
356 if (p->len < sizeof(struct virtio_net_hdr))
357 panic("received packet does not have virtio header");
358 len = p->len - sizeof(struct virtio_net_hdr);
359 if ((size_t)len > max)
360 len = (ssize_t)max;
363 * HACK: due to lack of padding, received packets may in fact be
364 * smaller than the minimum ethernet packet size. The TCP/IP service
365 * will accept the packets just fine if we increase the length to its
366 * minimum. We already zeroed out the rest of the packet data, so this
367 * is safe.
369 if (len < NDEV_ETH_PACKET_MIN)
370 len = NDEV_ETH_PACKET_MIN;
372 netdriver_copyout(data, 0, p->vdata, len);
374 /* Clean the packet. */
375 memset(p->vhdr, 0, sizeof(*p->vhdr));
376 memset(p->vdata, 0, MAX_PACK_SIZE);
377 STAILQ_INSERT_HEAD(&free_list, p, next);
379 /* Readd packets to the receive queue as necessary. */
380 virtio_net_refill_rx_queue();
382 return len;
386 * Initialize the driver and the virtual hardware.
388 static int
389 virtio_net_init(unsigned int instance, netdriver_addr_t * addr,
390 uint32_t * caps, unsigned int * ticks __unused)
392 int r;
394 if ((r = virtio_net_probe(instance)) != OK)
395 return r;
397 virtio_net_config(addr);
399 if (virtio_net_alloc_bufs() != OK)
400 panic("%s: Buffer allocation failed", netdriver_name());
402 virtio_net_init_queues();
404 /* Add packets to the receive queue. */
405 virtio_net_refill_rx_queue();
407 virtio_device_ready(net_dev);
409 virtio_irq_enable(net_dev);
411 *caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST;
412 return OK;
416 * The driver is terminating. Clean up.
418 static void
419 virtio_net_stop(void)
422 dput(("Terminating"));
424 free_contig(data_vir, PACKET_BUF_SZ);
425 free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
426 free(packets);
428 virtio_reset_device(net_dev);
429 virtio_free_queues(net_dev);
430 virtio_free_device(net_dev);
431 net_dev = NULL;
435 * The virtio-net device driver.
438 main(int argc, char *argv[])
441 env_setargs(argc, argv);
443 netdriver_task(&virtio_net_table);
445 return 0;