source: mainline/uspace/drv/nic/virtio-net/virtio-net.c@ 417aaafb

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 417aaafb was 417aaafb, checked in by Jakub Jermar <jakub@…>, 7 years ago

Protect the virtqueue with a mutex

  • Property mode set to 100644
File size: 15.7 KB
RevLine 
[033ebc25]1/*
2 * Copyright (c) 2018 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[1c40351]29#include "virtio-net.h"
30
[6413967]31#include <stdio.h>
[69927fa]32#include <stdint.h>
[6413967]33
[9364ced]34#include <as.h>
[6413967]35#include <ddf/driver.h>
[fe96085]36#include <ddf/interrupt.h>
[6413967]37#include <ddf/log.h>
38#include <ops/nic.h>
[69927fa]39#include <pci_dev_iface.h>
[6a0f1309]40#include <nic/nic.h>
[6413967]41
42#include <nic.h>
43
[00192cde]44#include <virtio-pci.h>
[69927fa]45
[00192cde]46#define NAME "virtio-net"
[69927fa]47
[cbcb34c]48#define VIRTIO_NET_NUM_QUEUES 3
49
[9364ced]50#define RX_QUEUE_1 0
51#define TX_QUEUE_1 1
52#define CT_QUEUE_1 2
[cbcb34c]53
[9364ced]54#define BUFFER_SIZE 2048
55#define RX_BUF_SIZE BUFFER_SIZE
56#define TX_BUF_SIZE BUFFER_SIZE
57#define CT_BUF_SIZE BUFFER_SIZE
58
[211b17a1]59static ddf_dev_ops_t virtio_net_dev_ops;
60
61static errno_t virtio_net_dev_add(ddf_dev_t *dev);
62
63static driver_ops_t virtio_net_driver_ops = {
64 .dev_add = virtio_net_dev_add
65};
66
67static driver_t virtio_net_driver = {
68 .name = NAME,
69 .driver_ops = &virtio_net_driver_ops
70};
[9364ced]71
[8b3cb67]72/** Allocate DMA buffers
73 *
74 * @param buffers[in] Number of buffers to allocate.
75 * @param size[in] Size of each buffer.
76 * @param write[in] True if the buffers are writable by the driver, false
77 * otherwise.
78 * @param buf[out] Output array holding virtual addresses of the allocated
79 * buffers.
80 * @param buf_p[out] Output array holding physical addresses of the allocated
81 * buffers.
82 *
83 * The buffers can be deallocated by virtio_net_teardown_bufs().
84 *
85 * @return EOK on success or error code.
86 */
[9364ced]87static errno_t virtio_net_setup_bufs(unsigned int buffers, size_t size,
88 bool write, void *buf[], uintptr_t buf_p[])
89{
90 /*
[8b3cb67]91 * Allocate all buffers at once in one large chunk.
[9364ced]92 */
93 void *virt = AS_AREA_ANY;
94 uintptr_t phys;
95 errno_t rc = dmamem_map_anonymous(buffers * size, 0,
96 write ? AS_AREA_WRITE : AS_AREA_READ, 0, &phys, &virt);
97 if (rc != EOK)
98 return rc;
99
100 ddf_msg(LVL_NOTE, "DMA buffers: %p-%p", virt, virt + buffers * size);
101
102 /*
103 * Calculate addresses of the individual buffers for easy access.
104 */
105 for (unsigned i = 0; i < buffers; i++) {
106 buf[i] = virt + i * size;
107 buf_p[i] = phys + i * size;
108 }
109
110 return EOK;
111}
112
[8b3cb67]113/** Deallocate DMA buffers
114 *
115 * @param buf[in] Array holding the virtual addresses of the DMA buffers
116 * previously allocated by virtio_net_setup_bufs().
117 */
[9364ced]118static void virtio_net_teardown_bufs(void *buf[])
119{
120 if (buf[0]) {
121 dmamem_unmap_anonymous(buf[0]);
122 buf[0] = NULL;
123 }
124}
[cbcb34c]125
[8b3cb67]126/** Create free descriptor list from the unused VIRTIO descriptors
127 *
128 * @param vdev[in] VIRTIO device for which the free list will be created.
129 * @param num[in] Index of the virtqueue for which the free list will be
130 * created.
131 * @param size[in] Number of descriptors on the free list. The free list will
132 * contain descriptors starting from 0 to \a size - 1.
133 * @param head[out] Variable that will hold the VIRTIO descriptor at the head
134 * of the free list.
135 */
136static void virtio_net_create_desc_free_list(virtio_dev_t *vdev, uint16_t num,
[b085bbaa]137 uint16_t size, uint16_t *head)
138{
139 for (unsigned i = 0; i < size; i++) {
[3d135e9]140 virtio_virtq_desc_set(vdev, num, i, 0, 0,
[b085bbaa]141 VIRTQ_DESC_F_NEXT, (i + 1 == size) ? -1U : i + 1);
142 }
143 *head = 0;
144}
145
[8b3cb67]146/** Allocate a descriptor from the free list
147 *
148 * @param vdev[in] VIRTIO device with the free list.
149 * @param num[in] Index of the virtqueue with free list.
150 * @param head[in,out] Head of the free list.
151 *
152 * @return Allocated descriptor or 0xFFFF if the list is empty.
153 */
154static uint16_t virtio_net_alloc_desc(virtio_dev_t *vdev, uint16_t num,
[3d135e9]155 uint16_t *head)
156{
[417aaafb]157 virtq_t *q = &vdev->queues[num];
158 fibril_mutex_lock(&q->lock);
[3d135e9]159 uint16_t descno = *head;
160 if (descno != (uint16_t) -1U)
161 *head = virtio_virtq_desc_get_next(vdev, num, descno);
[417aaafb]162 fibril_mutex_unlock(&q->lock);
[3d135e9]163 return descno;
164}
165
[8b3cb67]166/** Free a descriptor into the free list
167 *
168 * @param vdev[in] VIRTIO device with the free list.
169 * @param num[in] Index of the virtqueue with free list.
170 * @param head[in,out] Head of the free list.
171 * @param descno[in] The freed descriptor.
172 */
173static void virtio_net_free_desc(virtio_dev_t *vdev, uint16_t num,
[b8ef198b]174 uint16_t *head, uint16_t descno)
175{
[417aaafb]176 virtq_t *q = &vdev->queues[num];
177 fibril_mutex_lock(&q->lock);
[b8ef198b]178 virtio_virtq_desc_set(vdev, num, descno, 0, 0, VIRTQ_DESC_F_NEXT,
179 *head);
180 *head = descno;
[417aaafb]181 fibril_mutex_unlock(&q->lock);
[b8ef198b]182}
183
184static void virtio_net_irq_handler(ipc_call_t *icall, ddf_dev_t *dev)
185{
186 nic_t *nic = ddf_dev_data_get(dev);
187 virtio_net_t *virtio_net = nic_get_specific(nic);
188 virtio_dev_t *vdev = &virtio_net->virtio_dev;
189
190 uint16_t descno;
191 uint32_t len;
192 while (virtio_virtq_consume_used(vdev, RX_QUEUE_1, &descno, &len)) {
193 virtio_net_hdr_t *hdr =
194 (virtio_net_hdr_t *) virtio_net->rx_buf[descno];
195 if (len <= sizeof(*hdr)) {
196 ddf_msg(LVL_WARN,
197 "RX data length too short, packet dropped");
198 virtio_virtq_produce_available(vdev, RX_QUEUE_1,
199 descno);
200 continue;
201 }
202
203 nic_frame_t *frame = nic_alloc_frame(nic, len - sizeof(*hdr));
204 if (frame) {
205 memcpy(frame->data, &hdr[1], len - sizeof(*hdr));
206 nic_received_frame(nic, frame);
207 } else {
208 ddf_msg(LVL_WARN,
209 "Cannot allocate RX frame, packet dropped");
210 }
211
212 virtio_virtq_produce_available(vdev, RX_QUEUE_1, descno);
213 }
214
215 while (virtio_virtq_consume_used(vdev, TX_QUEUE_1, &descno, &len)) {
[8b3cb67]216 virtio_net_free_desc(vdev, TX_QUEUE_1,
217 &virtio_net->tx_free_head, descno);
[b8ef198b]218 }
219 while (virtio_virtq_consume_used(vdev, CT_QUEUE_1, &descno, &len)) {
[8b3cb67]220 virtio_net_free_desc(vdev, CT_QUEUE_1,
221 &virtio_net->ct_free_head, descno);
[b8ef198b]222 }
223}
224
[fe96085]225static errno_t virtio_net_register_interrupt(ddf_dev_t *dev)
226{
227 nic_t *nic = ddf_dev_data_get(dev);
228 virtio_net_t *virtio_net = nic_get_specific(nic);
229 virtio_dev_t *vdev = &virtio_net->virtio_dev;
230
231 hw_res_list_parsed_t res;
232 hw_res_list_parsed_init(&res);
233
234 errno_t rc = nic_get_resources(nic, &res);
235 if (rc != EOK)
236 return rc;
237
238 if (res.irqs.count < 1) {
239 hw_res_list_parsed_clean(&res);
240 rc = EINVAL;
241 return rc;
242 }
243
244 virtio_net->irq = res.irqs.irqs[0];
245 hw_res_list_parsed_clean(&res);
246
247 irq_pio_range_t pio_ranges[] = {
248 {
249 .base = vdev->isr_phys,
250 .size = sizeof(vdev->isr_phys),
251 }
252 };
253
254 irq_cmd_t irq_commands[] = {
255 {
256 .cmd = CMD_PIO_READ_8,
257 .addr = (void *) vdev->isr_phys,
258 .dstarg = 2
259 },
260 {
261 .cmd = CMD_PREDICATE,
262 .value = 1,
263 .srcarg = 2
264 },
265 {
266 .cmd = CMD_ACCEPT
267 }
268 };
269
270 irq_code_t irq_code = {
271 .rangecount = sizeof(pio_ranges) / sizeof(irq_pio_range_t),
272 .ranges = pio_ranges,
273 .cmdcount = sizeof(irq_commands) / sizeof(irq_cmd_t),
274 .cmds = irq_commands
275 };
276
277 return register_interrupt_handler(dev, virtio_net->irq,
278 virtio_net_irq_handler, &irq_code, &virtio_net->irq_handle);
279}
280
[1c40351]281static errno_t virtio_net_initialize(ddf_dev_t *dev)
282{
[fe96085]283 nic_t *nic = nic_create_and_bind(dev);
284 if (!nic)
[1c40351]285 return ENOMEM;
286
287 virtio_net_t *virtio_net = calloc(1, sizeof(virtio_net_t));
288 if (!virtio_net) {
289 nic_unbind_and_destroy(dev);
290 return ENOMEM;
291 }
292
[fe96085]293 nic_set_specific(nic, virtio_net);
[1c40351]294
295 errno_t rc = virtio_pci_dev_initialize(dev, &virtio_net->virtio_dev);
296 if (rc != EOK)
297 return rc;
298
[cbcb34c]299 virtio_dev_t *vdev = &virtio_net->virtio_dev;
[1c40351]300 virtio_pci_common_cfg_t *cfg = virtio_net->virtio_dev.common_cfg;
301 virtio_net_cfg_t *netcfg = virtio_net->virtio_dev.device_cfg;
302
[fe96085]303 /*
304 * Register IRQ
305 */
306 rc = virtio_net_register_interrupt(dev);
307 if (rc != EOK)
308 goto fail;
309
[341df5f]310 /* Reset the device and negotiate the feature bits */
311 rc = virtio_device_setup_start(vdev,
312 VIRTIO_NET_F_MAC | VIRTIO_NET_F_CTRL_VQ);
313 if (rc != EOK)
[1d0620b]314 goto fail;
[1c40351]315
[341df5f]316 /* Perform device-specific setup */
[cbcb34c]317
318 /*
319 * Discover and configure the virtqueues
320 */
[9af56b6]321 uint16_t num_queues = pio_read_le16(&cfg->num_queues);
[cbcb34c]322 if (num_queues != VIRTIO_NET_NUM_QUEUES) {
323 ddf_msg(LVL_NOTE, "Unsupported number of virtqueues: %u",
324 num_queues);
325 goto fail;
326 }
327
328 vdev->queues = calloc(sizeof(virtq_t), num_queues);
329 if (!vdev->queues) {
330 rc = ENOMEM;
331 goto fail;
332 }
333
[9364ced]334 rc = virtio_virtq_setup(vdev, RX_QUEUE_1, RX_BUFFERS);
335 if (rc != EOK)
336 goto fail;
337 rc = virtio_virtq_setup(vdev, TX_QUEUE_1, TX_BUFFERS);
338 if (rc != EOK)
339 goto fail;
340 rc = virtio_virtq_setup(vdev, CT_QUEUE_1, CT_BUFFERS);
341 if (rc != EOK)
342 goto fail;
343
344 /*
345 * Setup DMA buffers
346 */
347 rc = virtio_net_setup_bufs(RX_BUFFERS, RX_BUF_SIZE, false,
348 virtio_net->rx_buf, virtio_net->rx_buf_p);
[cbcb34c]349 if (rc != EOK)
350 goto fail;
[9364ced]351 rc = virtio_net_setup_bufs(TX_BUFFERS, TX_BUF_SIZE, true,
352 virtio_net->tx_buf, virtio_net->tx_buf_p);
[cbcb34c]353 if (rc != EOK)
354 goto fail;
[9364ced]355 rc = virtio_net_setup_bufs(CT_BUFFERS, CT_BUF_SIZE, true,
356 virtio_net->ct_buf, virtio_net->ct_buf_p);
[cbcb34c]357 if (rc != EOK)
358 goto fail;
359
[5dddac1]360 /*
361 * Give all RX buffers to the NIC
362 */
363 for (unsigned i = 0; i < RX_BUFFERS; i++) {
364 /*
365 * Associtate the buffer with the descriptor, set length and
366 * flags.
367 */
[3d135e9]368 virtio_virtq_desc_set(vdev, RX_QUEUE_1, i,
[5dddac1]369 virtio_net->rx_buf_p[i], RX_BUF_SIZE, VIRTQ_DESC_F_WRITE,
370 0);
371 /*
372 * Put the set descriptor into the available ring of the RX
373 * queue.
374 */
375 virtio_virtq_produce_available(vdev, RX_QUEUE_1, i);
376 }
377
[b085bbaa]378 /*
379 * Put all TX and CT buffers on a free list
380 */
[8b3cb67]381 virtio_net_create_desc_free_list(vdev, TX_QUEUE_1, TX_BUFFERS,
[b085bbaa]382 &virtio_net->tx_free_head);
[8b3cb67]383 virtio_net_create_desc_free_list(vdev, CT_QUEUE_1, CT_BUFFERS,
[b085bbaa]384 &virtio_net->ct_free_head);
385
[cbcb34c]386 /*
387 * Read the MAC address
388 */
[1c40351]389 nic_address_t nic_addr;
[6a0f1309]390 for (unsigned i = 0; i < ETH_ADDR; i++)
[cbcb34c]391 nic_addr.address[i] = pio_read_8(&netcfg->mac[i]);
[fe96085]392 rc = nic_report_address(nic, &nic_addr);
[1d0620b]393 if (rc != EOK)
394 goto fail;
[1c40351]395
[ea6840d]396 ddf_msg(LVL_NOTE, "MAC address: " PRIMAC, ARGSMAC(nic_addr.address));
[1c40351]397
[fe96085]398 /*
399 * Enable IRQ
400 */
401 rc = hw_res_enable_interrupt(ddf_dev_parent_sess_get(dev),
402 virtio_net->irq);
403 if (rc != EOK) {
404 ddf_msg(LVL_NOTE, "Failed to enable interrupt");
405 goto fail;
406 }
407
408 ddf_msg(LVL_NOTE, "Registered IRQ %d", virtio_net->irq);
409
[341df5f]410 /* Go live */
411 virtio_device_setup_finalize(vdev);
[1c40351]412
413 return EOK;
[1d0620b]414
415fail:
[9364ced]416 virtio_net_teardown_bufs(virtio_net->rx_buf);
417 virtio_net_teardown_bufs(virtio_net->tx_buf);
418 virtio_net_teardown_bufs(virtio_net->ct_buf);
419
[341df5f]420 virtio_device_setup_fail(vdev);
421 virtio_pci_dev_cleanup(vdev);
[1d0620b]422 return rc;
[1c40351]423}
424
[211b17a1]425static void virtio_net_uninitialize(ddf_dev_t *dev)
426{
427 nic_t *nic = ddf_dev_data_get(dev);
428 virtio_net_t *virtio_net = (virtio_net_t *) nic_get_specific(nic);
429
430 virtio_net_teardown_bufs(virtio_net->rx_buf);
431 virtio_net_teardown_bufs(virtio_net->tx_buf);
432 virtio_net_teardown_bufs(virtio_net->ct_buf);
433
434 virtio_device_setup_fail(&virtio_net->virtio_dev);
435 virtio_pci_dev_cleanup(&virtio_net->virtio_dev);
436}
437
[121ee4fd]438static void virtio_net_send(nic_t *nic, void *data, size_t size)
439{
[3d135e9]440 virtio_net_t *virtio_net = nic_get_specific(nic);
441 virtio_dev_t *vdev = &virtio_net->virtio_dev;
442
443 if (size > sizeof(virtio_net) + TX_BUF_SIZE) {
444 ddf_msg(LVL_WARN, "TX data too big, frame dropped");
445 return;
446 }
447
[8b3cb67]448 uint16_t descno = virtio_net_alloc_desc(vdev, TX_QUEUE_1,
[3d135e9]449 &virtio_net->tx_free_head);
450 if (descno == (uint16_t) -1U) {
451 ddf_msg(LVL_WARN, "No TX buffers available, frame dropped");
452 return;
453 }
454 assert(descno < TX_BUFFERS);
455
456 /* Setup the packed header */
457 virtio_net_hdr_t *hdr = (virtio_net_hdr_t *) virtio_net->tx_buf[descno];
458 memset(hdr, 0, sizeof(virtio_net_hdr_t));
459 hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
460
[b8ef198b]461 /* Copy packet data into the buffer just past the header */
[3d135e9]462 memcpy(&hdr[1], data, size);
463
464 /*
465 * Set the descriptor, put it into the virtqueue and notify the device
466 */
467 virtio_virtq_desc_set(vdev, TX_QUEUE_1, descno,
[4810acf]468 virtio_net->tx_buf_p[descno], sizeof(virtio_net_hdr_t) + size, 0, 0);
[3d135e9]469 virtio_virtq_produce_available(vdev, TX_QUEUE_1, descno);
[121ee4fd]470}
471
[5609d3c]472
473static errno_t virtio_net_on_multicast_mode_change(nic_t *nic,
474 nic_multicast_mode_t new_mode, const nic_address_t *address_list,
475 size_t address_count)
476{
477 switch (new_mode) {
478 case NIC_MULTICAST_BLOCKED:
479 nic_report_hw_filtering(nic, -1, 0, -1);
480 return EOK;
481 case NIC_MULTICAST_LIST:
482 nic_report_hw_filtering(nic, -1, 0, -1);
483 return EOK;
484 case NIC_MULTICAST_PROMISC:
485 nic_report_hw_filtering(nic, -1, 0, -1);
486 return EOK;
487 default:
488 return ENOTSUP;
489 }
490 return EOK;
491}
492
[0d3bfb9e]493static errno_t virtio_net_on_broadcast_mode_change(nic_t *nic,
494 nic_broadcast_mode_t new_mode)
495{
496 switch (new_mode) {
497 case NIC_BROADCAST_BLOCKED:
498 return ENOTSUP;
499 case NIC_BROADCAST_ACCEPTED:
500 return EOK;
501 default:
502 return ENOTSUP;
503 }
504}
505
[6413967]506static errno_t virtio_net_dev_add(ddf_dev_t *dev)
507{
508 ddf_msg(LVL_NOTE, "%s %s (handle = %zu)", __func__,
509 ddf_dev_get_name(dev), ddf_dev_get_handle(dev));
510
[1c40351]511 errno_t rc = virtio_net_initialize(dev);
[69927fa]512 if (rc != EOK)
513 return rc;
514
[211b17a1]515 ddf_fun_t *fun = ddf_fun_create(dev, fun_exposed, "port0");
516 if (fun == NULL) {
517 rc = ENOMEM;
518 goto error;
519 }
520 nic_t *nic = ddf_dev_data_get(dev);
521 nic_set_ddf_fun(nic, fun);
522 ddf_fun_set_ops(fun, &virtio_net_dev_ops);
523
[121ee4fd]524 nic_set_send_frame_handler(nic, virtio_net_send);
[5609d3c]525 nic_set_filtering_change_handlers(nic, NULL,
526 virtio_net_on_multicast_mode_change,
[0d3bfb9e]527 virtio_net_on_broadcast_mode_change, NULL, NULL);
528
[211b17a1]529 rc = ddf_fun_bind(fun);
530 if (rc != EOK) {
531 ddf_msg(LVL_ERROR, "Failed binding device function");
532 goto uninitialize;
533 }
[6413967]534
[211b17a1]535 rc = ddf_fun_add_to_category(fun, DEVICE_CATEGORY_NIC);
536 if (rc != EOK) {
537 ddf_msg(LVL_ERROR, "Failed adding function to category");
538 goto unbind;
539 }
[6413967]540
[211b17a1]541 ddf_msg(LVL_NOTE, "The %s device has been successfully initialized.",
542 ddf_dev_get_name(dev));
[6413967]543
[211b17a1]544 return EOK;
545
546unbind:
547 ddf_fun_unbind(fun);
548uninitialize:
549 virtio_net_uninitialize(dev);
550error:
551 return rc;
552}
[6413967]553
[0a087ae]554static errno_t virtio_net_get_device_info(ddf_fun_t *fun,
555 nic_device_info_t *info)
556{
[fe96085]557 nic_t *nic = nic_get_from_ddf_fun(fun);
558 if (!nic)
[0a087ae]559 return ENOENT;
560
561 str_cpy(info->vendor_name, sizeof(info->vendor_name), "Red Hat, Inc.");
562 str_cpy(info->model_name, sizeof(info->model_name),
563 "Virtio network device");
564
565 return EOK;
566}
567
568static errno_t virtio_net_get_cable_state(ddf_fun_t *fun,
569 nic_cable_state_t *state)
570{
571 *state = NIC_CS_PLUGGED;
572 return EOK;
573}
574
575static errno_t virtio_net_get_operation_mode(ddf_fun_t *fun, int *speed,
576 nic_channel_mode_t *duplex, nic_role_t *role)
577{
578 *speed = 1000;
579 *duplex = NIC_CM_FULL_DUPLEX;
580 *role = NIC_ROLE_UNKNOWN;
581 return EOK;
582}
583
584static nic_iface_t virtio_net_nic_iface = {
585 .get_device_info = virtio_net_get_device_info,
586 .get_cable_state = virtio_net_get_cable_state,
587 .get_operation_mode = virtio_net_get_operation_mode,
588};
[6413967]589
[033ebc25]590int main(void)
591{
[6413967]592 printf("%s: HelenOS virtio-net driver\n", NAME);
593
594 if (nic_driver_init(NAME) != EOK)
595 return 1;
596
597 nic_driver_implement(&virtio_net_driver_ops, &virtio_net_dev_ops,
598 &virtio_net_nic_iface);
599
600 (void) ddf_log_init(NAME);
601 return ddf_driver_main(&virtio_net_driver);
[033ebc25]602}
Note: See TracBrowser for help on using the repository browser.