1/*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <autoconf.h>
8#include <stdbool.h>
9
10#include <camkes.h>
11#include <camkes/dma.h>
12#include <camkes/io.h>
13#include <camkes/irq.h>
14
15#include <platsupport/io.h>
16#include <platsupport/irq.h>
17#include <ethdrivers/raw.h>
18#include <sel4utils/sel4_zf_logif.h>
19
20#undef PACKED
21#include <pico_stack.h>
22#include <pico_socket.h>
23#include <pico_addressing.h>
24#include <pico_ipv4.h>
25#include <pico_dhcp_client.h>
26#include <pico_device.h>
27
28#include "tuning_params.h"
29
30
31struct eth_driver *eth_driver;
32
33struct pico_device pico_dev;
34
35/*
36 *Struct eth_buf contains a virtual address (buf) to use for memory operations
37 *at the picoserver level and a physical address to be passed down to the
38 *driver.
39 */
40typedef struct eth_buf {
41    char *buf;
42    uintptr_t phys;
43    size_t len;
44} eth_buf_t;
45
46eth_buf_t rx_bufs[RX_BUFS];
47eth_buf_t tx_bufs[TX_BUFS];
48
49/* keeps track of the head of the queue */
50int pending_rx_head;
51/* keeps track of the tail of the queue */
52int pending_rx_tail;
53
54/*
55 * this is a cyclic queue of RX buffers pending to be read by a client,
56 * the head represents the first buffer in the queue, and the tail the last
57 */
58eth_buf_t *pending_rx[RX_BUFS];
59
60/* keeps track of how many TX buffers are in use */
61int num_tx;
62/*
63 * this represents the pool of buffers that can be used for TX,
64 * this array is a sliding array in that num_tx acts a pointer to
65 * separate between buffers that are in use and buffers that are
66 * not in use. E.g. 'o' = free, 'x' = in use
67 *  -------------------------------------
68 *  | o | o | o | o | o | o | x | x | x |
69 *  -------------------------------------
70 *                          ^
71 *                        num_tx
72 */
73eth_buf_t *tx_buf_pool[TX_BUFS];
74
75
76static int num_rx_bufs;
77static eth_buf_t *rx_buf_pool[RX_BUFS];
78
79static void eth_tx_complete(void *iface, void *cookie)
80{
81    eth_buf_t *buf = (eth_buf_t *)cookie;
82    tx_buf_pool[num_tx] = buf;
83    num_tx++;
84}
85
86static uintptr_t eth_allocate_rx_buf(void *iface, size_t buf_size, void **cookie)
87{
88    if (buf_size > BUF_SIZE) {
89        return 0;
90    }
91    if (num_rx_bufs == 0) {
92        return 0;
93    }
94    num_rx_bufs--;
95    *cookie = rx_buf_pool[num_rx_bufs];
96    return rx_buf_pool[num_rx_bufs]->phys;
97}
98
99
100static void eth_rx_complete(void *iface, unsigned int num_bufs, void **cookies, unsigned int *lens)
101{
102    /* insert filtering here. currently everything just goes to one client */
103    if (num_bufs != 1) {
104        goto error;
105    }
106    eth_buf_t *curr_buf = cookies[0];
107    if (((pending_rx_head + 1) % RX_BUFS) == pending_rx_tail) {
108        goto error;
109    }
110    curr_buf->len = lens[0];
111    pending_rx[pending_rx_head] = curr_buf;
112    pending_rx_head = (pending_rx_head + 1) % RX_BUFS;
113    return;
114error:
115    /* abort and put all the bufs back */
116    for (int i = 0; i < num_bufs; i++) {
117        eth_buf_t *returned_buf = cookies[i];
118        rx_buf_pool[num_rx_bufs] = returned_buf;
119        num_rx_bufs++;
120    }
121}
122
123static struct raw_iface_callbacks ethdriver_callbacks = {
124    .tx_complete = eth_tx_complete,
125    .rx_complete = eth_rx_complete,
126    .allocate_rx_buf = eth_allocate_rx_buf
127};
128
129
130/* Async driver will set a flag to signal that there is work to be done  */
131static int pico_eth_poll(struct pico_device *dev, int loop_score)
132{
133    while (loop_score > 0) {
134        int err;
135        if (pending_rx_head == pending_rx_tail) {
136            break;
137        }
138        eth_buf_t *rx = pending_rx[pending_rx_tail];
139        err = pico_stack_recv(dev, (void *)rx->buf, rx->len);
140        if (err <= 0) {
141            break;
142        } else {
143            pending_rx_tail = (pending_rx_tail + 1) % RX_BUFS;
144            rx_buf_pool[num_rx_bufs] = rx;
145            num_rx_bufs++;
146        }
147        loop_score--;
148    }
149    if (loop_score == 0) {
150        ZF_LOGE("rx loop score died\n");
151    }
152
153    return loop_score;
154}
155
156static int pico_eth_send(struct pico_device *dev, void *input_buf, int len)
157{
158    assert(len >= 12);
159    if (len > BUF_SIZE || len < 0) {
160        ZF_LOGF("Len invalid\n");
161    }
162
163    if (num_tx == 0) {
164        // No packets available
165        return 0; // Error for PICO
166    }
167
168    num_tx --;
169    eth_buf_t *tx_buf = tx_buf_pool[num_tx];
170
171    /* copy the packet over */
172    memcpy(tx_buf->buf, input_buf, len);
173
174    /* queue up transmit */
175    int err = eth_driver->i_fn.raw_tx(eth_driver, 1, (uintptr_t *) & (tx_buf->phys),
176                                      (unsigned int *)&len, tx_buf);
177    switch (err) {
178    case ETHIF_TX_FAILED:
179        tx_buf_pool[num_tx] = tx_buf;
180        num_tx++;
181        return 0; // Error for PICO
182    case ETHIF_TX_COMPLETE:
183    case ETHIF_TX_ENQUEUED:
184        break;
185    }
186    return len;
187}
188
189
190
191
192static void tick_on_event(UNUSED seL4_Word badge, void *cookie)
193{
194    pico_stack_tick();
195}
196
197static int hardware_interface_searcher(void *cookie, void *interface_instance, char **properties)
198{
199
200    eth_driver = interface_instance;
201    return PS_INTERFACE_FOUND_MATCH;
202}
203
204
205int setup_eth0(ps_io_ops_t *io_ops)
206{
207    int error = ps_interface_find(&io_ops->interface_registration_ops,
208                                  PS_ETHERNET_INTERFACE, hardware_interface_searcher, NULL);
209    if (error) {
210        ZF_LOGE("Unable to find an ethernet device");
211        return -1;
212    }
213
214    /* preallocate buffers */
215    for (int i = 0; i < RX_BUFS; i++) {
216        eth_buf_t *buf = &rx_bufs[i];
217        buf->buf = ps_dma_alloc(&io_ops->dma_manager, BUF_SIZE, 64, 1, PS_MEM_NORMAL);
218        if (!buf) {
219            ZF_LOGE("Failed to allocate RX buffer.");
220            return -1;
221        }
222        memset(buf->buf, 0, BUF_SIZE);
223        buf->phys = ps_dma_pin(&io_ops->dma_manager, buf->buf, BUF_SIZE);
224        if (!buf->phys) {
225            ZF_LOGE("ps_dma_pin: Failed to return physical address.");
226            return -1;
227        }
228        rx_buf_pool[num_rx_bufs] = buf;
229        num_rx_bufs++;
230    }
231
232    for (int i = 0; i < TX_BUFS; i++) {
233        eth_buf_t *buf = &tx_bufs[i];
234        buf->buf = ps_dma_alloc(&io_ops->dma_manager, BUF_SIZE, 64, 1, PS_MEM_NORMAL);
235        if (!buf) {
236            ZF_LOGE("Failed to allocate TX buffer: %d.", i);
237            return -1;
238        }
239        memset(buf->buf, 0, BUF_SIZE);
240        buf->phys = ps_dma_pin(&io_ops->dma_manager, buf->buf, BUF_SIZE);
241        if (!buf->phys) {
242            ZF_LOGE("ps_dma_pin: Failed to return physical address.");
243            return -1;
244        }
245        tx_buf_pool[num_tx] = buf;
246        num_tx++;
247    }
248
249    /* Setup ethdriver callbacks and poll the driver so it can do any more init. */
250    eth_driver->cb_cookie = NULL;
251    eth_driver->i_cb = ethdriver_callbacks;
252    eth_driver->i_fn.raw_poll(eth_driver);
253
254    /* Setup pico device to install into picotcp */
255    pico_dev.send = pico_eth_send;
256    pico_dev.poll = pico_eth_poll;
257    uint8_t mac[6] = {0};
258    eth_driver->i_fn.get_mac(eth_driver, mac);
259    pico_dev.mtu = 1500;
260    if (pico_device_init(&pico_dev, "eth0", mac) != 0) {
261        ZF_LOGF("Failed to initialize pico device");
262    }
263    /* Set a maximum number of frames in the device queue to avoid memory leaks. */
264    pico_dev.q_in->max_frames = 512;
265    pico_dev.q_out->max_frames = 512;
266
267    single_threaded_component_register_handler(0, "pico_stack_tick", tick_on_event, NULL);
268
269    return 0;
270}
271
272CAMKES_POST_INIT_MODULE_DEFINE(install_eth_device, setup_eth0);
273