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.
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"
24 #define dput(s) do { dprintf(s); printf("\n"); } while (0)
25 #define dprintf(s) do { \
26 printf("%s: ", netdriver_name()); \
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)
50 struct virtio_net_hdr
*vhdr
;
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
;
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
;
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
= {
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 }
111 virtio_net_probe(unsigned int skip
)
113 /* virtio-net has at least 2 queues */
115 net_dev
= virtio_setup_device(0x00001, netdriver_name(), netf
,
116 sizeof(netf
) / sizeof(netf
[0]),
117 1 /* threads */, skip
);
121 /* If the host supports the control queue, allocate it as well */
122 if (virtio_host_supports(net_dev
, VIRTIO_NET_F_CTRL_VQ
))
125 if (virtio_alloc_queues(net_dev
, queues
) != OK
) {
126 virtio_free_device(net_dev
);
134 virtio_net_config(netdriver_addr_t
* addr
)
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" : ":"));
154 if (virtio_host_supports(net_dev
, VIRTIO_NET_F_STATUS
)) {
155 dput(("Current Status %x", (u32_t
)virtio_sread16(net_dev
, 6)));
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"));
168 virtio_net_alloc_bufs(void)
170 data_vir
= alloc_contig(PACKET_BUF_SZ
, 0, &data_phys
);
175 hdrs_vir
= alloc_contig(BUF_PACKETS
* sizeof(hdrs_vir
[0]),
179 free_contig(data_vir
, PACKET_BUF_SZ
);
183 packets
= malloc(BUF_PACKETS
* sizeof(packets
[0]));
186 free_contig(data_vir
, PACKET_BUF_SZ
);
187 free_contig(hdrs_vir
, BUF_PACKETS
* sizeof(hdrs_vir
[0]));
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]));
199 virtio_net_init_queues(void)
202 STAILQ_INIT(&free_list
);
203 STAILQ_INIT(&recv_list
);
205 for (i
= 0; i
< BUF_PACKETS
; 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
);
216 virtio_net_refill_rx_queue(void)
218 struct vumap_phys phys
[2];
221 while ((in_rx
< BUF_PACKETS
/ 2) && !STAILQ_EMPTY(&free_list
)) {
223 p
= STAILQ_FIRST(&free_list
);
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
);
243 if (in_rx
== 0 && STAILQ_EMPTY(&free_list
))
244 dput(("warning: rx queue underflow!"));
248 virtio_net_check_queues(void)
253 /* Put the received packets into the recv list */
254 while (virtio_from_queue(net_dev
, RX_Q
, (void **)&p
, &len
) == 0) {
256 STAILQ_INSERT_TAIL(&recv_list
, p
, next
);
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
);
272 virtio_net_check_pending(void)
275 /* Pending read and something in recv_list? */
276 if (!STAILQ_EMPTY(&recv_list
))
279 if (!STAILQ_EMPTY(&free_list
))
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();
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.
310 virtio_net_send(struct netdriver_data
* data
, size_t len
)
312 struct vumap_phys phys
[2];
315 if (STAILQ_EMPTY(&free_list
))
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
);
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.
343 virtio_net_recv(struct netdriver_data
* data
, size_t max
)
348 /* Get the first received packet, if any. */
349 if (STAILQ_EMPTY(&recv_list
))
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
)
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
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();
386 * Initialize the driver and the virtual hardware.
389 virtio_net_init(unsigned int instance
, netdriver_addr_t
* addr
,
390 uint32_t * caps
, unsigned int * ticks __unused
)
394 if ((r
= virtio_net_probe(instance
)) != OK
)
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
;
416 * The driver is terminating. Clean up.
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]));
428 virtio_reset_device(net_dev
);
429 virtio_free_queues(net_dev
);
430 virtio_free_device(net_dev
);
435 * The virtio-net device driver.
438 main(int argc
, char *argv
[])
441 env_setargs(argc
, argv
);
443 netdriver_task(&virtio_net_table
);