blob: a41e8ebc6ed9ae574d4074746306f3b8e93d837c [file] [log] [blame] [edit]
// 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.
#ifndef LIB_DRIVER2_DRIVER_BASE_H_
#define LIB_DRIVER2_DRIVER_BASE_H_
#include <fidl/fuchsia.driver.framework/cpp/fidl.h>
#include <lib/driver2/driver_context.h>
#include <lib/driver2/logger.h>
#include <lib/driver2/namespace.h>
#include <lib/driver2/outgoing_directory.h>
#include <lib/driver2/record.h>
#include <lib/driver2/start_args.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/sys/component/cpp/structured_config.h>
namespace driver {
using DriverStartArgs = fuchsia_driver_framework::DriverStartArgs;
// |DriverBase| is an interface that drivers should inherit from. It provides methods
// for accessing the start args, as well as helper methods for common initialization tasks.
//
// There are two virtual methods, |Start| which must be overridden,
// and |Stop| which is optional to override.
//
// In order to work with the default |BasicFactory| factory implementation,
// classes which inherit from |DriverBase| must implement a constructor with the following
// signature and forward said parameters to the |DriverBase| base class:
//
// T(DriverStartArgs start_args, fdf::UnownedDispatcher driver_dispatcher);
//
// Otherwise a custom factory must be created and used to call constructors of any other shape.
//
// The following illustrates an example:
//
// ```
// class MyDriver : public driver::DriverBase {
// public:
// MyDriver(driver::DriverStartArgs start_args, fdf::UnownedDispatcher driver_dispatcher)
// : driver::DriverBase("my_driver", std::move(start_args), std::move(driver_dispatcher)) {}
//
// zx::status<> Start() override {
// context().incoming()->Connect(...);
// context().outgoing()->AddService(...);
// FDF_LOG(INFO, "hello world!");
// node_client_.Bind(std::move(node()), dispatcher());
//
// /* Ensure all capabilities offered have been added to the outgoing directory first. */
// auto add_result = node_client_->AddChild(...); if (add_result.is_error()) {
// /* Releasing the node channel signals unbind to DF. */
// node_client_.AsyncTeardown(); // Or node().reset() if we hadn't moved it into the client.
// return add_result.take_error();
// }
//
// return zx::ok();
// }
// private:
// fidl::SharedClient<fuchsia_driver_framework::Node> node_client_;
// };
// ```
class DriverBase {
public:
DriverBase(std::string_view name, DriverStartArgs start_args,
fdf::UnownedDispatcher driver_dispatcher)
: start_args_(std::move(start_args)),
driver_dispatcher_(std::move(driver_dispatcher)),
dispatcher_(driver_dispatcher_->async_dispatcher()),
driver_context_(driver_dispatcher_->get()) {
auto ns = std::move(start_args_.ns());
ZX_ASSERT(ns.has_value());
Namespace incoming = Namespace::Create(ns.value()).value();
logger_ = Logger::Create(incoming, dispatcher_, name).value();
auto outgoing_request = std::move(start_args_.outgoing_dir());
ZX_ASSERT(outgoing_request.has_value());
driver_context_.InitializeAndServe(std::move(incoming), std::move(outgoing_request.value()));
}
DriverBase(const DriverBase&) = delete;
DriverBase& operator=(const DriverBase&) = delete;
virtual ~DriverBase() = default;
// This method will be called by the factory to start the driver. This is when
// the driver should setup the outgoing directory through `context()->outgoing()->Add...` calls.
// Do not call Serve, as it has already been called by the |DriverBase| constructor.
// Child nodes can be created here synchronously or asynchronously as long as all of the
// protocols being offered to the child has been added to the outgoing directory first.
virtual zx::status<> Start() = 0;
virtual void Stop() {}
// This can be used to log in driver factories:
// `FDF_LOGL(INFO, driver->logger(), "...");`
Logger& logger() { return logger_; }
protected:
// The logger can't be private because the logging macros rely on it.
// NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
Logger logger_;
fidl::ClientEnd<fuchsia_driver_framework::Node>& node() {
auto& node = start_args_.node();
ZX_ASSERT(node.has_value());
return node.value();
}
const fidl::ClientEnd<fuchsia_driver_framework::Node>& node() const {
auto& node = start_args_.node();
ZX_ASSERT(node.has_value());
return node.value();
}
template <typename StructuredConfig>
StructuredConfig take_config() {
static_assert(component::IsDriverStructuredConfigV<StructuredConfig>,
"Invalid type supplied. StructuredConfig must be a driver flavored "
"structured config type. Example usage: take_config<my_driverconfig::Config>().");
return StructuredConfig::TakeFromStartArgs(start_args_);
}
DriverContext& context() { return driver_context_; }
const DriverContext& context() const { return driver_context_; }
fdf::UnownedDispatcher& driver_dispatcher() { return driver_dispatcher_; }
const fdf::UnownedDispatcher& driver_dispatcher() const { return driver_dispatcher_; }
async_dispatcher_t* dispatcher() { return dispatcher_; }
const async_dispatcher_t* dispatcher() const { return dispatcher_; }
std::optional<fuchsia_data::Dictionary>& program() { return start_args_.program(); }
const std::optional<fuchsia_data::Dictionary>& program() const { return start_args_.program(); }
std::optional<std::string>& url() { return start_args_.url(); }
const std::optional<std::string>& url() const { return start_args_.url(); }
std::optional<std::string>& node_name() { return start_args_.node_name(); }
const std::optional<std::string>& node_name() const { return start_args_.node_name(); }
std::optional<std::vector<fuchsia_driver_framework::NodeSymbol>>& symbols() {
return start_args_.symbols();
}
const std::optional<std::vector<fuchsia_driver_framework::NodeSymbol>>& symbols() const {
return start_args_.symbols();
}
private:
DriverStartArgs start_args_;
fdf::UnownedDispatcher driver_dispatcher_;
async_dispatcher_t* dispatcher_;
DriverContext driver_context_;
};
// This is the default Factory that is used to Create a Driver of type |Driver|, that inherits the
// |DriverBase| class. |Driver| must implement a constructor with the following
// signature and forward said parameters to the |DriverBase| base class:
//
// T(DriverStartArgs start_args, fdf::UnownedDispatcher driver_dispatcher);
template <typename Driver>
class BasicFactory {
static_assert(std::is_base_of_v<DriverBase, Driver>, "Driver has to inherit from DriverBase");
static_assert(std::is_constructible_v<Driver, DriverStartArgs, fdf::UnownedDispatcher>,
"Driver must contain a constructor with the signature '(driver::DriverStartArgs, "
"fdf::UnownedDispatcher)' in order to be used with the BasicFactory.");
public:
static zx::status<std::unique_ptr<DriverBase>> CreateDriver(
DriverStartArgs start_args, fdf::UnownedDispatcher driver_dispatcher) {
std::unique_ptr<DriverBase> driver =
std::make_unique<Driver>(std::move(start_args), std::move(driver_dispatcher));
auto result = driver->Start();
if (result.is_error()) {
FDF_LOGL(WARNING, driver->logger(), "Failed to Start driver: %s", result.status_string());
return result.take_error();
}
return zx::ok(std::move(driver));
}
};
// |Record| implements static |Start| and |Stop| methods which will be used by the framework.
//
// By default, it will utilize |BasicFactory| to construct your primary driver class, |Driver|,
// and invoke it's |Start| and |Stop| methods.
//
// |Driver| must inherit from |DriverBase|. If provided, |Factory| must implement a
// public |CreateDriver| function with the following signature:
// ```
// static zx::status<std::unique_ptr<DriverBase>> CreateDriver(
// DriverStartArgs start_args, fdf::UnownedDispatcher driver_dispatcher)
// ```
//
// This illustrates how to use a |Record| with the default |BasicFactory|:
// ```
// FUCHSIA_DRIVER_RECORD_CPP_V2(driver::Record<MyDriver>);
// ```
//
// This illustrates how to use a |Record| with a custom factory:
// ```
// class CustomFactory {
// public:
// static zx::status<std::unique_ptr<DriverBase>> CreateDriver(
// DriverStartArgs start_args, fdf::UnownedDispatcher driver_dispatcher)
// ...construct and start driver...
// };
// // We must define the record before passing into the macro, otherwise the macro expansion
// // will think the comma is to pass a second macro argument.
// using record = driver::Record<MyDriver, CustomFactory>;
// FUCHSIA_DRIVER_RECORD_CPP_V2(record);
// ```
template <typename Driver, typename Factory = BasicFactory<Driver>>
class Record {
static_assert(std::is_base_of_v<DriverBase, Driver>, "Driver has to inherit from DriverBase");
DECLARE_HAS_MEMBER_FN(has_create_driver, CreateDriver);
static_assert(has_create_driver_v<Factory>,
"Factory must implement a public static CreateDriver function.");
static_assert(
std::is_same_v<decltype(&Factory::CreateDriver),
zx::status<std::unique_ptr<DriverBase>> (*)(
DriverStartArgs start_args, fdf::UnownedDispatcher driver_dispatcher)>,
"CreateDriver must be a public static function with signature "
"'zx::status<std::unique_ptr<driver::DriverBase>> (driver::DriverStartArgs start_args, "
"fdf::UnownedDispatcher driver_dispatcher)'.");
public:
static zx_status_t Start(EncodedDriverStartArgs encoded_start_args, fdf_dispatcher_t* dispatcher,
void** driver) {
// Decode the incoming `msg`.
auto wire_format_metadata =
fidl::WireFormatMetadata::FromOpaque(encoded_start_args.wire_format_metadata);
fitx::result start_args = fidl::Decode<fuchsia_driver_framework::DriverStartArgs>(
fidl::EncodedMessage::FromEncodedCMessage(encoded_start_args.msg), wire_format_metadata);
if (!start_args.is_ok()) {
return start_args.error_value().status();
}
zx::status<std::unique_ptr<DriverBase>> created_driver =
Factory::CreateDriver(std::move(*start_args), fdf::UnownedDispatcher(dispatcher));
if (created_driver.is_error()) {
return created_driver.status_value();
}
// Store `driver` pointer.
*driver = (*created_driver).release();
return ZX_OK;
}
static zx_status_t Stop(void* driver) {
DriverBase* casted_driver = static_cast<DriverBase*>(driver);
casted_driver->Stop();
delete casted_driver;
return ZX_OK;
}
};
#define FUCHSIA_DRIVER_RECORD_CPP_V2(record) \
FUCHSIA_DRIVER_RECORD_V1(.start = record::Start, .stop = record::Stop)
} // namespace driver
#endif // LIB_DRIVER2_DRIVER_BASE_H_