1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2021-11-11 GuEe-GUI the first version
9 */
10
11 #include <rthw.h>
12 #include <rtthread.h>
13 #include <cpuport.h>
14 #include <mm_aspace.h>
15
16 #ifdef RT_USING_VIRTIO_NET
17
18 #include <virtio_net.h>
19
virtio_net_tx(rt_device_t dev,struct pbuf * p)20 static rt_err_t virtio_net_tx(rt_device_t dev, struct pbuf *p)
21 {
22 rt_uint16_t id;
23 struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev;
24 struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev;
25 struct virtq *queue_tx = &virtio_dev->queues[VIRTIO_NET_QUEUE_TX];
26
27 id = (queue_tx->avail->idx * 2) % queue_tx->num;
28
29 virtio_net_dev->info[id].hdr.flags = 0;
30 virtio_net_dev->info[id].hdr.gso_type = 0;
31 virtio_net_dev->info[id].hdr.hdr_len = 0;
32 virtio_net_dev->info[id].hdr.gso_size = 0;
33 virtio_net_dev->info[id].hdr.csum_start = 0;
34 virtio_net_dev->info[id].hdr.csum_offset = 0;
35 virtio_net_dev->info[id].hdr.num_buffers = 0;
36
37 pbuf_copy_partial(p, virtio_net_dev->info[id].rx_buffer, p->tot_len, 0);
38
39 virtio_free_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id);
40 virtio_free_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id + 1);
41
42 virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id,
43 VIRTIO_VA2PA(&virtio_net_dev->info[id].hdr), VIRTIO_NET_HDR_SIZE, VIRTQ_DESC_F_NEXT, id + 1);
44
45 virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id + 1,
46 VIRTIO_VA2PA(virtio_net_dev->info[id].rx_buffer), p->tot_len, 0, 0);
47
48 virtio_submit_chain(virtio_dev, VIRTIO_NET_QUEUE_TX, id);
49
50 virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_TX);
51
52 virtio_alloc_desc(virtio_dev, VIRTIO_NET_QUEUE_TX);
53 virtio_alloc_desc(virtio_dev, VIRTIO_NET_QUEUE_TX);
54
55 return RT_EOK;
56 }
57
virtio_net_rx(rt_device_t dev)58 static struct pbuf *virtio_net_rx(rt_device_t dev)
59 {
60 rt_uint16_t id;
61 rt_uint32_t len;
62 struct pbuf *p = RT_NULL;
63 struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev;
64 struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev;
65 struct virtq *queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX];
66
67 if (queue_rx->used_idx != queue_rx->used->idx)
68 {
69 id = (queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id + 1) % queue_rx->num;
70 len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len - VIRTIO_NET_HDR_SIZE;
71
72 if (len > VIRTIO_NET_PAYLOAD_MAX_SIZE)
73 {
74 rt_kprintf("%s: Receive buffer's size = %u is too big!\n", virtio_net_dev->parent.parent.parent.name, len);
75 len = VIRTIO_NET_PAYLOAD_MAX_SIZE;
76 }
77
78 p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
79
80 if (p != RT_NULL)
81 {
82 rt_memcpy(p->payload, (void *)queue_rx->desc[id].addr - PV_OFFSET, len);
83
84 queue_rx->used_idx++;
85
86 virtio_submit_chain(virtio_dev, VIRTIO_NET_QUEUE_RX, id - 1);
87
88 virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_RX);
89 }
90 }
91
92 return p;
93 }
94
virtio_net_init(rt_device_t dev)95 static rt_err_t virtio_net_init(rt_device_t dev)
96 {
97 int i;
98 rt_uint16_t idx[VIRTIO_NET_RTX_QUEUE_SIZE];
99 struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev;
100 struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev;
101 struct virtq *queue_rx, *queue_tx;
102
103 queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX];
104 queue_tx = &virtio_dev->queues[VIRTIO_NET_QUEUE_TX];
105
106 virtio_alloc_desc_chain(virtio_dev, VIRTIO_NET_QUEUE_RX, queue_rx->num, idx);
107 virtio_alloc_desc_chain(virtio_dev, VIRTIO_NET_QUEUE_TX, queue_tx->num, idx);
108
109 for (i = 0; i < queue_rx->num; ++i)
110 {
111 rt_uint16_t id = (i * 2) % queue_rx->num;
112 void *addr = virtio_net_dev->info[i].tx_buffer;
113
114 /* Descriptor for net_hdr */
115 virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_RX, id,
116 VIRTIO_VA2PA(addr), VIRTIO_NET_HDR_SIZE, VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE, id + 1);
117
118 /* Descriptor for data */
119 virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_RX, id + 1,
120 VIRTIO_VA2PA(addr) + VIRTIO_NET_HDR_SIZE, VIRTIO_NET_MSS, VIRTQ_DESC_F_WRITE, 0);
121
122 queue_rx->avail->ring[i] = id;
123 }
124 rt_hw_dsb();
125
126 queue_rx->avail->flags = 0;
127 queue_rx->avail->idx = queue_rx->num;
128
129 queue_rx->used_idx = queue_rx->used->idx;
130
131 queue_tx->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT;
132 queue_tx->avail->idx = 0;
133
134 virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_RX);
135
136 return eth_device_linkchange(&virtio_net_dev->parent, RT_TRUE);
137 }
138
virtio_net_control(rt_device_t dev,int cmd,void * args)139 static rt_err_t virtio_net_control(rt_device_t dev, int cmd, void *args)
140 {
141 rt_err_t status = RT_EOK;
142 struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev;
143
144 switch (cmd)
145 {
146 case NIOCTL_GADDR:
147 if (args == RT_NULL)
148 {
149 status = -RT_ERROR;
150 break;
151 }
152
153 rt_memcpy(args, virtio_net_dev->config->mac, sizeof(virtio_net_dev->config->mac));
154 break;
155 default:
156 status = -RT_EINVAL;
157 break;
158 }
159
160 return status;
161 }
162
163 #ifdef RT_USING_DEVICE_OPS
164 const static struct rt_device_ops virtio_net_ops =
165 {
166 virtio_net_init,
167 RT_NULL,
168 RT_NULL,
169 RT_NULL,
170 RT_NULL,
171 virtio_net_control
172 };
173 #endif
174
virtio_net_isr(int irqno,void * param)175 static void virtio_net_isr(int irqno, void *param)
176 {
177 struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)param;
178 struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev;
179 struct virtq *queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX];
180
181 virtio_interrupt_ack(virtio_dev);
182 rt_hw_dsb();
183
184 if (queue_rx->used_idx != queue_rx->used->idx)
185 {
186 rt_hw_dsb();
187
188 eth_device_ready(&virtio_net_dev->parent);
189 }
190 }
191
rt_virtio_net_init(rt_ubase_t * mmio_base,rt_uint32_t irq)192 rt_err_t rt_virtio_net_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
193 {
194 static int dev_no = 0;
195 char dev_name[RT_NAME_MAX];
196 struct virtio_device *virtio_dev;
197 struct virtio_net_device *virtio_net_dev;
198
199 virtio_net_dev = rt_malloc(sizeof(struct virtio_net_device));
200
201 if (virtio_net_dev == RT_NULL)
202 {
203 goto _alloc_fail;
204 }
205
206 virtio_dev = &virtio_net_dev->virtio_dev;
207 virtio_dev->irq = irq;
208 virtio_dev->mmio_base = mmio_base;
209
210 virtio_net_dev->config = (struct virtio_net_config *)virtio_dev->mmio_config->config;
211
212 virtio_reset_device(virtio_dev);
213 virtio_status_acknowledge_driver(virtio_dev);
214
215 virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
216 (1 << VIRTIO_NET_F_CTRL_VQ) |
217 (1 << VIRTIO_F_RING_EVENT_IDX));
218
219 virtio_status_driver_ok(virtio_dev);
220
221 if (virtio_queues_alloc(virtio_dev, 2) != RT_EOK)
222 {
223 goto _alloc_fail;
224 }
225
226 if (virtio_queue_init(virtio_dev, VIRTIO_NET_QUEUE_RX, VIRTIO_NET_RTX_QUEUE_SIZE) != RT_EOK)
227 {
228 goto _alloc_fail;
229 }
230
231 if (virtio_queue_init(virtio_dev, VIRTIO_NET_QUEUE_TX, VIRTIO_NET_RTX_QUEUE_SIZE) != RT_EOK)
232 {
233 virtio_queue_destroy(virtio_dev, VIRTIO_NET_QUEUE_RX);
234 goto _alloc_fail;
235 }
236
237 virtio_net_dev->parent.parent.type = RT_Device_Class_NetIf;
238 #ifdef RT_USING_DEVICE_OPS
239 virtio_net_dev->parent.parent.ops = &virtio_net_ops;
240 #else
241 virtio_net_dev->parent.parent.init = virtio_net_init;
242 virtio_net_dev->parent.parent.open = RT_NULL;
243 virtio_net_dev->parent.parent.close = RT_NULL;
244 virtio_net_dev->parent.parent.read = RT_NULL;
245 virtio_net_dev->parent.parent.write = RT_NULL;
246 virtio_net_dev->parent.parent.control = virtio_net_control;
247 #endif
248 virtio_net_dev->parent.eth_tx = virtio_net_tx;
249 virtio_net_dev->parent.eth_rx = virtio_net_rx;
250
251 rt_snprintf(dev_name, RT_NAME_MAX, "virtio-net%d", dev_no++);
252
253 rt_hw_interrupt_install(irq, virtio_net_isr, virtio_net_dev, dev_name);
254 rt_hw_interrupt_umask(irq);
255
256 return eth_device_init(&virtio_net_dev->parent, dev_name);
257
258 _alloc_fail:
259
260 if (virtio_net_dev != RT_NULL)
261 {
262 virtio_queues_free(virtio_dev);
263 rt_free(virtio_net_dev);
264 }
265 return -RT_ENOMEM;
266 }
267 #endif /* RT_USING_VIRTIO_NET */
268