blob: b7d5a1d1a364fd04d2f50f08650d9b4ba132abba [file] [log] [blame]
// Copyright 2022 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 <fidl/fuchsia.component.decl/cpp/wire.h>
#include <fidl/fuchsia.driver.framework/cpp/wire.h>
#include <fidl/fuchsia.runtime.test/cpp/driver/wire.h>
#include <fidl/fuchsia.runtime.test/cpp/wire.h>
#include <lib/async/cpp/executor.h>
#include <lib/driver2/logger.h>
#include <lib/driver2/namespace.h>
#include <lib/driver2/promise.h>
#include <lib/driver2/record_cpp.h>
#include <lib/driver2/runtime.h>
#include <lib/driver2/runtime_connector_impl.h>
#include <lib/fdf/cpp/channel.h>
#include <lib/fdf/dispatcher.h>
#include <lib/fpromise/bridge.h>
#include <lib/fpromise/scope.h>
#include <lib/sys/component/cpp/outgoing_directory.h>
#include <bind/fuchsia/test/cpp/bind.h>
namespace fdf {
using namespace fuchsia_driver_framework;
} // namespace fdf
namespace fcd = fuchsia_component_decl;
namespace fio = fuchsia_io;
namespace ft = fuchsia_runtime_test;
using fpromise::error;
using fpromise::ok;
using fpromise::promise;
using fpromise::result;
namespace {
class RootDriver : public driver::RuntimeConnectorImpl {
public:
RootDriver(fdf::UnownedDispatcher dispatcher, fidl::WireSharedClient<fdf::Node> node,
driver::Namespace ns, driver::Logger logger)
: driver::RuntimeConnectorImpl(dispatcher->async_dispatcher()),
dispatcher_(dispatcher->async_dispatcher()),
executor_(dispatcher->async_dispatcher()),
outgoing_(component::OutgoingDirectory::Create(dispatcher->async_dispatcher())),
fdf_dispatcher_(std::move(dispatcher)),
node_(std::move(node)),
ns_(std::move(ns)),
logger_(std::move(logger)) {}
static constexpr const char* Name() { return "root"; }
static zx::status<std::unique_ptr<RootDriver>> Start(fdf::wire::DriverStartArgs& start_args,
fdf::UnownedDispatcher dispatcher,
fidl::WireSharedClient<fdf::Node> node,
driver::Namespace ns,
driver::Logger logger) {
auto driver = std::make_unique<RootDriver>(std::move(dispatcher), std::move(node),
std::move(ns), std::move(logger));
auto result = driver->Run(std::move(start_args.outgoing_dir()));
if (result.is_error()) {
return result.take_error();
}
return zx::ok(std::move(driver));
}
zx_status_t RegisterProtocolHandler(fdf::Channel channel) {
// Wait for messages from the child.
auto channel_read = std::make_unique<fdf::ChannelRead>(
channel.release(), 0 /* options */,
[this](fdf_dispatcher_t* dispatcher, fdf::ChannelRead* channel_read, fdf_status_t status) {
if (status == ZX_OK) {
zx_status_t status =
HandleChildRuntimeRequest(fdf::UnownedChannel(channel_read->channel()));
if (status == ZX_OK) {
status = channel_read->Begin(fdf_dispatcher_->get());
if (status == ZX_OK) {
return;
}
}
}
fdf_handle_close(channel_read->channel());
delete channel_read;
});
zx_status_t status = channel_read->Begin(fdf_dispatcher_->get());
if (status != ZX_OK) {
return status;
}
channel_read.release();
return ZX_OK;
}
zx_status_t HandleChildRuntimeRequest(fdf::UnownedChannel channel) {
auto read_return = channel->Read(0);
if (read_return.is_error()) {
FDF_LOG(ERROR, "HandleChildRuntimeRequest got unexpected read error: %s",
zx_status_get_string(read_return.status_value()));
return read_return.status_value();
}
uint32_t data = ft::wire::kParentDeviceTestData;
void* ptr = read_return->arena.Allocate(sizeof(data));
memcpy(ptr, &data, sizeof(data));
auto write_status =
channel->Write(0, read_return->arena, ptr, sizeof(data), cpp20::span<zx_handle_t>());
if (write_status.is_error()) {
FDF_LOG(ERROR, "HandleChildRuntimeRequest got unexpected write error: %s",
zx_status_get_string(write_status.status_value()));
return write_status.status_value();
}
return ZX_OK;
}
private:
zx::status<> Run(fidl::ServerEnd<fio::Directory> outgoing_dir) {
// Setup the outgoing directory.
auto service = [this](fidl::ServerEnd<fdf::RuntimeConnector> server_end) {
fidl::BindServer(dispatcher_, std::move(server_end), this);
};
zx::status<> status = outgoing_.AddProtocol<fdf::RuntimeConnector>(std::move(service));
if (status.is_error()) {
return status;
}
auto serve = outgoing_.Serve(std::move(outgoing_dir));
if (serve.is_error()) {
return serve.take_error();
}
RegisterProtocol(fidl::DiscoverableProtocolName<ft::DriverTransportProtocol>,
fit::bind_member(this, &RootDriver::RegisterProtocolHandler));
// Start the driver.
auto task =
AddChild().or_else(fit::bind_member(this, &RootDriver::UnbindNode)).wrap_with(scope_);
executor_.schedule_task(std::move(task));
return zx::ok();
}
promise<void, fdf::wire::NodeError> AddChild() {
fidl::Arena arena;
// Offer `fuchsia.test.Handshake` to the driver that binds to the node.
auto protocol = fcd::wire::OfferProtocol::Builder(arena)
.source_name(fidl::StringView::FromExternal(
fidl::DiscoverableProtocolName<fdf::RuntimeConnector>))
.target_name(fidl::StringView::FromExternal(
fidl::DiscoverableProtocolName<fdf::RuntimeConnector>))
.dependency_type(fcd::wire::DependencyType::kStrong)
.Build();
fcd::wire::Offer offer = fcd::wire::Offer::WithProtocol(arena, std::move(protocol));
// Set the properties of the node that a driver will bind to.
auto property = fdf::wire::NodeProperty::Builder(arena)
.key(fdf::wire::NodePropertyKey::WithIntValue(1 /* BIND_PROTOCOL */))
.value(fdf::wire::NodePropertyValue::WithIntValue(
bind_fuchsia_test::BIND_PROTOCOL_DEVICE))
.Build();
auto args =
fdf::wire::NodeAddArgs::Builder(arena)
.name("leaf")
.properties(fidl::VectorView<fdf::wire::NodeProperty>::FromExternal(&property, 1))
.offers(fidl::VectorView<fcd::wire::Offer>::FromExternal(&offer, 1))
.Build();
// Create endpoints of the `NodeController` for the node.
auto endpoints = fidl::CreateEndpoints<fdf::NodeController>();
if (endpoints.is_error()) {
return fpromise::make_error_promise(fdf::wire::NodeError::kInternal);
}
return driver::AddChild(node_, std::move(args), std::move(endpoints->server), {})
.and_then([this, client = std::move(endpoints->client)]() mutable {
controller_.Bind(std::move(client), dispatcher_);
});
}
result<> UnbindNode(const fdf::wire::NodeError& error) {
FDF_LOG(ERROR, "Failed to start root driver: %d", error);
node_.AsyncTeardown();
return ok();
}
async_dispatcher_t* const dispatcher_;
async::Executor executor_;
component::OutgoingDirectory outgoing_;
fdf::UnownedDispatcher const fdf_dispatcher_;
fidl::WireSharedClient<fdf::Node> node_;
fidl::WireSharedClient<fdf::NodeController> controller_;
driver::Namespace ns_;
driver::Logger logger_;
// NOTE: Must be the last member.
fpromise::scope scope_;
};
} // namespace
FUCHSIA_DRIVER_RECORD_CPP_V1(RootDriver);