blob: 12bc46411daf599f11c714f487aefb6b384629be [file] [log] [blame]
// Copyright 2019 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 "spi-child.h"
#include <lib/async/cpp/task.h>
#include <lib/trace/event.h>
#include <zircon/errors.h>
#include "sdk/lib/driver/logging/cpp/logger.h"
namespace spi {
template <typename T>
inline zx_status_t FidlStatus(const T& result) {
if (result.ok()) {
return result->is_ok() ? ZX_OK : result->error_value();
}
return result.status();
}
void SpiChild::OpenSession(OpenSessionRequestView request, OpenSessionCompleter::Sync& completer) {
Bind(std::move(request->session));
}
void SpiChild::TransmitVector(TransmitVectorRequestView request,
TransmitVectorCompleter::Sync& completer) {
fdf::Arena arena('SPI_');
spi_.buffer(arena)
->TransmitVector(cs_, request->data)
.ThenExactlyOnce(
fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::TransmitVector>&),
sizeof(TransmitVectorCompleter::Async)>(
[completer = completer.ToAsync()](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "Couldn't complete SpiImpl::TransmitVector: %s",
zx_status_get_string(status));
completer.Reply(status);
} else {
completer.Reply(ZX_OK);
}
}));
}
void SpiChild::ReceiveVector(ReceiveVectorRequestView request,
ReceiveVectorCompleter::Sync& completer) {
struct {
uint32_t size;
ReceiveVectorCompleter::Async completer;
} request_data{request->size, completer.ToAsync()};
fdf::Arena arena('SPI_');
spi_.buffer(arena)
->ReceiveVector(cs_, request->size)
.ThenExactlyOnce(
fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::ReceiveVector>&),
sizeof(request_data)>([request_data = std::move(request_data)](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "Couldn't complete SpiImpl::ReceiveVector: %s",
zx_status_get_string(status));
request_data.completer.Reply(status, {});
} else if (result->value()->data.count() != request_data.size) {
FDF_LOG(ERROR, "Expected %u bytes != received %zu bytes", request_data.size,
result->value()->data.count());
request_data.completer.Reply(ZX_ERR_INTERNAL, {});
} else {
request_data.completer.Reply(ZX_OK, result->value()->data);
}
}));
}
void SpiChild::ExchangeVector(ExchangeVectorRequestView request,
ExchangeVectorCompleter::Sync& completer) {
struct {
size_t txdata_count;
ExchangeVectorCompleter::Async completer;
} request_data{request->txdata.count(), completer.ToAsync()};
fdf::Arena arena('SPI_');
spi_.buffer(arena)
->ExchangeVector(cs_, request->txdata)
.ThenExactlyOnce(
fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::ExchangeVector>&),
sizeof(request_data)>([request_data = std::move(request_data)](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "Couldn't complete SpiImpl::ExchangeVector: %s",
zx_status_get_string(status));
request_data.completer.Reply(status, {});
} else if (result->value()->rxdata.count() != request_data.txdata_count) {
FDF_LOG(ERROR, "Expected %zu bytes != received %zu bytes", request_data.txdata_count,
result->value()->rxdata.count());
request_data.completer.Reply(ZX_ERR_INTERNAL, {});
} else {
request_data.completer.Reply(ZX_OK, result->value()->rxdata);
}
}));
}
void SpiChild::RegisterVmo(RegisterVmoRequestView request, RegisterVmoCompleter::Sync& completer) {
fdf::Arena arena('SPI_');
spi_.buffer(arena)
->RegisterVmo(cs_, request->vmo_id, std::move(request->vmo), request->rights)
.ThenExactlyOnce(
fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::RegisterVmo>&),
sizeof(RegisterVmoCompleter::Async)>(
[completer = completer.ToAsync()](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "Couldn't complete SpiImpl::RegisterVmo: %s",
zx_status_get_string(status));
completer.ReplyError(status);
} else {
completer.ReplySuccess();
}
}));
}
void SpiChild::UnregisterVmo(UnregisterVmoRequestView request,
UnregisterVmoCompleter::Sync& completer) {
fdf::Arena arena('SPI_');
spi_.buffer(arena)
->UnregisterVmo(cs_, request->vmo_id)
.ThenExactlyOnce(
fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::UnregisterVmo>&),
sizeof(UnregisterVmoCompleter::Async)>(
[completer = completer.ToAsync()](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "Couldn't complete SpiImpl::UnregisterVmo: %s",
zx_status_get_string(status));
completer.ReplyError(status);
} else {
completer.ReplySuccess(std::move(result->value()->vmo));
}
}));
}
void SpiChild::Transmit(TransmitRequestView request, TransmitCompleter::Sync& completer) {
TRACE_DURATION("spi", "Transmit", "cs", cs_, "size", request->buffer.size);
fdf::Arena arena('SPI_');
spi_.buffer(arena)
->TransmitVmo(cs_, request->buffer)
.ThenExactlyOnce(
fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::TransmitVmo>&),
sizeof(TransmitCompleter::Async)>(
[completer = completer.ToAsync()](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "Couldn't complete SpiImpl::TransmitVmo: %s",
zx_status_get_string(status));
completer.ReplyError(status);
} else {
completer.ReplySuccess();
}
}));
}
void SpiChild::Receive(ReceiveRequestView request, ReceiveCompleter::Sync& completer) {
TRACE_DURATION("spi", "Receive", "cs", cs_, "size", request->buffer.size);
fdf::Arena arena('SPI_');
spi_.buffer(arena)
->ReceiveVmo(cs_, request->buffer)
.ThenExactlyOnce(fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::ReceiveVmo>&),
sizeof(ReceiveCompleter::Async)>([completer = completer.ToAsync()](
auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "Couldn't complete SpiImpl::ReceiveVmo: %s", zx_status_get_string(status));
completer.ReplyError(status);
} else {
completer.ReplySuccess();
}
}));
}
void SpiChild::Exchange(ExchangeRequestView request, ExchangeCompleter::Sync& completer) {
if (request->tx_buffer.size != request->rx_buffer.size) {
FDF_LOG(ERROR, "tx_buffer and rx_buffer size must match. %zu (tx) != %zu (rx)",
request->tx_buffer.size, request->rx_buffer.size);
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
TRACE_DURATION("spi", "Exchange", "cs", cs_, "size", request->tx_buffer.size);
fdf::Arena arena('SPI_');
spi_.buffer(arena)
->ExchangeVmo(cs_, request->tx_buffer, request->rx_buffer)
.ThenExactlyOnce(
fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::ExchangeVmo>&),
sizeof(ExchangeCompleter::Async)>(
[completer = completer.ToAsync()](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "Couldn't complete SpiImpl::ExchangeVmo: %s",
zx_status_get_string(status));
completer.ReplyError(status);
} else {
completer.ReplySuccess();
}
}));
}
void SpiChild::CanAssertCs(CanAssertCsCompleter::Sync& completer) {
completer.Reply(!has_siblings_);
}
void SpiChild::AssertCs(AssertCsCompleter::Sync& completer) {
if (has_siblings_) {
completer.Reply(ZX_ERR_NOT_SUPPORTED);
return;
}
fdf::Arena arena('SPI_');
spi_.buffer(arena)->LockBus(cs_).ThenExactlyOnce(
fit::inline_callback<void(
fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::LockBus>&),
sizeof(AssertCsCompleter::Async)>(
[completer = completer.ToAsync()](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "SpiImpl::LockBus failed: %s", zx_status_get_string(status));
completer.Reply(status);
} else {
completer.Reply(ZX_OK);
}
}));
}
void SpiChild::DeassertCs(DeassertCsCompleter::Sync& completer) {
if (has_siblings_) {
completer.Reply(ZX_ERR_NOT_SUPPORTED);
return;
}
fdf::Arena arena('SPI_');
spi_.buffer(arena)->UnlockBus(cs_).ThenExactlyOnce(
fit::inline_callback<
void(fdf::WireUnownedResult<fuchsia_hardware_spiimpl::SpiImpl::UnlockBus>&),
sizeof(DeassertCsCompleter::Async)>(
[completer = completer.ToAsync()](auto& result) mutable {
if (zx_status_t status = FidlStatus(result); status != ZX_OK) {
FDF_LOG(ERROR, "SpiImpl::UnlockBus failed: %s", zx_status_get_string(status));
completer.Reply(status);
} else {
completer.Reply(ZX_OK);
}
}));
}
void SpiChild::Bind(fidl::ServerEnd<fuchsia_hardware_spi::Device> server_end) {
// binding_ can only be accessed on fidl_dispatcher_.
async::PostTask(
fidl_dispatcher_->async_dispatcher(), [this, server_end = std::move(server_end)]() mutable {
if (binding_.has_value()) {
server_end.Close(ZX_ERR_ALREADY_BOUND);
return;
}
binding_.emplace(fidl_dispatcher_->async_dispatcher(), std::move(server_end), this,
[](SpiChild* self, fidl::UnbindInfo info) {
self->binding_.reset();
fdf::Arena arena('SPI_');
(void)self->spi_.sync().buffer(arena)->ReleaseRegisteredVmos(self->cs_);
});
});
}
fuchsia_hardware_spi::Service::InstanceHandler SpiChild::CreateInstanceHandler() {
return fuchsia_hardware_spi::Service::InstanceHandler({
.device =
[this](fidl::ServerEnd<fuchsia_hardware_spi::Device> server_end) {
Bind(std::move(server_end));
},
});
}
void SpiChild::DevfsConnect(fidl::ServerEnd<fuchsia_hardware_spi::Controller> server) {
controller_bindings_.AddBinding(fdf::Dispatcher::GetCurrent()->async_dispatcher(),
std::move(server), this, fidl::kIgnoreBindingClosure);
}
} // namespace spi