1/*
2 * Copyright 2020, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
8 * See "LICENSE_GPLv2.txt" for details.
9 *
10 * @TAG(DATA61_GPL)
11 */
12
13#include <autoconf.h>
14#include <stdbool.h>
15
16#include <camkes/dma.h>
17#include <camkes/dataport.h>
18#include <camkes/io.h>
19#include <camkes/irq.h>
20#include <camkes/virtqueue.h>
21
22#include <platsupport/io.h>
23#include <platsupport/irq.h>
24#include <platsupport/interface_registration.h>
25#include <ethdrivers/raw.h>
26#include <ethdrivers/intel.h>
27#include <sel4utils/sel4_zf_logif.h>
28#include <virtqueue.h>
29#include <picotcp-ethernet-async.h>
30
31typedef struct data {
32    ps_io_ops_t *io_ops;
33    virtqueue_device_t tx_virtqueue;
34    virtqueue_device_t rx_virtqueue;
35    bool action;
36    bool blocked_tx;
37    bool no_rx_bufs;
38    uint8_t hw_mac[6];
39    struct eth_driver *eth_driver;
40} server_data_t;
41
42#define BUF_SIZE 2048
43
44static void eth_tx_complete(void *iface, void *cookie)
45{
46    server_data_t *state = iface;
47
48    virtqueue_ring_object_t handle;
49    handle.first = (uint32_t)(uintptr_t)cookie;
50    handle.cur = (uint32_t)(uintptr_t)cookie;
51    if (!virtqueue_add_used_buf(&state->tx_virtqueue, &handle, BUF_SIZE)) {
52        ZF_LOGF("eth_tx_complete: Error while enqueuing used buffer, queue full");
53    }
54    if (state->blocked_tx) {
55        state->action = true;
56    }
57}
58
59static uintptr_t eth_allocate_rx_buf(void *iface, size_t buf_size, void **cookie)
60{
61    if (buf_size > BUF_SIZE) {
62        return 0;
63    }
64    server_data_t *state = iface;
65
66    virtqueue_ring_object_t handle;
67
68    if (virtqueue_get_available_buf(&state->rx_virtqueue, &handle) == 0) {
69        // No buffer available to fill RX ring with.
70        state->no_rx_bufs = true;
71        return 0;
72    }
73    state->no_rx_bufs = false;
74    void *buf;
75    unsigned len;
76    vq_flags_t flag;
77    int more = virtqueue_gather_available(&state->rx_virtqueue, &handle, &buf, &len, &flag);
78    if (more == 0) {
79        ZF_LOGF("eth_allocate_rx_buf: Invalid virtqueue ring entry");
80    }
81
82    uintptr_t phys = ps_dma_pin(&state->io_ops->dma_manager, DECODE_DMA_ADDRESS(buf), BUF_SIZE);
83    *cookie = (void *)(uintptr_t) handle.first;
84    return phys;
85}
86
87static void eth_rx_complete(void *iface, unsigned int num_bufs, void **cookies, unsigned int *lens)
88{
89    server_data_t *state = iface;
90    if (num_bufs != 1) {
91        ZF_LOGE("Dropping packets because num_received didn't match descriptor");
92        for (int i = 0; i < num_bufs; i++) {
93            virtqueue_ring_object_t handle;
94            handle.first = (uintptr_t)cookies[i];
95            handle.cur = (uintptr_t)cookies[i];
96            if (!virtqueue_add_used_buf(&state->rx_virtqueue, &handle, 0)) {
97                ZF_LOGF("eth_rx_complete: Error while enqueuing used buffer, queue full");
98            }
99        }
100        state->action = true;
101        return;
102
103    }
104    virtqueue_ring_object_t handle;
105    handle.first = (uintptr_t)cookies[0];
106    handle.cur = (uintptr_t)cookies[0];
107    if (!virtqueue_add_used_buf(&state->rx_virtqueue, &handle, lens[0])) {
108        ZF_LOGF("eth_rx_complete: Error while enqueuing used buffer, queue full");
109    }
110    state->action = true;
111    return;
112}
113
114static struct raw_iface_callbacks ethdriver_callbacks = {
115    .tx_complete = eth_tx_complete,
116    .rx_complete = eth_rx_complete,
117    .allocate_rx_buf = eth_allocate_rx_buf
118};
119
120
121
122static void client_get_mac(uint8_t *b1, uint8_t *b2, uint8_t *b3, uint8_t *b4, uint8_t *b5, uint8_t *b6, void *cookie)
123{
124    server_data_t *state = cookie;
125    *b1 = state->hw_mac[0];
126    *b2 = state->hw_mac[1];
127    *b3 = state->hw_mac[2];
128    *b4 = state->hw_mac[3];
129    *b5 = state->hw_mac[4];
130    *b6 = state->hw_mac[5];
131}
132
133static void virt_queue_handle_irq(seL4_Word badge, void *cookie)
134{
135    server_data_t *state = cookie;
136    if (state->no_rx_bufs) {
137        state->eth_driver->i_fn.raw_poll(state->eth_driver);
138    }
139    while (1) {
140
141        virtqueue_ring_object_t handle;
142
143        unsigned next = (state->tx_virtqueue.a_ring_last_seen + 1) & (state->tx_virtqueue.queue_len - 1);
144
145        if (next == state->tx_virtqueue.avail_ring->idx) {
146            break;
147        }
148        handle.first = state->tx_virtqueue.avail_ring->ring[next];
149        handle.cur = handle.first;
150
151        void *buf;
152        unsigned len;
153        vq_flags_t flag;
154        int more = virtqueue_gather_available(&state->tx_virtqueue, &handle, &buf, &len, &flag);
155        if (more == 0) {
156            ZF_LOGF("tx_queue_handle_irq: Invalid virtqueue ring entry");
157        }
158
159        uintptr_t phys = ps_dma_pin(&state->io_ops->dma_manager, DECODE_DMA_ADDRESS(buf), BUF_SIZE);
160        int err = state->eth_driver->i_fn.raw_tx(state->eth_driver, 1, (uintptr_t *) &phys, (unsigned int *)&len,
161                                                 (void *)(uintptr_t)handle.first);
162        if (err != ETHIF_TX_ENQUEUED) {
163            state->blocked_tx = true;
164            break;
165        } else {
166            state->blocked_tx = false;
167            virtqueue_get_available_buf(&state->tx_virtqueue, &handle);
168        }
169    }
170
171}
172
173
174static void notify_client(UNUSED seL4_Word badge, void *cookie)
175{
176    server_data_t *state = cookie;
177    if (state->action) {
178        if (state->blocked_tx) {
179            virt_queue_handle_irq(badge, cookie);
180        }
181        state->action = false;
182        state->tx_virtqueue.notify();
183    }
184}
185
186
187static int hardware_interface_searcher(void *cookie, void *interface_instance, char **properties)
188{
189
190    server_data_t *state = cookie;
191    state->eth_driver = interface_instance;
192    return PS_INTERFACE_FOUND_MATCH;
193}
194
195int picotcp_ethernet_async_server_init(ps_io_ops_t *io_ops, const char *tx_virtqueue, const char *rx_virtqueue,
196                                       register_callback_handler_fn_t register_handler, register_get_mac_server_fn register_get_mac_fn)
197{
198
199    server_data_t *data;
200    int error = ps_calloc(&io_ops->malloc_ops, 1, sizeof(*data), (void **)&data);
201    data->io_ops = io_ops;
202
203
204    error = ps_interface_find(&io_ops->interface_registration_ops,
205                              PS_ETHERNET_INTERFACE, hardware_interface_searcher, data);
206    if (error) {
207        ZF_LOGF("Unable to find an ethernet device");
208    }
209
210    data->eth_driver->cb_cookie = data;
211    data->eth_driver->i_cb = ethdriver_callbacks;
212
213    seL4_Word tx_badge;
214    seL4_Word rx_badge;
215
216    /* Initialise read virtqueue */
217    error = camkes_virtqueue_device_init_with_recv(&data->tx_virtqueue, camkes_virtqueue_get_id_from_name(tx_virtqueue),
218                                                   NULL, &tx_badge);
219    if (error) {
220        ZF_LOGE("Unable to initialise serial server read virtqueue");
221    }
222    /* Initialise write virtqueue */
223    error = camkes_virtqueue_device_init_with_recv(&data->rx_virtqueue, camkes_virtqueue_get_id_from_name(rx_virtqueue),
224                                                   NULL, &rx_badge);
225    if (error) {
226        ZF_LOGE("Unable to initialise serial server write virtqueue");
227    }
228
229    error = register_handler(tx_badge, "tx_event", virt_queue_handle_irq, data);
230    if (error) {
231        ZF_LOGE("Unable to register handler");
232    }
233    error = register_handler(rx_badge, "rx_event", virt_queue_handle_irq, data);
234    if (error) {
235        ZF_LOGE("Unable to register handler");
236    }
237
238    error = register_handler(0, "notify_client", notify_client, data);
239    if (error) {
240        ZF_LOGE("Unable to register handler");
241    }
242
243
244    data->eth_driver->i_fn.get_mac(data->eth_driver, data->hw_mac);
245    data->eth_driver->i_fn.raw_poll(data->eth_driver);
246
247    register_get_mac_fn(client_get_mac, data);
248    return 0;
249}
250