1/*
2 *  Copyright 2017, 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 BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <autoconf.h>
14#include <ethdrivers/gen_config.h>
15#include <picotcp/gen_config.h>
16
17#ifdef CONFIG_LIB_PICOTCP
18
19#include <ethdrivers/pico_dev_eth.h>
20#include <ethdrivers/helpers.h>
21#include <string.h>
22#include <inttypes.h>
23#include "debug.h"
24#include <utils/zf_log.h>
25
26static int alloc_buf_pool(pico_device_eth *pico_iface)
27{
28    /* Take the next free buffer */
29    if (pico_iface->next_free_buf == -1) {
30        ZF_LOGE("Out of preallocated eth buffers.");
31        return -1;
32    }
33
34    int retval = pico_iface->next_free_buf;
35    pico_iface->next_free_buf = pico_iface->buf_pool[retval];
36    return retval;
37
38}
39
40static void free_buf_pool(pico_device_eth *pico_iface, int buf_no)
41{
42    /* Return back into the buffer pool */
43    if (buf_no < 0 || buf_no > CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS) {
44        ZF_LOGE("Attempted to return a buffer outside of the pool %d.", buf_no);
45        return;
46    }
47    pico_iface->buf_pool[buf_no] = pico_iface->next_free_buf;
48    pico_iface->next_free_buf = buf_no;
49}
50
51static void destroy_free_bufs(pico_device_eth *pico_iface)
52{
53    if (pico_iface->bufs) {
54        free(pico_iface->bufs);
55    }
56    if (pico_iface->dma_bufs) {
57        for (int i = 0; i < CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS; i++) {
58            if (pico_iface->dma_bufs[i].virt) {
59                dma_unpin_free(&pico_iface->dma_man, pico_iface->dma_bufs[i].virt,
60                               CONFIG_LIB_ETHDRIVER_PREALLOCATED_BUF_SIZE);
61            }
62        }
63        free(pico_iface->dma_bufs);
64        pico_iface->dma_bufs = NULL;
65    }
66
67    if (pico_iface->buf_pool) {
68        free(pico_iface->buf_pool);
69    }
70    pico_iface->next_free_buf = -1;
71
72    if (pico_iface->rx_lens) {
73        free(pico_iface->rx_lens);
74    }
75
76    pico_iface->bufs = NULL;
77}
78
79static void initialize_free_bufs(pico_device_eth *pico_iface)
80{
81    dma_addr_t *dma_bufs = NULL;
82    dma_bufs = calloc(CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS, sizeof(dma_addr_t));
83    pico_iface->dma_bufs = dma_bufs;
84    if (!dma_bufs) {
85        destroy_free_bufs(pico_iface);
86        return;
87    }
88
89    pico_iface->bufs = malloc(sizeof(dma_addr_t *) * CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS);
90    if (!pico_iface->bufs) {
91        destroy_free_bufs(pico_iface);
92        return;
93    }
94
95    /* Pin buffers */
96    for (int i = 0; i < CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS; i++) {
97        dma_bufs[i] = dma_alloc_pin(&pico_iface->dma_man,
98                                    CONFIG_LIB_ETHDRIVER_PREALLOCATED_BUF_SIZE, 1,  pico_iface->driver.dma_alignment);
99        if (!dma_bufs[i].phys) {
100            destroy_free_bufs(pico_iface);
101            return;
102        }
103        ps_dma_cache_clean_invalidate(&pico_iface->dma_man, dma_bufs[i].virt,
104                                      CONFIG_LIB_ETHDRIVER_PREALLOCATED_BUF_SIZE);
105        pico_iface->bufs[i] = &dma_bufs[i];
106    }
107
108    pico_iface->num_free_bufs = CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS;
109
110    /* Setup the buffer pool management */
111    pico_iface->buf_pool = malloc(sizeof(int) * CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS);
112    if (!pico_iface->buf_pool) {
113        destroy_free_bufs(pico_iface);
114        return;
115    }
116
117    for (int i = 0; i < CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS; i++) {
118        pico_iface->buf_pool[i] = i - 1;
119    }
120    pico_iface->next_free_buf = CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS - 1;
121
122    /* Rx queue */
123    pico_iface->rx_count = 0;
124    pico_iface->rx_lens = calloc(CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS, sizeof(int));
125    if (!pico_iface->rx_lens) {
126        destroy_free_bufs(pico_iface);
127        return;
128    }
129
130    pico_iface->rx_queue = calloc(CONFIG_LIB_ETHDRIVER_NUM_PREALLOCATED_BUFFERS, sizeof(int));
131    if (!pico_iface->rx_queue) {
132        destroy_free_bufs(pico_iface);
133        return;
134    }
135
136    return;
137
138}
139
140static uintptr_t pico_allocate_rx_buf(void *iface, size_t buf_size, void **cookie)
141{
142    pico_device_eth *pico_iface = (pico_device_eth *)iface;
143
144    if (buf_size > CONFIG_LIB_ETHDRIVER_PREALLOCATED_BUF_SIZE) {
145        ZF_LOGE("Requested RX buffer of size %zu which can never be fullfilled by preallocated buffers of size %d",
146                buf_size, CONFIG_LIB_ETHDRIVER_PREALLOCATED_BUF_SIZE);
147        return 0;
148    }
149
150    if (!pico_iface->bufs) {
151        initialize_free_bufs(pico_iface);
152        if (!pico_iface->bufs) {
153            ZF_LOGE("Failed lazy initialization of preallocated free buffers");
154            return 0;
155        }
156    }
157
158    int buf_no = alloc_buf_pool(pico_iface);
159    if (buf_no < 0) {
160        /* No buffers available */
161        return 0;
162    }
163
164    dma_addr_t *buf = pico_iface->bufs[buf_no];
165    ps_dma_cache_invalidate(&pico_iface->dma_man, buf->virt, buf_size);
166    *(long *) cookie = buf_no;
167    return buf->phys;
168}
169
170static void pico_tx_complete(void *iface, void *cookie)
171{
172    free_buf_pool(iface, (long) cookie);
173}
174
175static void pico_rx_complete(void *iface, unsigned int num_bufs, void **cookies, unsigned int *lens)
176{
177    /* A buffer has been filled. Put it into the receive queue to be collected. */
178    pico_device_eth *pico_iface = (pico_device_eth *)iface;
179
180    if (num_bufs > 1) {
181        ZF_LOGE("RX buffer of size is smaller than MTU. Frame splitting unhandled.\n");
182        /* Frame splitting is not handled. Warn and return bufs to pool. */
183        for (int i = 0; i < num_bufs; i++) {
184            free_buf_pool(pico_iface, (long) cookies[i]);
185        }
186    } else {
187        int buf_no = (long) cookies[0];
188        /* Store the information about the rx bufs */
189        pico_iface->rx_queue[pico_iface->rx_count] = buf_no;
190        pico_iface->rx_lens[buf_no] = lens[0];
191        pico_iface->rx_count += 1;
192
193#ifdef CONFIG_LIB_PICOTCP_ASYNC_DRIVER
194        pico_iface->pico_dev.__serving_interrupt = 1;
195#endif
196    }
197    ZF_LOGD("RX complete, %d in queue!\n", ((pico_device_eth *)iface)->rx_count);
198
199    return;
200}
201
202/* Pico TCP implementation */
203
204static int pico_eth_send(struct pico_device *dev, void *input_buf, int len)
205{
206
207    dma_addr_t buf;
208    int status;
209    struct pico_device_eth *eth_device = (struct pico_device_eth *)dev;
210
211    if (len > CONFIG_LIB_ETHDRIVER_PREALLOCATED_BUF_SIZE) {
212        return 0;
213    }
214
215    long buf_no = alloc_buf_pool(eth_device);
216    if (buf_no < 0) {
217        return 0;
218    }
219
220    dma_addr_t *orig_buf = eth_device->bufs[buf_no];
221    buf = *orig_buf;
222    memcpy(buf.virt, input_buf, len);
223    ps_dma_cache_clean(&eth_device->dma_man, buf.virt, len);
224
225    unsigned int length = len;
226    status = eth_device->driver.i_fn.raw_tx(&eth_device->driver, 1, &buf.phys, &length, (void *) buf_no);
227
228    switch (status) {
229    case ETHIF_TX_FAILED:
230        pico_tx_complete(dev, (void *) buf_no);
231        ZF_LOGE("Failed tx\n");
232        return 0; // Error for PICO
233    case ETHIF_TX_COMPLETE:
234        pico_tx_complete(dev, (void *) buf_no);
235    case ETHIF_TX_ENQUEUED:
236        break;
237    }
238
239    return length;
240
241}
242
243/* Async driver will set a flag to signal that there is work to be done  */
244static int pico_eth_poll(struct pico_device *dev, int loop_score)
245{
246    struct pico_device_eth *eth_device = (struct pico_device_eth *)dev;
247    while (loop_score > 0) {
248        if (eth_device->rx_count == 0) {
249#ifdef CONFIG_LIB_PICOTCP_ASYNC_DRIVER
250            /* Also clear the serving_interrupt flag for async driver*/
251            eth_device->pico_dev.__serving_interrupt = 0;
252#endif
253            break;
254        }
255
256        /* Retrieve the data from the rx buffer */
257        eth_device->rx_count -= 1;
258        int buf_no = eth_device->rx_queue[eth_device->rx_count];
259        dma_addr_t *buf = eth_device->bufs[buf_no];
260
261        int len = eth_device->rx_lens[buf_no];
262        ps_dma_cache_invalidate(&eth_device->dma_man, buf->virt, len);
263        pico_stack_recv(dev, buf->virt, len);
264
265        free_buf_pool(eth_device, buf_no);
266        loop_score--;
267    }
268
269    return loop_score;
270}
271
272static struct raw_iface_callbacks pico_prealloc_callbacks = {
273    .tx_complete = pico_tx_complete,
274    .rx_complete = pico_rx_complete,
275    .allocate_rx_buf = pico_allocate_rx_buf
276};
277
278struct pico_device *pico_eth_create_no_malloc(char *name,
279                                              ethif_driver_init driver_init, void *driver_config, ps_io_ops_t io_ops, struct pico_device_eth *eth_dev)
280{
281
282    if (eth_dev == NULL) {
283        ZF_LOGE("Invalid pico_device passed into Pico create no_malloc");
284        return NULL;
285    }
286
287    memset(eth_dev, 0, sizeof(struct pico_device_eth));
288
289    /* Set the dma manager up */
290    eth_dev->driver.i_cb = pico_prealloc_callbacks;
291    eth_dev->dma_man = io_ops.dma_manager;
292
293    eth_dev->driver.cb_cookie = eth_dev;
294    /* Initialize hardware */
295    int err;
296    err = driver_init(&(eth_dev->driver), io_ops, driver_config);
297    if (err) {
298        return NULL;
299    }
300
301    /* Initialise buffers in case driver did not do so */
302    if (!eth_dev->bufs) {
303        initialize_free_bufs(eth_dev);
304    }
305
306    /* Attach funciton pointers */
307    eth_dev->pico_dev.send = pico_eth_send;
308
309#ifdef CONFIG_LIB_PICOTCP_ASYNC_DRIVER
310    /* Although the same function, .poll needs to be set to NULL for an async driver */
311    eth_dev->pico_dev.poll = NULL;
312    eth_dev->pico_dev.dsr = pico_eth_poll;
313#else
314    eth_dev->pico_dev.poll = pico_eth_poll;
315#endif
316
317    /* Also do some low level init */
318    int mtu;
319    uint8_t mac[6] = {0};
320
321    eth_dev->driver.i_fn.low_level_init(&eth_dev->driver, mac, &mtu);
322
323    /* Configure the mtu in picotcp */
324    eth_dev->pico_dev.mtu = mtu;
325
326    /* Register in picoTCP (equivalent to netif init in lwip)*/
327    if (pico_device_init(&(eth_dev->pico_dev), name, mac) != 0) {
328        ZF_LOGE("Failed to initialize pico device");
329        free(eth_dev);
330        return NULL;
331    }
332
333    return (struct pico_device *)eth_dev;
334}
335
336struct pico_device *pico_eth_create(char *name,
337                                    ethif_driver_init driver_init, void *driver_config, ps_io_ops_t io_ops)
338{
339
340    /* Create the pico device struct */
341    struct pico_device_eth *eth_dev = malloc(sizeof(struct pico_device_eth));
342    if (!eth_dev) {
343        ZF_LOGE("Failed to malloc pico eth device interface");
344        return NULL;
345    }
346
347    return pico_eth_create_no_malloc(name, driver_init, driver_config, io_ops, eth_dev);
348}
349
350#endif // CONFIG_LIB_PICOTCP
351