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