blob: 2aa17f0c04ed32b06ff8f67ca48712839afa2b6f [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.composite.test/cpp/wire.h>
#include <fidl/fuchsia.driver.framework/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/fpromise/scope.h>
#include <bind/fuchsia/test/cpp/bind.h>
#include "src/devices/lib/compat/compat.h"
namespace fdf {
using namespace fuchsia_driver_framework;
} // namespace fdf
namespace fcd = fuchsia_component_decl;
namespace fio = fuchsia_io;
namespace ft = fuchsia_composite_test;
using fpromise::error;
using fpromise::ok;
using fpromise::promise;
using fpromise::result;
namespace {
// Name these differently than what the child expects, so we test that
// FDF renames these correctly.
const std::string_view kLeftName = "left-node";
const std::string_view kRightName = "right-node";
class NumberServer : public fidl::WireServer<ft::Device> {
public:
explicit NumberServer(uint32_t number) : number_(number) {}
void GetNumber(GetNumberRequestView request, GetNumberCompleter::Sync& completer) override {
completer.Reply(number_);
}
private:
uint32_t number_;
};
class RootDriver {
public:
RootDriver(async_dispatcher_t* dispatcher, fidl::WireSharedClient<fdf::Node> node,
driver::Namespace ns, driver::Logger logger, component::OutgoingDirectory outgoing)
: dispatcher_(dispatcher),
executor_(dispatcher),
node_(std::move(node)),
ns_(std::move(ns)),
logger_(std::move(logger)),
outgoing_(std::move(outgoing)) {}
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 outgoing = component::OutgoingDirectory::Create(dispatcher->async_dispatcher());
auto driver =
std::make_unique<RootDriver>(dispatcher->async_dispatcher(), std::move(node), std::move(ns),
std::move(logger), std::move(outgoing));
auto serve = driver->outgoing_.Serve(std::move(start_args.outgoing_dir()));
if (serve.is_error()) {
return serve.take_error();
}
auto status = driver->Run();
if (status.is_error()) {
return status.take_error();
}
return zx::ok(std::move(driver));
}
private:
zx::status<> Run() {
vfs_.SetDispatcher(dispatcher_);
service_dir_ = fbl::MakeRefCounted<fs::PseudoDir>();
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
zx_status_t serve_status = vfs_.Serve(service_dir_, endpoints->server.TakeChannel(),
fs::VnodeConnectionOptions::ReadWrite());
if (serve_status != ZX_OK) {
return zx::error(serve_status);
}
// Add service "left".
{
component::ServiceHandler handler;
ft::Service::Handler service(&handler);
auto device = [this](fidl::ServerEnd<ft::Device> server_end) mutable -> void {
fidl::BindServer<fidl::WireServer<ft::Device>>(dispatcher_, std::move(server_end),
&this->left_server_);
};
zx::status<> status = service.add_device(std::move(device));
if (status.is_error()) {
FDF_LOG(ERROR, "Failed to add device %s", status.status_string());
}
status = outgoing_.AddService<ft::Service>(std::move(handler), kLeftName);
if (status.is_error()) {
FDF_LOG(ERROR, "Failed to add service %s", status.status_string());
}
}
// Add service "right".
{
component::ServiceHandler handler;
ft::Service::Handler service(&handler);
auto device = [this](fidl::ServerEnd<ft::Device> server_end) mutable -> void {
fidl::BindServer<fidl::WireServer<ft::Device>>(dispatcher_, std::move(server_end),
&this->right_server_);
};
zx::status<> status = service.add_device(std::move(device));
if (status.is_error()) {
FDF_LOG(ERROR, "Failed to add device %s", status.status_string());
}
status = outgoing_.AddService<ft::Service>(std::move(handler), kRightName);
if (status.is_error()) {
FDF_LOG(ERROR, "Failed to add service %s", status.status_string());
}
}
// Start the driver.
auto task = AddChild(kLeftName, bind_fuchsia_test::BIND_PROTOCOL_DEVICE, left_controller_)
.and_then(AddChild(kRightName, bind_fuchsia_test::BIND_PROTOCOL_POWER_CHILD,
right_controller_))
.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(
std::string_view name, int protocol,
fidl::WireSharedClient<fdf::NodeController>& controller) {
fidl::Arena arena;
// Set the properties of the node that a driver will bind to.
fdf::wire::NodeProperty property(arena);
property.set_key(arena, fdf::wire::NodePropertyKey::WithIntValue(1 /* BIND_PROTOCOL */))
.set_value(arena, fdf::wire::NodePropertyValue::WithIntValue(protocol));
fdf::wire::NodeAddArgs args(arena);
// Set the offers of the node.
auto service = fcd::wire::OfferService::Builder(arena);
service.source_name(arena, ft::Service::Name);
service.target_name(arena, ft::Service::Name);
fidl::VectorView<fcd::wire::NameMapping> mappings(arena, 1);
mappings[0].source_name = fidl::StringView(arena, name);
mappings[0].target_name = fidl::StringView(arena, "default");
service.renamed_instances(mappings);
auto instance_filter = fidl::VectorView<fidl::StringView>(arena, 1);
instance_filter[0] = fidl::StringView(arena, "default");
service.source_instance_filter(instance_filter);
auto offers = fidl::VectorView<fcd::wire::Offer>(arena, 1);
offers[0] = fcd::wire::Offer::WithService(arena, service.Build());
args.set_offers(arena, offers);
args.set_name(arena, fidl::StringView::FromExternal(name))
.set_properties(arena,
fidl::VectorView<fdf::wire::NodeProperty>::FromExternal(&property, 1));
// 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, &controller, 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_;
fidl::WireSharedClient<fdf::Node> node_;
fidl::WireSharedClient<fdf::NodeController> left_controller_;
fidl::WireSharedClient<fdf::NodeController> right_controller_;
driver::Namespace ns_;
driver::Logger logger_;
NumberServer left_server_ = NumberServer(1);
NumberServer right_server_ = NumberServer(2);
fs::SynchronousVfs vfs_;
fbl::RefPtr<fs::PseudoDir> service_dir_;
component::OutgoingDirectory outgoing_;
// NOTE: Must be the last member.
fpromise::scope scope_;
};
} // namespace
FUCHSIA_DRIVER_RECORD_CPP_V1(RootDriver);