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(ð_device->dma_man, buf.virt, len); 224 225 unsigned int length = len; 226 status = eth_device->driver.i_fn.raw_tx(ð_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(ð_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(ð_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