blob: c10330ce7a106b17139839478f93ad0bcddf8ecc [file] [log] [blame]
// 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 <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(int fd, 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;
}
eth_fifos_t fifos;
zx_status_t status;
ssize_t r;
if ((r = ioctl_ethernet_get_fifos(fd, &fifos)) < 0) {
fprintf(stderr, "eth_create: failed to get fifos: %zd\n", r);
return r;
}
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;
}
if ((r = ioctl_ethernet_set_iobuf(fd, &vmo)) < 0) {
fprintf(stderr, "eth_create: failed to set iobuf: %zd\n", r);
status = r;
goto fail;
}
if ((r = ioctl_ethernet_set_client_name(fd, "netsvc", 6)) < 0) {
fprintf(stderr, "eth_create: failed to set client name: %zd\n", r);
}
eth->tx_fifo = fifos.tx_fifo;
eth->rx_fifo = fifos.rx_fifo;
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_fifo);
zx_handle_close(fifos.rx_fifo);
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 = cookie,
};
uint32_t actual;
IORING_TRACE("eth:tx+ c=%p o=%u l=%u f=%u\n",
e.cookie, e.offset, e.length, e.flags);
return zx_fifo_write(eth->tx_fifo, &e, sizeof(e), &actual);
}
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 = cookie,
};
uint32_t actual;
IORING_TRACE("eth:rx+ c=%p o=%u l=%u f=%u\n",
e.cookie, e.offset, e.length, e.flags);
return zx_fifo_write(eth->rx_fifo, &e, sizeof(e), &actual);
}
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;
uint32_t count;
if ((status = zx_fifo_read(eth->tx_fifo, entries, sizeof(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=%p o=%u l=%u f=%u\n",
e->cookie, e->offset, e->length, e->flags);
func(ctx, 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;
uint32_t count;
if ((status = zx_fifo_read(eth->rx_fifo, entries, sizeof(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=%p o=%u l=%u f=%u\n",
e->cookie, e->offset, e->length, e->flags);
func(ctx, 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) {
return status;
}
if (signals & ZX_FIFO_PEER_CLOSED) {
return ZX_ERR_PEER_CLOSED;
}
return ZX_OK;
}