| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "eth-client.h" |
| |
| #include <fuchsia/hardware/ethernet/c/fidl.h> |
| #include <zircon/device/ethernet.h> |
| #include <zircon/syscalls.h> |
| |
| #include <limits.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #if 0 |
| #define IORING_TRACE(fmt...) fprintf(stderr, fmt) |
| #else |
| #define IORING_TRACE(fmt...) do {} while (0) |
| #endif |
| |
| void eth_destroy(eth_client_t* eth) { |
| zx_handle_close(eth->rx_fifo); |
| zx_handle_close(eth->tx_fifo); |
| free(eth); |
| } |
| |
| zx_status_t eth_create(zx_handle_t svc, zx_handle_t io_vmo, void* io_mem, eth_client_t** out) { |
| eth_client_t* eth; |
| |
| if ((eth = calloc(1, sizeof(*eth))) == NULL) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| fuchsia_hardware_ethernet_Fifos fifos; |
| zx_status_t status, call_status; |
| |
| status = fuchsia_hardware_ethernet_DeviceGetFifos(svc, &call_status, &fifos); |
| if (status != ZX_OK || call_status != ZX_OK) { |
| fprintf(stderr, "eth_create: failed to get fifos: %d, %d\n", status, call_status); |
| return status == ZX_OK ? call_status : status; |
| } |
| |
| zx_handle_t vmo; |
| if ((status = zx_handle_duplicate(io_vmo, ZX_RIGHT_SAME_RIGHTS, &vmo)) < 0) { |
| fprintf(stderr, "eth_create: failed to duplicate vmo\n"); |
| goto fail; |
| } |
| status = fuchsia_hardware_ethernet_DeviceSetIOBuffer(svc, vmo, &call_status); |
| if (status != ZX_OK || call_status != ZX_OK) { |
| fprintf(stderr, "eth_create: failed to set iobuf: %d, %d\n", status, call_status); |
| if (status == ZX_OK) { |
| status = call_status; |
| } |
| goto fail; |
| } |
| status = fuchsia_hardware_ethernet_DeviceSetClientName(svc, "netsvc", 6, &call_status); |
| if (status != ZX_OK || call_status != ZX_OK) { |
| fprintf(stderr, "eth_create: failed to set client name %d, %d\n", status, call_status); |
| } |
| |
| eth->tx_fifo = fifos.tx; |
| eth->rx_fifo = fifos.rx; |
| eth->rx_size = fifos.rx_depth; |
| eth->tx_size = fifos.tx_depth; |
| eth->iobuf = io_mem; |
| |
| *out = eth; |
| return ZX_OK; |
| |
| fail: |
| zx_handle_close(fifos.tx); |
| zx_handle_close(fifos.rx); |
| eth_destroy(eth); |
| return status; |
| } |
| |
| zx_status_t eth_queue_tx(eth_client_t* eth, void* cookie, |
| void* data, size_t len, uint32_t options) { |
| eth_fifo_entry_t e = { |
| .offset = data - eth->iobuf, |
| .length = len, |
| .flags = options, |
| .cookie = (uint64_t)cookie, |
| }; |
| IORING_TRACE("eth:tx+ c=0x%08lx o=%u l=%u f=%u\n", |
| e.cookie, e.offset, e.length, e.flags); |
| |
| return zx_fifo_write(eth->tx_fifo, sizeof(e), &e, 1, NULL); |
| } |
| |
| zx_status_t eth_queue_rx(eth_client_t* eth, void* cookie, |
| void* data, size_t len, uint32_t options) { |
| eth_fifo_entry_t e = { |
| .offset = data - eth->iobuf, |
| .length = len, |
| .flags = options, |
| .cookie = (uint64_t)cookie, |
| }; |
| IORING_TRACE("eth:rx+ c=0x%08lx o=%u l=%u f=%u\n", |
| e.cookie, e.offset, e.length, e.flags); |
| return zx_fifo_write(eth->rx_fifo, sizeof(e), &e, 1, NULL); |
| } |
| |
| zx_status_t eth_complete_tx(eth_client_t* eth, void* ctx, |
| void (*func)(void* ctx, void* cookie)) { |
| eth_fifo_entry_t entries[eth->tx_size]; |
| zx_status_t status; |
| size_t count; |
| if ((status = zx_fifo_read(eth->tx_fifo, sizeof(entries[0]), entries, countof(entries), &count)) < 0) { |
| if (status == ZX_ERR_SHOULD_WAIT) { |
| return ZX_OK; |
| } else { |
| return status; |
| } |
| } |
| |
| for (eth_fifo_entry_t* e = entries; count-- > 0; e++) { |
| IORING_TRACE("eth:tx- c=0x%08lx o=%u l=%u f=%u\n", |
| e->cookie, e->offset, e->length, e->flags); |
| func(ctx, (void*)e->cookie); |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t eth_complete_rx(eth_client_t* eth, void* ctx, |
| void (*func)(void* ctx, void* cookie, size_t len, uint32_t flags)) { |
| eth_fifo_entry_t entries[eth->rx_size]; |
| zx_status_t status; |
| size_t count; |
| if ((status = zx_fifo_read(eth->rx_fifo, sizeof(entries[0]), entries, countof(entries), &count)) < 0) { |
| if (status == ZX_ERR_SHOULD_WAIT) { |
| return ZX_OK; |
| } else { |
| return status; |
| } |
| } |
| |
| for (eth_fifo_entry_t* e = entries; count-- > 0; e++) { |
| IORING_TRACE("eth:rx- c=0x%08lx o=%u l=%u f=%u\n", |
| e->cookie, e->offset, e->length, e->flags); |
| func(ctx, (void*)e->cookie, e->length, e->flags); |
| } |
| return ZX_OK; |
| } |
| |
| |
| // Wait for completed rx packets |
| // ZX_ERR_PEER_CLOSED - far side disconnected |
| // ZX_ERR_TIMED_OUT - deadline lapsed |
| // ZX_OK - completed packets are available |
| zx_status_t eth_wait_rx(eth_client_t* eth, zx_time_t deadline) { |
| zx_status_t status; |
| zx_signals_t signals; |
| |
| if ((status = zx_object_wait_one(eth->rx_fifo, |
| ZX_FIFO_READABLE | ZX_FIFO_PEER_CLOSED, |
| deadline, &signals)) < 0) { |
| if (signals & ZX_FIFO_READABLE) { |
| return ZX_OK; |
| } |
| return status; |
| } |
| if (signals & ZX_FIFO_PEER_CLOSED) { |
| return ZX_ERR_PEER_CLOSED; |
| } |
| return ZX_OK; |
| } |