blob: 8393c481f93a7a18ca53e515a04f5e05894d7e1b [file]
// 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 "src/bringup/bin/netsvc/eth-client.h"
#include <stdio.h>
#include <zircon/device/ethernet.h>
#include <zircon/syscalls.h>
#include <fbl/alloc_checker.h>
#if 0
#define IORING_TRACE(fmt...) fprintf(stderr, fmt)
#else
#define IORING_TRACE(fmt...)
#endif
zx::status<std::unique_ptr<EthClient>> EthClient::Create(
async_dispatcher_t* dispatcher, fidl::ClientEnd<fuchsia_hardware_ethernet::Device> client_end,
zx::vmo io_vmo, void* io_mem, fit::closure on_rx, fit::closure on_status,
fit::closure on_closed) {
fidl::WireSyncClient eth = fidl::BindSyncClient(std::move(client_end));
fidl::WireResult r = eth->GetFifos();
if (!r.ok()) {
fprintf(stderr, "%s: failed to get fifos: %s\n", __FUNCTION__, r.status_string());
return zx::error(r.status());
}
if (zx_status_t status = r.value().status; status != ZX_OK) {
fprintf(stderr, "%s: GetFifos error: %s\n", __FUNCTION__, zx_status_get_string(status));
return zx::error(status);
}
fuchsia_hardware_ethernet::wire::Fifos& fifos = *r.value().info;
{
fidl::WireResult result = eth->SetIoBuffer(std::move(io_vmo));
if (!result.ok()) {
fprintf(stderr, "%s: failed to set iobuf: %s\n", __FUNCTION__, result.status_string());
return zx::error(result.status());
}
if (zx_status_t status = result.value().status; status != ZX_OK) {
fprintf(stderr, "%s: set iobuf error: %s\n", __FUNCTION__, zx_status_get_string(status));
return zx::error(status);
}
}
{
fidl::WireResult result = eth->SetClientName("netsvc");
if (!result.ok()) {
fprintf(stderr, "%s: failed to set client name %s\n", __FUNCTION__, result.status_string());
return zx::error(result.status());
}
if (zx_status_t status = result.value().status; status != ZX_OK) {
fprintf(stderr, "%s: set client name error %s\n", __FUNCTION__, zx_status_get_string(status));
return zx::error(status);
}
}
{
fidl::WireResult result = eth->Start();
if (!result.ok()) {
fprintf(stderr, "%s: failed to start device %s\n", __FUNCTION__, result.status_string());
return zx::error(result.status());
}
if (zx_status_t status = result.value().status; status != ZX_OK) {
fprintf(stderr, "%s: device error %s\n", __FUNCTION__, result.status_string());
return zx::error(status);
}
}
fbl::AllocChecker ac;
std::unique_ptr<EthClient> cli(new (&ac) EthClient(
dispatcher, std::move(eth), std::move(fifos.tx), fifos.tx_depth, std::move(fifos.rx),
fifos.rx_depth, io_mem, std::move(on_rx), std::move(on_status), std::move(on_closed)));
if (!ac.check()) {
return zx::error(ZX_ERR_NO_MEMORY);
}
return zx::ok(std::move(cli));
}
zx_status_t EthClient::QueueTx(void* cookie, void* data, size_t len) {
eth_fifo_entry_t e = {
.offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(data) - io_mem_),
.length = static_cast<uint16_t>(len),
.cookie = reinterpret_cast<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 tx_fifo_.write(sizeof(e), &e, 1, nullptr);
}
zx_status_t EthClient::QueueRx(void* cookie, void* data, size_t len) {
eth_fifo_entry_t e = {
.offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(data) - io_mem_),
.length = static_cast<uint16_t>(len),
.cookie = reinterpret_cast<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 rx_fifo_.write(sizeof(e), &e, 1, nullptr);
}
namespace {
template <typename F>
zx_status_t CompleteFifo(zx::fifo& fifo, uint32_t depth, F process_entry) {
eth_fifo_entry_t entries[depth];
size_t count;
if (zx_status_t status = fifo.read(sizeof(*entries), entries, depth, &count); status != ZX_OK) {
if (status == ZX_ERR_SHOULD_WAIT) {
return ZX_OK;
}
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);
process_entry(*e);
}
return ZX_OK;
}
} // namespace
zx_status_t EthClient::CompleteTx(void* ctx, void (*func)(void* ctx, void* cookie)) {
return CompleteFifo(tx_fifo_, tx_size_, [ctx, func](const eth_fifo_entry_t& 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, reinterpret_cast<void*>(e.cookie));
});
}
zx_status_t EthClient::CompleteRx(void* ctx, void (*func)(async_dispatcher_t* dispatcher, void* ctx,
void* cookie, size_t len)) {
return CompleteFifo(rx_fifo_, rx_size_, [ctx, func, this](const eth_fifo_entry_t& e) {
IORING_TRACE("eth:rx- c=0x%08lx o=%u l=%u f=%u\n", e.cookie, e.offset, e.length, e.flags);
func(dispatcher_, ctx, reinterpret_cast<void*>(e.cookie), e.length);
});
}
void EthClient::OnRxSignal(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
if (status == ZX_ERR_CANCELED) {
return;
}
ZX_ASSERT_MSG(status == ZX_OK, "dispatch error %s", zx_status_get_string(status));
if (signal->observed & ZX_FIFO_PEER_CLOSED) {
on_closed_();
return;
}
if (signal->observed & ZX_FIFO_READABLE) {
on_rx_();
}
if (signal->observed & fuchsia_hardware_ethernet::wire::kSignalStatus) {
on_signal_();
}
status = wait->Begin(dispatcher);
ZX_ASSERT_MSG(status == ZX_OK, "failed to reinstall wait %s", zx_status_get_string(status));
}
zx_status_t EthClient::WaitTx(zx::time deadline) {
zx_signals_t signals;
if (zx_status_t status =
tx_fifo_.wait_one(ZX_FIFO_READABLE | ZX_FIFO_PEER_CLOSED, deadline, &signals);
status != ZX_OK) {
return status;
}
if (signals & ZX_FIFO_PEER_CLOSED) {
return ZX_ERR_PEER_CLOSED;
}
return ZX_OK;
}
zx::status<fuchsia_hardware_ethernet::wire::DeviceStatus> EthClient::GetStatus() {
fidl::WireResult result = device_->GetStatus();
if (!result.ok()) {
return zx::error(result.status());
}
return zx::ok(result.value().device_status);
}