blob: d6b409c742c9f84a32157cbb767c4db14b94b00b [file] [log] [blame]
// Copyright 2021 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 SRC_DEVICES_MISC_DRIVERS_COMPAT_DEVICE_H_
#define SRC_DEVICES_MISC_DRIVERS_COMPAT_DEVICE_H_
#include <fidl/fuchsia.device.manager/cpp/wire.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.driver.compat/cpp/wire.h>
#include <fidl/fuchsia.driver.framework/cpp/wire.h>
#include <lib/async/cpp/executor.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <lib/driver/compat/cpp/compat.h>
#include <lib/driver/compat/cpp/symbols.h>
#include <lib/driver/devfs/cpp/connector.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/fdf/cpp/channel.h>
#include <lib/fpromise/bridge.h>
#include <lib/fpromise/scope.h>
#include <lib/inspect/component/cpp/component.h>
#include <lib/sync/cpp/completion.h>
#include <list>
#include <memory>
#include <unordered_map>
#include "src/devices/lib/fidl/device_server.h"
namespace compat {
constexpr char kDfv2Variable[] = "IS_DFV2";
// The DFv1 ops: zx_protocol_device_t.
constexpr char kOps[] = "compat-ops";
class Driver;
// Device is an implementation of a DFv1 device.
class Device : public std::enable_shared_from_this<Device>, public devfs_fidl::DeviceInterface {
// Forward declaration.
struct DelayedReleaseOp;
public:
Device(device_t device, const zx_protocol_device_t* ops, Driver* driver,
std::optional<Device*> parent, std::shared_ptr<fdf::Logger> logger,
async_dispatcher_t* dispatcher);
~Device() override;
zx_device_t* ZxDevice();
// Binds a device to a DFv2 node.
void Bind(fidl::WireSharedClient<fuchsia_driver_framework::Node> node);
// Unbinds a device from a DFv2 node.
void Unbind();
// Call Unbind or Suspend based on the power state.
fpromise::promise<void> HandleStopSignal();
// Call the Unbind op for the device.
fpromise::promise<void> UnbindOp();
// Call the Suspend op for the device.
fpromise::promise<void> SuspendOp();
// Removes all of the child devices.
fpromise::promise<void> RemoveChildren();
// Suspends all of the child devices.
fpromise::promise<void> SuspendChildren();
const char* Name() const;
bool HasChildren() const;
// Functions to implement the DFv1 device API.
zx_status_t Add(device_add_args_t* zx_args, zx_device_t** out);
// Remove this device. This call will make sure that DFv1 unbind and release
// are called in the correct order. This promise will finish once the device
// has been completely removed.
fpromise::promise<void> Remove();
zx_status_t GetProtocol(uint32_t proto_id, void* out) const;
zx_status_t GetFragmentProtocol(const char* fragment, uint32_t proto_id, void* out);
zx_status_t AddMetadata(uint32_t type, const void* data, size_t size);
zx_status_t GetMetadata(uint32_t type, void* buf, size_t buflen, size_t* actual);
zx_status_t GetMetadataSize(uint32_t type, size_t* out_size);
void InitReply(zx_status_t status);
zx_status_t ConnectFragmentFidl(const char* fragment_name, const char* service_name,
const char* protocol_name, zx::channel request);
zx_status_t AddCompositeNodeSpec(const char* name, const composite_node_spec_t* spec);
// Connects to the runtime service using the v2 protocol discovery with tokens.
zx_status_t ConnectFragmentRuntime(const char* fragment_name, const char* service_name,
const char* protocol_name, fdf::Channel request);
zx_status_t ConnectNsProtocol(const char* protocol_name, zx::channel request);
fpromise::promise<void, zx_status_t> WaitForInitToComplete();
zx_status_t CreateNode();
void CompleteUnbind();
void CompleteSuspend();
zx_status_t PublishInspect(zx::vmo inspect_vmo);
// Stores the child's release op, to be called after dispatcher shutdown.
void AddDelayedChildReleaseOp(std::unique_ptr<DelayedReleaseOp> op);
std::string_view topological_path() const { return topological_path_; }
void set_topological_path(std::string path) { topological_path_ = std::move(path); }
void set_fragments(std::vector<std::string> names) { fragments_ = std::move(names); }
Driver* driver() { return driver_; }
async_dispatcher_t* dispatcher() { return dispatcher_; }
fpromise::scope& scope() { return scope_; }
fdf::Logger& logger() { return *logger_; }
async::Executor& executor() { return executor_; }
DeviceServer& device_server() { return device_server_; }
devfs_fidl::DeviceServer& devfs_server() { return devfs_server_; }
void set_logger(std::shared_ptr<fdf::Logger> logger) { logger_ = std::move(logger); }
const std::vector<std::string>& fragments() { return fragments_; }
// Public for testing.
std::optional<Device*>& parent() { return parent_; }
private:
// Holds the device's release op to call later.
struct DelayedReleaseOp {
device_t compat_symbol;
const zx_protocol_device_t* ops;
explicit DelayedReleaseOp(std::shared_ptr<Device> device);
~DelayedReleaseOp();
};
Device(Device&&) = delete;
Device& operator=(Device&&) = delete;
// This function will export everything about the device. Meaning it will:
// - Add the device's capabilities into the outgoing directory.
// - Export the device to devfs.
// - Create the device's Node.
// This must only be called after the device's init has finished.
zx_status_t ExportAfterInit();
// device_fidl::DeviceInterface APIs.
void LogError(const char* error) override;
bool IsUnbound() override;
bool MessageOp(fidl::IncomingHeaderAndMessage msg, device_fidl_txn_t txn) override;
void ConnectToDeviceFidl(ConnectToDeviceFidlRequestView request,
ConnectToDeviceFidlCompleter::Sync& completer) override;
void ConnectToController(ConnectToControllerRequestView request,
ConnectToControllerCompleter::Sync& completer) override;
void Bind(BindRequestView request, BindCompleter::Sync& completer) override;
void Rebind(RebindRequestView request, RebindCompleter::Sync& completer) override;
void UnbindChildren(UnbindChildrenCompleter::Sync& completer) override;
void ScheduleUnbind(ScheduleUnbindCompleter::Sync& completer) override;
void GetTopologicalPath(GetTopologicalPathCompleter::Sync& completer) override;
// This calls Unbind on the device and then frees it.
void UnbindAndRelease();
void InsertOrUpdateProperty(fuchsia_driver_framework::wire::NodePropertyKey key,
fuchsia_driver_framework::wire::NodePropertyValue value);
std::string OutgoingName();
bool ShouldCallRelease() {
// We only shut down the devices that have a parent, since that means that *this* compat driver
// owns the device. If the device does not have a parent, then ops_ belongs to another driver,
// and it's that driver's responsibility to be shut down. We purposefully leak in
// shutdown/reboot flows to emulate DFv1 shutdown. The fdf::Node client should have been torn
// down by the driver runtime canceling all outstanding waits by the time stop has been called,
// allowing shutdown to proceed.
return parent_ && system_power_state() == fuchsia_device_manager::SystemPowerState::kFullyOn;
}
bool HasChildNamed(std::string_view name) const;
fuchsia_device_manager::wire::SystemPowerState system_power_state() const;
bool stop_triggered() const;
// This arena backs `properties_`.
// This should be declared before any objects it backs so it is destructed last.
fidl::Arena<512> arena_;
std::vector<fuchsia_driver_framework::wire::NodeProperty> properties_;
std::optional<driver_devfs::Connector<fuchsia_device::Controller>> devfs_connector_;
std::optional<driver_devfs::Connector<fuchsia_device::Controller>> devfs_controller_connector_;
fidl::ServerBindingGroup<fuchsia_device::Controller> dev_controller_bindings_;
devfs_fidl::DeviceServer devfs_server_;
DeviceServer device_server_;
// This is the device's topological path without the leading '/dev/'.
// TODO(https://fxbug.dev/42068361): Simplify this and the GetTopologicalPath API.
std::string topological_path_;
const std::string name_;
// A unique id for the device.
uint32_t device_id_ = 0;
std::shared_ptr<fdf::Logger> logger_;
async_dispatcher_t* const dispatcher_;
uint32_t device_flags_ = 0;
std::vector<std::string> fragments_;
// This device's driver. The driver owns all of its Device objects, so it
// is garaunteed to outlive the Device.
Driver* driver_ = nullptr;
std::mutex init_lock_;
bool init_is_finished_ __TA_GUARDED(init_lock_) = false;
zx_status_t init_status_ __TA_GUARDED(init_lock_) = ZX_OK;
std::vector<fpromise::completer<void, zx_status_t>> init_waiters_ __TA_GUARDED(init_lock_);
bool pending_removal_ = false;
// Completed when unbind is replied to.
fpromise::completer<void> unbind_completer_;
// Completed when suspend is replied to.
fpromise::completer<void> suspend_completer_;
// The default protocol of the device.
device_t compat_symbol_;
const zx_protocol_device_t* ops_;
// A list of completers for promises that are waiting for this device to be
// removed.
std::vector<fpromise::completer<void>> remove_completers_;
std::optional<fpromise::promise<>> controller_teardown_finished_;
// The device's parent. If this field is set then the Device ptr is guaranteed
// to be non-null. The parent is also guaranteed to outlive its child.
//
// This is used by a Device to free itself, by calling parent_.RemoveChild(this).
//
// parent_ will be std::nullopt when the Device is the fake device created
// by the Driver class in the DFv1 shim. It is also std::nullopt when it is the last child of
// the root device. When parent_ is std::nullopt, the Device will be freed when the Driver is
// freed.
fidl::WireSharedClient<fuchsia_driver_framework::Node> node_;
fidl::WireSharedClient<fuchsia_driver_framework::NodeController> controller_;
std::optional<Device*> parent_;
// If true, the release op will be called after the dispatcher has been shutdown.
// This is to keep with DFv1 behavior which only applies to the last remaining
// device of a driver.
bool release_after_dispatcher_shutdown_ = false;
// These will automatically destruct when this device
// (the fake compat device created by the Driver class in the DFv1 shim) destructs.
std::vector<std::unique_ptr<DelayedReleaseOp>> delayed_child_release_ops_;
// The Device's children. The Device has full ownership of the children,
// but these are shared pointers so that the NodeController can get a weak
// pointer to the child in order to erase them.
std::list<std::shared_ptr<Device>> children_;
async::Executor executor_;
// File representing the device's inspect vmo, if any.
std::optional<zx::vmo> inspect_vmo_;
// NOTE: Must be the last member.
fpromise::scope scope_;
};
std::vector<fuchsia_driver_framework::wire::NodeProperty> CreateProperties(
fidl::AnyArena& arena, fdf::Logger& logger, device_add_args_t* zx_args);
} // namespace compat
struct zx_device : public compat::Device {
// NOTE: Intentionally empty, do not add to this.
};
#endif // SRC_DEVICES_MISC_DRIVERS_COMPAT_DEVICE_H_