1d6aed566Sopenharmony_ci/*
2d6aed566Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3d6aed566Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4d6aed566Sopenharmony_ci * you may not use this file except in compliance with the License.
5d6aed566Sopenharmony_ci * You may obtain a copy of the License at
6d6aed566Sopenharmony_ci *
7d6aed566Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8d6aed566Sopenharmony_ci *
9d6aed566Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10d6aed566Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11d6aed566Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12d6aed566Sopenharmony_ci * See the License for the specific language governing permissions and
13d6aed566Sopenharmony_ci * limitations under the License.
14d6aed566Sopenharmony_ci */
15d6aed566Sopenharmony_ci
16d6aed566Sopenharmony_ci/* enable lwip 'netif_add' API */
17d6aed566Sopenharmony_ci#define __LWIP__
18d6aed566Sopenharmony_ci
19d6aed566Sopenharmony_ci#include "los_reg.h"
20d6aed566Sopenharmony_ci#include "los_compiler.h"
21d6aed566Sopenharmony_ci#include "los_debug.h"
22d6aed566Sopenharmony_ci#include "los_interrupt.h"
23d6aed566Sopenharmony_ci
24d6aed566Sopenharmony_ci#define IFNAMSIZ  IF_NAMESIZE
25d6aed566Sopenharmony_ci
26d6aed566Sopenharmony_ci#include "los_task.h"
27d6aed566Sopenharmony_ci#include "los_sched.h"
28d6aed566Sopenharmony_ciVOID LOS_TaskLockSave(UINT32 *intSave)
29d6aed566Sopenharmony_ci{
30d6aed566Sopenharmony_ci    *intSave = LOS_IntLock();
31d6aed566Sopenharmony_ci    g_losTaskLock++;
32d6aed566Sopenharmony_ci}
33d6aed566Sopenharmony_ci
34d6aed566Sopenharmony_ciVOID LOS_TaskUnlockRestore(UINT32 intSave)
35d6aed566Sopenharmony_ci{
36d6aed566Sopenharmony_ci    if (g_losTaskLock > 0) {
37d6aed566Sopenharmony_ci        g_losTaskLock--;
38d6aed566Sopenharmony_ci        if (g_losTaskLock == 0) {
39d6aed566Sopenharmony_ci            LOS_IntRestore(intSave);
40d6aed566Sopenharmony_ci            LOS_Schedule();
41d6aed566Sopenharmony_ci            return;
42d6aed566Sopenharmony_ci        }
43d6aed566Sopenharmony_ci    }
44d6aed566Sopenharmony_ci
45d6aed566Sopenharmony_ci    LOS_IntRestore(intSave);
46d6aed566Sopenharmony_ci}
47d6aed566Sopenharmony_ci
48d6aed566Sopenharmony_ci#define LOS_SpinLock(lock) LOS_TaskLock()
49d6aed566Sopenharmony_ci#define LOS_SpinUnlock(lock) LOS_TaskUnlock()
50d6aed566Sopenharmony_ci#define LOS_SpinLockSave(lock, intSave)  LOS_TaskLockSave(intSave)
51d6aed566Sopenharmony_ci#define LOS_SpinUnlockRestore(lock, intSave)  LOS_TaskUnlockRestore(intSave)
52d6aed566Sopenharmony_ci
53d6aed566Sopenharmony_ci#include "stddef.h"
54d6aed566Sopenharmony_citypedef struct Spinlock {
55d6aed566Sopenharmony_ci    size_t      rawLock;
56d6aed566Sopenharmony_ci#ifdef LOSCFG_KERNEL_SMP
57d6aed566Sopenharmony_ci    UINT32      cpuid;
58d6aed566Sopenharmony_ci    VOID        *owner;
59d6aed566Sopenharmony_ci    const CHAR  *name;
60d6aed566Sopenharmony_ci#endif
61d6aed566Sopenharmony_ci} SPIN_LOCK_S;
62d6aed566Sopenharmony_ci
63d6aed566Sopenharmony_ci/* kernel changed lwip 'netif->client_data' size, so this should be prior */
64d6aed566Sopenharmony_ci#include "netinet/if_ether.h"
65d6aed566Sopenharmony_ci
66d6aed566Sopenharmony_ci#include "lwip/opt.h"
67d6aed566Sopenharmony_ci#include "lwip/netif.h"
68d6aed566Sopenharmony_ci#include "lwip/etharp.h"
69d6aed566Sopenharmony_ci#include "lwip/tcpip.h"
70d6aed566Sopenharmony_ci#include "lwip/mem.h"
71d6aed566Sopenharmony_ci#include "virtmmio.h"
72d6aed566Sopenharmony_ci
73d6aed566Sopenharmony_ci#define VIRTIO_NET_F_MTU                    (1 << 3)
74d6aed566Sopenharmony_ci#define VIRTIO_NET_F_MAC                    (1 << 5)
75d6aed566Sopenharmony_cistruct VirtnetConfig {
76d6aed566Sopenharmony_ci    uint8_t mac[6];
77d6aed566Sopenharmony_ci    uint16_t status;
78d6aed566Sopenharmony_ci    uint16_t maxVirtqPairs;
79d6aed566Sopenharmony_ci    uint16_t mtu;
80d6aed566Sopenharmony_ci};
81d6aed566Sopenharmony_ci
82d6aed566Sopenharmony_ci#define VIRTMMIO_NETIF_NAME                 "virtnet"
83d6aed566Sopenharmony_ci#define VIRTMMIO_NETIF_NICK                 "vn0"
84d6aed566Sopenharmony_ci#define VIRTMMIO_NETIF_DFT_IP               "10.0.2.15"
85d6aed566Sopenharmony_ci#define VIRTMMIO_NETIF_DFT_GW               "10.0.2.2"
86d6aed566Sopenharmony_ci#define VIRTMMIO_NETIF_DFT_MASK             "255.255.255.0"
87d6aed566Sopenharmony_ci#define VIRTMMIO_NETIF_DFT_RXQSZ            16
88d6aed566Sopenharmony_ci#define VIRTMMIO_NETIF_DFT_TXQSZ            32
89d6aed566Sopenharmony_ci
90d6aed566Sopenharmony_ci/* This struct is actually ignored by this simple driver */
91d6aed566Sopenharmony_cistruct VirtnetHdr {
92d6aed566Sopenharmony_ci    uint8_t flag;
93d6aed566Sopenharmony_ci    uint8_t gsoType;
94d6aed566Sopenharmony_ci    uint16_t hdrLen;
95d6aed566Sopenharmony_ci    uint16_t gsoSize;
96d6aed566Sopenharmony_ci    uint16_t csumStart;
97d6aed566Sopenharmony_ci    uint16_t csumOffset;
98d6aed566Sopenharmony_ci    uint16_t numBuffers;
99d6aed566Sopenharmony_ci};
100d6aed566Sopenharmony_ci
101d6aed566Sopenharmony_ci/*
102d6aed566Sopenharmony_ci * We use two queues for Tx/Rx respectively. When Tx/Rx, no dynamic memory alloc/free:
103d6aed566Sopenharmony_ci * output pbuf directly put into queue and freed by tcpip_thread when used; input has
104d6aed566Sopenharmony_ci * some fixed-size buffers just after the queues and released by application when consumed.
105d6aed566Sopenharmony_ci *
106d6aed566Sopenharmony_ci * Tx/Rx queues memory layout:
107d6aed566Sopenharmony_ci *                         Rx queue                                Tx queue             Rx buffers
108d6aed566Sopenharmony_ci * +-----------------+------------------+------------------++------+-------+------++----------------------+
109d6aed566Sopenharmony_ci * | desc: 16B align | avail: 2B align  | used: 4B align   || desc | avail | used || 4B align             |
110d6aed566Sopenharmony_ci * | 16∗(Queue Size) | 4+2∗(Queue Size) | 4+8∗(Queue Size) ||      |       |      || 1528*(Rx Queue Size) |
111d6aed566Sopenharmony_ci * +-----------------+------------------+------------------++------+-------+------++----------------------+
112d6aed566Sopenharmony_ci */
113d6aed566Sopenharmony_ci#define VIRTQ_NUM_NET       2
114d6aed566Sopenharmony_ci#define VIRTQ_RXBUF_ALIGN   4
115d6aed566Sopenharmony_ci#define VIRTQ_RXBUF_SIZE    ALIGN(sizeof(struct VirtnetHdr) + ETH_FRAME_LEN, VIRTQ_RXBUF_ALIGN)
116d6aed566Sopenharmony_ci
117d6aed566Sopenharmony_cistruct RbufRecord {
118d6aed566Sopenharmony_ci    struct pbuf_custom  cbuf;
119d6aed566Sopenharmony_ci    struct VirtNetif    *nic;
120d6aed566Sopenharmony_ci    uint16_t            id;     /* index to Rx vq[0].desc[] */
121d6aed566Sopenharmony_ci};
122d6aed566Sopenharmony_ci
123d6aed566Sopenharmony_cistruct TbufRecord {
124d6aed566Sopenharmony_ci    struct pbuf         *head;  /* first pbuf address of this pbuf chain */
125d6aed566Sopenharmony_ci    uint16_t            count;  /* occupied desc entries, including VirtnetHdr */
126d6aed566Sopenharmony_ci    uint16_t            tail;   /* tail pbuf's index to Tx vq[1].desc[] */
127d6aed566Sopenharmony_ci};
128d6aed566Sopenharmony_ci
129d6aed566Sopenharmony_cistruct VirtNetif {
130d6aed566Sopenharmony_ci    struct VirtmmioDev  dev;
131d6aed566Sopenharmony_ci
132d6aed566Sopenharmony_ci    struct RbufRecord   *rbufRec;
133d6aed566Sopenharmony_ci    SPIN_LOCK_S         recvLock;
134d6aed566Sopenharmony_ci
135d6aed566Sopenharmony_ci    uint16_t            tFreeHdr;   /* head of Tx free desc entries list */
136d6aed566Sopenharmony_ci    uint16_t            tFreeNum;
137d6aed566Sopenharmony_ci    struct TbufRecord   *tbufRec;
138d6aed566Sopenharmony_ci    SPIN_LOCK_S         transLock;
139d6aed566Sopenharmony_ci
140d6aed566Sopenharmony_ci    struct VirtnetHdr   vnHdr;
141d6aed566Sopenharmony_ci};
142d6aed566Sopenharmony_ci
143d6aed566Sopenharmony_cistatic bool Feature0(uint32_t features, uint32_t *supported, void *dev)
144d6aed566Sopenharmony_ci{
145d6aed566Sopenharmony_ci    struct netif *netif = dev;
146d6aed566Sopenharmony_ci    struct VirtNetif *nic = netif->state;
147d6aed566Sopenharmony_ci    struct VirtnetConfig *conf = (struct VirtnetConfig *)(nic->dev.base + VIRTMMIO_REG_CONFIG);
148d6aed566Sopenharmony_ci    int i;
149d6aed566Sopenharmony_ci
150d6aed566Sopenharmony_ci    if (features & VIRTIO_NET_F_MTU) {
151d6aed566Sopenharmony_ci        if (conf->mtu > ETH_DATA_LEN) {
152d6aed566Sopenharmony_ci            PRINT_ERR("unsupported backend net MTU: %u\n", conf->mtu);
153d6aed566Sopenharmony_ci            return false;
154d6aed566Sopenharmony_ci        }
155d6aed566Sopenharmony_ci        netif->mtu = conf->mtu;
156d6aed566Sopenharmony_ci        *supported |= VIRTIO_NET_F_MTU;
157d6aed566Sopenharmony_ci    } else {
158d6aed566Sopenharmony_ci        netif->mtu = ETH_DATA_LEN;
159d6aed566Sopenharmony_ci    }
160d6aed566Sopenharmony_ci
161d6aed566Sopenharmony_ci    LOS_ASSERT(features & VIRTIO_NET_F_MAC);
162d6aed566Sopenharmony_ci    for (i = 0; i < ETHARP_HWADDR_LEN; i++) {
163d6aed566Sopenharmony_ci        netif->hwaddr[i] = conf->mac[i];
164d6aed566Sopenharmony_ci    }
165d6aed566Sopenharmony_ci    netif->hwaddr_len = ETHARP_HWADDR_LEN;
166d6aed566Sopenharmony_ci    *supported |= VIRTIO_NET_F_MAC;
167d6aed566Sopenharmony_ci
168d6aed566Sopenharmony_ci    return true;
169d6aed566Sopenharmony_ci}
170d6aed566Sopenharmony_ci
171d6aed566Sopenharmony_cistatic bool Feature1(uint32_t features, uint32_t *supported, void *dev)
172d6aed566Sopenharmony_ci{
173d6aed566Sopenharmony_ci    if (features & VIRTIO_F_VERSION_1) {
174d6aed566Sopenharmony_ci        *supported |= VIRTIO_F_VERSION_1;
175d6aed566Sopenharmony_ci    } else {
176d6aed566Sopenharmony_ci        PRINT_ERR("net device has no VERSION_1 feature\n");
177d6aed566Sopenharmony_ci        return false;
178d6aed566Sopenharmony_ci    }
179d6aed566Sopenharmony_ci
180d6aed566Sopenharmony_ci    return true;
181d6aed566Sopenharmony_ci}
182d6aed566Sopenharmony_ci
183d6aed566Sopenharmony_cistatic err_t InitTxFreelist(struct VirtNetif *nic)
184d6aed566Sopenharmony_ci{
185d6aed566Sopenharmony_ci    int i;
186d6aed566Sopenharmony_ci
187d6aed566Sopenharmony_ci    nic->tbufRec = malloc(sizeof(struct TbufRecord) * nic->dev.vq[1].qsz);
188d6aed566Sopenharmony_ci    if (nic->tbufRec == NULL) {
189d6aed566Sopenharmony_ci        PRINT_ERR("alloc nic->tbufRec memory failed\n");
190d6aed566Sopenharmony_ci        return ERR_MEM;
191d6aed566Sopenharmony_ci    }
192d6aed566Sopenharmony_ci
193d6aed566Sopenharmony_ci    for (i = 0; i < nic->dev.vq[1].qsz - 1; i++) {
194d6aed566Sopenharmony_ci        nic->dev.vq[1].desc[i].flag = VIRTQ_DESC_F_NEXT;
195d6aed566Sopenharmony_ci        nic->dev.vq[1].desc[i].next = i + 1;
196d6aed566Sopenharmony_ci    }
197d6aed566Sopenharmony_ci    nic->tFreeHdr = 0;
198d6aed566Sopenharmony_ci    nic->tFreeNum = nic->dev.vq[1].qsz;
199d6aed566Sopenharmony_ci
200d6aed566Sopenharmony_ci    return ERR_OK;
201d6aed566Sopenharmony_ci}
202d6aed566Sopenharmony_ci
203d6aed566Sopenharmony_cistatic void FreeTxEntry(struct VirtNetif *nic, uint16_t head)
204d6aed566Sopenharmony_ci{
205d6aed566Sopenharmony_ci    uint16_t count, idx, tail;
206d6aed566Sopenharmony_ci    struct pbuf *phead = NULL;
207d6aed566Sopenharmony_ci    struct Virtq *q = &nic->dev.vq[1];
208d6aed566Sopenharmony_ci
209d6aed566Sopenharmony_ci    idx = q->desc[head].next;
210d6aed566Sopenharmony_ci    phead = nic->tbufRec[idx].head;
211d6aed566Sopenharmony_ci    count = nic->tbufRec[idx].count;
212d6aed566Sopenharmony_ci    tail = nic->tbufRec[idx].tail;
213d6aed566Sopenharmony_ci
214d6aed566Sopenharmony_ci    LOS_SpinLock(&nic->transLock);
215d6aed566Sopenharmony_ci    if (nic->tFreeNum > 0) {
216d6aed566Sopenharmony_ci        q->desc[tail].next = nic->tFreeHdr;
217d6aed566Sopenharmony_ci        q->desc[tail].flag = VIRTQ_DESC_F_NEXT;
218d6aed566Sopenharmony_ci    }
219d6aed566Sopenharmony_ci    nic->tFreeNum += count;
220d6aed566Sopenharmony_ci    nic->tFreeHdr = head;
221d6aed566Sopenharmony_ci    LOS_SpinUnlock(&nic->transLock);
222d6aed566Sopenharmony_ci
223d6aed566Sopenharmony_ci    pbuf_free_callback(phead);
224d6aed566Sopenharmony_ci}
225d6aed566Sopenharmony_ci
226d6aed566Sopenharmony_cistatic void ReleaseRxEntry(struct pbuf *p)
227d6aed566Sopenharmony_ci{
228d6aed566Sopenharmony_ci    struct RbufRecord *pc = (struct RbufRecord *)p;
229d6aed566Sopenharmony_ci    struct VirtNetif *nic = pc->nic;
230d6aed566Sopenharmony_ci    uint32_t intSave;
231d6aed566Sopenharmony_ci
232d6aed566Sopenharmony_ci    LOS_SpinLockSave(&nic->recvLock, &intSave);
233d6aed566Sopenharmony_ci    nic->dev.vq[0].avail->ring[nic->dev.vq[0].avail->index % nic->dev.vq[0].qsz] = pc->id;
234d6aed566Sopenharmony_ci    DSB;
235d6aed566Sopenharmony_ci    nic->dev.vq[0].avail->index++;
236d6aed566Sopenharmony_ci    LOS_SpinUnlockRestore(&nic->recvLock, intSave);
237d6aed566Sopenharmony_ci
238d6aed566Sopenharmony_ci    if (nic->dev.vq[0].used->flag != VIRTQ_USED_F_NO_NOTIFY) {
239d6aed566Sopenharmony_ci        FENCE_WRITE_UINT32(0, nic->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
240d6aed566Sopenharmony_ci    }
241d6aed566Sopenharmony_ci}
242d6aed566Sopenharmony_ci
243d6aed566Sopenharmony_cistatic err_t ConfigRxBuffer(struct VirtNetif *nic, VADDR_T buf)
244d6aed566Sopenharmony_ci{
245d6aed566Sopenharmony_ci    uint32_t i;
246d6aed566Sopenharmony_ci    PADDR_T paddr;
247d6aed566Sopenharmony_ci    struct Virtq *q = &nic->dev.vq[0];
248d6aed566Sopenharmony_ci
249d6aed566Sopenharmony_ci    nic->rbufRec = calloc(q->qsz, sizeof(struct RbufRecord));
250d6aed566Sopenharmony_ci    if (nic->rbufRec == NULL) {
251d6aed566Sopenharmony_ci        PRINT_ERR("alloc nic->rbufRec memory failed\n");
252d6aed566Sopenharmony_ci        return ERR_MEM;
253d6aed566Sopenharmony_ci    }
254d6aed566Sopenharmony_ci
255d6aed566Sopenharmony_ci    paddr = VMM_TO_DMA_ADDR(buf);
256d6aed566Sopenharmony_ci
257d6aed566Sopenharmony_ci    for (i = 0; i < q->qsz; i++) {
258d6aed566Sopenharmony_ci        q->desc[i].pAddr = u32_to_u64(paddr);
259d6aed566Sopenharmony_ci        q->desc[i].len = sizeof(struct VirtnetHdr) + ETH_FRAME_LEN;
260d6aed566Sopenharmony_ci        q->desc[i].flag = VIRTQ_DESC_F_WRITE;
261d6aed566Sopenharmony_ci        paddr += VIRTQ_RXBUF_SIZE;
262d6aed566Sopenharmony_ci
263d6aed566Sopenharmony_ci        q->avail->ring[i] = i;
264d6aed566Sopenharmony_ci
265d6aed566Sopenharmony_ci        nic->rbufRec[i].cbuf.custom_free_function = ReleaseRxEntry;
266d6aed566Sopenharmony_ci        nic->rbufRec[i].nic = nic;
267d6aed566Sopenharmony_ci        nic->rbufRec[i].id = i;
268d6aed566Sopenharmony_ci    }
269d6aed566Sopenharmony_ci
270d6aed566Sopenharmony_ci    return ERR_OK;
271d6aed566Sopenharmony_ci}
272d6aed566Sopenharmony_ci
273d6aed566Sopenharmony_cistatic err_t ConfigQueue(struct VirtNetif *nic)
274d6aed566Sopenharmony_ci{
275d6aed566Sopenharmony_ci    VADDR_T buf, pad;
276d6aed566Sopenharmony_ci    void *base = NULL;
277d6aed566Sopenharmony_ci    err_t ret;
278d6aed566Sopenharmony_ci    size_t size;
279d6aed566Sopenharmony_ci    uint16_t qsz[VIRTQ_NUM_NET];
280d6aed566Sopenharmony_ci
281d6aed566Sopenharmony_ci    /*
282d6aed566Sopenharmony_ci     * lwip request (packet address - ETH_PAD_SIZE) must align with 4B.
283d6aed566Sopenharmony_ci     * We pad before the first Rx buf to happy it. Rx buf = VirtnetHdr + packet,
284d6aed566Sopenharmony_ci     * then (buf base + pad + VirtnetHdr - ETH_PAD_SIZE) should align with 4B.
285d6aed566Sopenharmony_ci     * When allocating memory, VIRTQ_RXBUF_ALIGN - 1 is enough for padding.
286d6aed566Sopenharmony_ci     */
287d6aed566Sopenharmony_ci    qsz[0] = VIRTMMIO_NETIF_DFT_RXQSZ;
288d6aed566Sopenharmony_ci    qsz[1] = VIRTMMIO_NETIF_DFT_TXQSZ;
289d6aed566Sopenharmony_ci    size = VirtqSize(qsz[0]) + VirtqSize(qsz[1]) + VIRTQ_RXBUF_ALIGN - 1 + qsz[0] * VIRTQ_RXBUF_SIZE;
290d6aed566Sopenharmony_ci
291d6aed566Sopenharmony_ci    base = calloc(1, size);
292d6aed566Sopenharmony_ci    if (base == NULL) {
293d6aed566Sopenharmony_ci        PRINT_ERR("alloc queues memory failed\n");
294d6aed566Sopenharmony_ci        return ERR_MEM;
295d6aed566Sopenharmony_ci    }
296d6aed566Sopenharmony_ci
297d6aed566Sopenharmony_ci    buf = VirtmmioConfigQueue(&nic->dev, (VADDR_T)base, qsz, VIRTQ_NUM_NET);
298d6aed566Sopenharmony_ci    if (buf == 0) {
299d6aed566Sopenharmony_ci        return ERR_IF;
300d6aed566Sopenharmony_ci    }
301d6aed566Sopenharmony_ci
302d6aed566Sopenharmony_ci    pad = (buf + sizeof(struct VirtnetHdr) - ETH_PAD_SIZE) % VIRTQ_RXBUF_ALIGN;
303d6aed566Sopenharmony_ci    if (pad) {
304d6aed566Sopenharmony_ci        pad = VIRTQ_RXBUF_ALIGN - pad;
305d6aed566Sopenharmony_ci    }
306d6aed566Sopenharmony_ci    buf += pad;
307d6aed566Sopenharmony_ci    if ((ret = ConfigRxBuffer(nic, buf)) != ERR_OK) {
308d6aed566Sopenharmony_ci        return ret;
309d6aed566Sopenharmony_ci    }
310d6aed566Sopenharmony_ci
311d6aed566Sopenharmony_ci    if ((ret = InitTxFreelist(nic)) != ERR_OK) {
312d6aed566Sopenharmony_ci        return ret;
313d6aed566Sopenharmony_ci    }
314d6aed566Sopenharmony_ci
315d6aed566Sopenharmony_ci    return ERR_OK;
316d6aed566Sopenharmony_ci}
317d6aed566Sopenharmony_ci
318d6aed566Sopenharmony_cistatic uint16_t GetTxFreeEntry(struct VirtNetif *nic, uint16_t count)
319d6aed566Sopenharmony_ci{
320d6aed566Sopenharmony_ci    uint32_t intSave;
321d6aed566Sopenharmony_ci    uint16_t head, tail, idx;
322d6aed566Sopenharmony_ci
323d6aed566Sopenharmony_ciRETRY:
324d6aed566Sopenharmony_ci    LOS_SpinLockSave(&nic->transLock, &intSave);
325d6aed566Sopenharmony_ci    if (count > nic->tFreeNum) {
326d6aed566Sopenharmony_ci        LOS_SpinUnlockRestore(&nic->transLock, intSave);
327d6aed566Sopenharmony_ci        LOS_TaskYield();
328d6aed566Sopenharmony_ci        goto RETRY;
329d6aed566Sopenharmony_ci    }
330d6aed566Sopenharmony_ci
331d6aed566Sopenharmony_ci    nic->tFreeNum -= count;
332d6aed566Sopenharmony_ci    head = nic->tFreeHdr;
333d6aed566Sopenharmony_ci    idx = head;
334d6aed566Sopenharmony_ci    while (count--) {
335d6aed566Sopenharmony_ci        tail = idx;
336d6aed566Sopenharmony_ci        idx = nic->dev.vq[1].desc[idx].next;
337d6aed566Sopenharmony_ci    }
338d6aed566Sopenharmony_ci    nic->tFreeHdr = idx;   /* may be invalid if empty, but tFreeNum must be valid: 0 */
339d6aed566Sopenharmony_ci    LOS_SpinUnlockRestore(&nic->transLock, intSave);
340d6aed566Sopenharmony_ci    nic->dev.vq[1].desc[tail].flag &= ~VIRTQ_DESC_F_NEXT;
341d6aed566Sopenharmony_ci
342d6aed566Sopenharmony_ci    return head;
343d6aed566Sopenharmony_ci}
344d6aed566Sopenharmony_ci
345d6aed566Sopenharmony_cistatic err_t LowLevelOutput(struct netif *netif, struct pbuf *p)
346d6aed566Sopenharmony_ci{
347d6aed566Sopenharmony_ci    uint16_t add, idx, head, tmp;
348d6aed566Sopenharmony_ci    struct pbuf *q = NULL;
349d6aed566Sopenharmony_ci    struct VirtNetif *nic = netif->state;
350d6aed566Sopenharmony_ci    struct Virtq *trans = &nic->dev.vq[1];
351d6aed566Sopenharmony_ci
352d6aed566Sopenharmony_ci#if ETH_PAD_SIZE
353d6aed566Sopenharmony_ci    pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
354d6aed566Sopenharmony_ci#endif
355d6aed566Sopenharmony_ci
356d6aed566Sopenharmony_ci    /* plus 1 for VirtnetHdr */
357d6aed566Sopenharmony_ci    add = pbuf_clen(p) + 1;
358d6aed566Sopenharmony_ci    if (add > trans->qsz) {
359d6aed566Sopenharmony_ci        PRINT_ERR("packet pbuf_clen %u larger than supported %u\n", add - 1, trans->qsz - 1);
360d6aed566Sopenharmony_ci        return ERR_IF;
361d6aed566Sopenharmony_ci    }
362d6aed566Sopenharmony_ci
363d6aed566Sopenharmony_ci    head = GetTxFreeEntry(nic, add);
364d6aed566Sopenharmony_ci    trans->desc[head].pAddr = u32_to_u64(VMM_TO_DMA_ADDR((PADDR_T)&nic->vnHdr));
365d6aed566Sopenharmony_ci    trans->desc[head].len = sizeof(struct VirtnetHdr);
366d6aed566Sopenharmony_ci    idx = trans->desc[head].next;
367d6aed566Sopenharmony_ci    tmp = head;
368d6aed566Sopenharmony_ci    q = p;
369d6aed566Sopenharmony_ci    while (q != NULL) {
370d6aed566Sopenharmony_ci        tmp = trans->desc[tmp].next;
371d6aed566Sopenharmony_ci        trans->desc[tmp].pAddr = u32_to_u64(VMM_TO_DMA_ADDR((PADDR_T)q->payload));
372d6aed566Sopenharmony_ci        trans->desc[tmp].len = q->len;
373d6aed566Sopenharmony_ci        q = q->next;
374d6aed566Sopenharmony_ci    }
375d6aed566Sopenharmony_ci
376d6aed566Sopenharmony_ci    nic->tbufRec[idx].head = p;
377d6aed566Sopenharmony_ci    nic->tbufRec[idx].count = add;
378d6aed566Sopenharmony_ci    nic->tbufRec[idx].tail = tmp;
379d6aed566Sopenharmony_ci    pbuf_ref(p);
380d6aed566Sopenharmony_ci
381d6aed566Sopenharmony_ci    trans->avail->ring[trans->avail->index % trans->qsz] = head;
382d6aed566Sopenharmony_ci    DSB;
383d6aed566Sopenharmony_ci    trans->avail->index++;
384d6aed566Sopenharmony_ci    FENCE_WRITE_UINT32(1, nic->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
385d6aed566Sopenharmony_ci
386d6aed566Sopenharmony_ci#if ETH_PAD_SIZE
387d6aed566Sopenharmony_ci    pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
388d6aed566Sopenharmony_ci#endif
389d6aed566Sopenharmony_ci
390d6aed566Sopenharmony_ci    return ERR_OK;
391d6aed566Sopenharmony_ci}
392d6aed566Sopenharmony_ci
393d6aed566Sopenharmony_cistatic struct pbuf *LowLevelInput(const struct netif *netif, const struct VirtqUsedElem *e)
394d6aed566Sopenharmony_ci{
395d6aed566Sopenharmony_ci    struct VirtNetif *nic = netif->state;
396d6aed566Sopenharmony_ci    struct pbuf *p = NULL;
397d6aed566Sopenharmony_ci    uint16_t len;
398d6aed566Sopenharmony_ci    VADDR_T payload;
399d6aed566Sopenharmony_ci
400d6aed566Sopenharmony_ci    payload = DMA_TO_VMM_ADDR(nic->dev.vq[0].desc[e->id].pAddr) + sizeof(struct VirtnetHdr);
401d6aed566Sopenharmony_ci#if ETH_PAD_SIZE
402d6aed566Sopenharmony_ci    payload -= ETH_PAD_SIZE;
403d6aed566Sopenharmony_ci#endif
404d6aed566Sopenharmony_ci    pbuf_alloced_custom(PBUF_RAW, ETH_FRAME_LEN, PBUF_ROM | PBUF_ALLOC_FLAG_RX,
405d6aed566Sopenharmony_ci                        &nic->rbufRec[e->id].cbuf, (void *)payload, ETH_FRAME_LEN);
406d6aed566Sopenharmony_ci
407d6aed566Sopenharmony_ci    len = e->len - sizeof(struct VirtnetHdr);
408d6aed566Sopenharmony_ci    LOS_ASSERT(len <= ETH_FRAME_LEN);
409d6aed566Sopenharmony_ci#if ETH_PAD_SIZE
410d6aed566Sopenharmony_ci    len += ETH_PAD_SIZE;
411d6aed566Sopenharmony_ci#endif
412d6aed566Sopenharmony_ci
413d6aed566Sopenharmony_ci    p = &nic->rbufRec[e->id].cbuf.pbuf;
414d6aed566Sopenharmony_ci    p->len = len;
415d6aed566Sopenharmony_ci    p->tot_len = p->len;
416d6aed566Sopenharmony_ci    return p;
417d6aed566Sopenharmony_ci}
418d6aed566Sopenharmony_ci
419d6aed566Sopenharmony_cistatic void VirtnetRxHandle(struct netif *netif)
420d6aed566Sopenharmony_ci{
421d6aed566Sopenharmony_ci    struct VirtNetif *nic = netif->state;
422d6aed566Sopenharmony_ci    struct Virtq *q = &nic->dev.vq[0];
423d6aed566Sopenharmony_ci    struct pbuf *buf = NULL;
424d6aed566Sopenharmony_ci    struct VirtqUsedElem *e = NULL;
425d6aed566Sopenharmony_ci
426d6aed566Sopenharmony_ci    q->avail->flag = VIRTQ_AVAIL_F_NO_INTERRUPT;
427d6aed566Sopenharmony_ci    while (1) {
428d6aed566Sopenharmony_ci        if (q->last == q->used->index) {
429d6aed566Sopenharmony_ci            q->avail->flag = 0;
430d6aed566Sopenharmony_ci            /* recheck if new one come in between empty ring and enable interrupt */
431d6aed566Sopenharmony_ci            DSB;
432d6aed566Sopenharmony_ci            if (q->last == q->used->index) {
433d6aed566Sopenharmony_ci                break;
434d6aed566Sopenharmony_ci            }
435d6aed566Sopenharmony_ci            q->avail->flag = VIRTQ_AVAIL_F_NO_INTERRUPT;
436d6aed566Sopenharmony_ci        }
437d6aed566Sopenharmony_ci
438d6aed566Sopenharmony_ci        DSB;
439d6aed566Sopenharmony_ci        e = &q->used->ring[q->last % q->qsz];
440d6aed566Sopenharmony_ci        buf = LowLevelInput(netif, e);
441d6aed566Sopenharmony_ci        if (netif->input(buf, netif) != ERR_OK) {
442d6aed566Sopenharmony_ci            LWIP_DEBUGF(NETIF_DEBUG, ("IP input error\n"));
443d6aed566Sopenharmony_ci            ReleaseRxEntry(buf);
444d6aed566Sopenharmony_ci        }
445d6aed566Sopenharmony_ci
446d6aed566Sopenharmony_ci        q->last++;
447d6aed566Sopenharmony_ci    }
448d6aed566Sopenharmony_ci}
449d6aed566Sopenharmony_ci
450d6aed566Sopenharmony_cistatic void VirtnetTxHandle(struct VirtNetif *nic)
451d6aed566Sopenharmony_ci{
452d6aed566Sopenharmony_ci    struct Virtq *q = &nic->dev.vq[1];
453d6aed566Sopenharmony_ci    struct VirtqUsedElem *e = NULL;
454d6aed566Sopenharmony_ci
455d6aed566Sopenharmony_ci    /* Bypass recheck as VirtnetRxHandle */
456d6aed566Sopenharmony_ci    q->avail->flag = VIRTQ_AVAIL_F_NO_INTERRUPT;
457d6aed566Sopenharmony_ci    while (q->last != q->used->index) {
458d6aed566Sopenharmony_ci        DSB;
459d6aed566Sopenharmony_ci        e = &q->used->ring[q->last % q->qsz];
460d6aed566Sopenharmony_ci        FreeTxEntry(nic, e->id);
461d6aed566Sopenharmony_ci        q->last++;
462d6aed566Sopenharmony_ci    }
463d6aed566Sopenharmony_ci    q->avail->flag = 0;
464d6aed566Sopenharmony_ci}
465d6aed566Sopenharmony_ci
466d6aed566Sopenharmony_cistatic void VirtnetIRQhandle(void *param)
467d6aed566Sopenharmony_ci{
468d6aed566Sopenharmony_ci    struct netif *netif = (struct netif *)param;
469d6aed566Sopenharmony_ci    struct VirtNetif *nic = netif->state;
470d6aed566Sopenharmony_ci
471d6aed566Sopenharmony_ci    if (!(GET_UINT32(nic->dev.base + VIRTMMIO_REG_INTERRUPTSTATUS) & VIRTMMIO_IRQ_NOTIFY_USED)) {
472d6aed566Sopenharmony_ci        return;
473d6aed566Sopenharmony_ci    }
474d6aed566Sopenharmony_ci
475d6aed566Sopenharmony_ci    VirtnetRxHandle(netif);
476d6aed566Sopenharmony_ci
477d6aed566Sopenharmony_ci    VirtnetTxHandle(nic);
478d6aed566Sopenharmony_ci
479d6aed566Sopenharmony_ci    FENCE_WRITE_UINT32(VIRTMMIO_IRQ_NOTIFY_USED, nic->dev.base + VIRTMMIO_REG_INTERRUPTACK);
480d6aed566Sopenharmony_ci}
481d6aed566Sopenharmony_ci
482d6aed566Sopenharmony_cistatic err_t LowLevelInit(struct netif *netif)
483d6aed566Sopenharmony_ci{
484d6aed566Sopenharmony_ci    struct VirtNetif *nic = netif->state;
485d6aed566Sopenharmony_ci    int ret;
486d6aed566Sopenharmony_ci
487d6aed566Sopenharmony_ci    if (!VirtmmioDiscover(VIRTMMIO_DEVICE_ID_NET, &nic->dev)) {
488d6aed566Sopenharmony_ci        return ERR_IF;
489d6aed566Sopenharmony_ci    }
490d6aed566Sopenharmony_ci
491d6aed566Sopenharmony_ci    VirtmmioInitBegin(&nic->dev);
492d6aed566Sopenharmony_ci
493d6aed566Sopenharmony_ci    if (!VirtmmioNegotiate(&nic->dev, Feature0, Feature1, netif)) {
494d6aed566Sopenharmony_ci        ret = ERR_IF;
495d6aed566Sopenharmony_ci        goto ERR_OUT;
496d6aed566Sopenharmony_ci    }
497d6aed566Sopenharmony_ci
498d6aed566Sopenharmony_ci    if ((ret = ConfigQueue(nic)) != ERR_OK) {
499d6aed566Sopenharmony_ci        goto ERR_OUT;
500d6aed566Sopenharmony_ci    }
501d6aed566Sopenharmony_ci
502d6aed566Sopenharmony_ci    if (!VirtmmioRegisterIRQ(&nic->dev, (HWI_PROC_FUNC)VirtnetIRQhandle, netif, VIRTMMIO_NETIF_NAME)) {
503d6aed566Sopenharmony_ci        ret = ERR_IF;
504d6aed566Sopenharmony_ci        goto ERR_OUT;
505d6aed566Sopenharmony_ci    }
506d6aed566Sopenharmony_ci
507d6aed566Sopenharmony_ci    VritmmioInitEnd(&nic->dev);
508d6aed566Sopenharmony_ci
509d6aed566Sopenharmony_ci    /* everything is ready, now notify device the receive buffer */
510d6aed566Sopenharmony_ci    nic->dev.vq[0].avail->index += nic->dev.vq[0].qsz;
511d6aed566Sopenharmony_ci    FENCE_WRITE_UINT32(0, nic->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
512d6aed566Sopenharmony_ci    return ERR_OK;
513d6aed566Sopenharmony_ci
514d6aed566Sopenharmony_ciERR_OUT:
515d6aed566Sopenharmony_ci    VirtmmioInitFailed(&nic->dev);
516d6aed566Sopenharmony_ci    return ret;
517d6aed566Sopenharmony_ci}
518d6aed566Sopenharmony_ci
519d6aed566Sopenharmony_cistatic err_t EthernetIfInit(struct netif *netif)
520d6aed566Sopenharmony_ci{
521d6aed566Sopenharmony_ci    struct VirtNetif *nic = NULL;
522d6aed566Sopenharmony_ci
523d6aed566Sopenharmony_ci    LWIP_ASSERT("netif != NULL", (netif != NULL));
524d6aed566Sopenharmony_ci
525d6aed566Sopenharmony_ci    nic = mem_calloc(1, sizeof(struct VirtNetif));
526d6aed566Sopenharmony_ci    if (nic == NULL) {
527d6aed566Sopenharmony_ci        PRINT_ERR("alloc nic memory failed\n");
528d6aed566Sopenharmony_ci        return ERR_MEM;
529d6aed566Sopenharmony_ci    }
530d6aed566Sopenharmony_ci    netif->state = nic;
531d6aed566Sopenharmony_ci
532d6aed566Sopenharmony_ci#if LWIP_NETIF_HOSTNAME
533d6aed566Sopenharmony_ci    netif->hostname = VIRTMMIO_NETIF_NAME;
534d6aed566Sopenharmony_ci#endif
535d6aed566Sopenharmony_ci
536d6aed566Sopenharmony_ci    strncpy_s(netif->name, sizeof(netif->name), VIRTMMIO_NETIF_NICK, sizeof(netif->name));
537d6aed566Sopenharmony_ci    strncpy_s(netif->full_name, sizeof(netif->full_name), VIRTMMIO_NETIF_NICK, sizeof(netif->full_name));
538d6aed566Sopenharmony_ci
539d6aed566Sopenharmony_ci    netif->output = etharp_output;
540d6aed566Sopenharmony_ci    netif->linkoutput = LowLevelOutput;
541d6aed566Sopenharmony_ci
542d6aed566Sopenharmony_ci    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
543d6aed566Sopenharmony_ci
544d6aed566Sopenharmony_ci    return LowLevelInit(netif);
545d6aed566Sopenharmony_ci}
546d6aed566Sopenharmony_ci
547d6aed566Sopenharmony_cistatic void VirtnetDeInit(struct netif *netif)
548d6aed566Sopenharmony_ci{
549d6aed566Sopenharmony_ci    struct VirtNetif *nic = netif->state;
550d6aed566Sopenharmony_ci
551d6aed566Sopenharmony_ci    if (nic && (nic->dev.irq & ~_IRQ_MASK)) {
552d6aed566Sopenharmony_ci        LOS_HwiDelete(nic->dev.irq, NULL);
553d6aed566Sopenharmony_ci    }
554d6aed566Sopenharmony_ci    if (nic && nic->rbufRec) {
555d6aed566Sopenharmony_ci        free(nic->rbufRec);
556d6aed566Sopenharmony_ci    }
557d6aed566Sopenharmony_ci    if (nic && nic->tbufRec) {
558d6aed566Sopenharmony_ci        free(nic->tbufRec);
559d6aed566Sopenharmony_ci    }
560d6aed566Sopenharmony_ci    if (nic && nic->dev.vq[0].desc) {
561d6aed566Sopenharmony_ci        free(nic->dev.vq[0].desc);
562d6aed566Sopenharmony_ci    }
563d6aed566Sopenharmony_ci    if (nic) {
564d6aed566Sopenharmony_ci        mem_free(nic);
565d6aed566Sopenharmony_ci    }
566d6aed566Sopenharmony_ci    mem_free(netif);
567d6aed566Sopenharmony_ci}
568d6aed566Sopenharmony_ci
569d6aed566Sopenharmony_cistruct netif *VirtnetInit(void)
570d6aed566Sopenharmony_ci{
571d6aed566Sopenharmony_ci    ip4_addr_t ip, mask, gw;
572d6aed566Sopenharmony_ci    struct netif *netif = NULL;
573d6aed566Sopenharmony_ci
574d6aed566Sopenharmony_ci    netif = mem_calloc(1, sizeof(struct netif));
575d6aed566Sopenharmony_ci    if (netif == NULL) {
576d6aed566Sopenharmony_ci        PRINT_ERR("alloc netif memory failed\n");
577d6aed566Sopenharmony_ci        return NULL;
578d6aed566Sopenharmony_ci    }
579d6aed566Sopenharmony_ci
580d6aed566Sopenharmony_ci    ip.addr = ipaddr_addr(VIRTMMIO_NETIF_DFT_IP);
581d6aed566Sopenharmony_ci    mask.addr = ipaddr_addr(VIRTMMIO_NETIF_DFT_MASK);
582d6aed566Sopenharmony_ci    gw.addr = ipaddr_addr(VIRTMMIO_NETIF_DFT_GW);
583d6aed566Sopenharmony_ci    if (netif_add(netif, &ip, &mask, &gw, netif->state,
584d6aed566Sopenharmony_ci                    EthernetIfInit, tcpip_input) == NULL) {
585d6aed566Sopenharmony_ci        PRINT_ERR("add virtio-mmio net device failed\n");
586d6aed566Sopenharmony_ci        VirtnetDeInit(netif);
587d6aed566Sopenharmony_ci        return NULL;
588d6aed566Sopenharmony_ci    }
589d6aed566Sopenharmony_ci
590d6aed566Sopenharmony_ci    return netif;
591d6aed566Sopenharmony_ci}
592d6aed566Sopenharmony_ci
593