| // 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_DRIVER_H_ |
| #define SRC_DEVICES_MISC_DRIVERS_COMPAT_DRIVER_H_ |
| |
| #include <fidl/fuchsia.boot/cpp/wire.h> |
| #include <fidl/fuchsia.kernel/cpp/wire.h> |
| #include <fidl/fuchsia.scheduler/cpp/markers.h> |
| #include <lib/async/cpp/executor.h> |
| #include <lib/driver/compat/cpp/compat.h> |
| #include <lib/driver/component/cpp/driver_base.h> |
| #include <lib/driver/devfs/cpp/connector.h> |
| #include <lib/fdf/cpp/dispatcher.h> |
| #include <lib/fpromise/scope.h> |
| #include <lib/inspect/component/cpp/component.h> |
| |
| #include <unordered_set> |
| |
| #include "src/devices/misc/drivers/compat/device.h" |
| |
| extern std::mutex kDriverGlobalsLock; |
| |
| namespace compat { |
| |
| // Driver is the compatibility driver that loads DFv1 drivers. |
| class Driver : public fdf::DriverBase { |
| using Base = fdf::DriverBase; |
| |
| public: |
| Driver(fdf::DriverStartArgs start_args, zx::vmo config_vmo, |
| fdf::UnownedSynchronizedDispatcher driver_dispatcher, device_t device, |
| const zx_protocol_device_t* ops, std::string_view driver_path); |
| ~Driver() override; |
| |
| void Start(fdf::StartCompleter completer) override; |
| void PrepareStop(fdf::PrepareStopCompleter completer) override; |
| |
| // Returns the context that DFv1 driver provided. |
| void* Context() const; |
| |
| zx::result<zx::vmo> LoadFirmware(Device* device, const char* filename, size_t* size); |
| |
| zx_handle_t GetInfoResource(); |
| |
| zx_handle_t GetIommuResource(); |
| |
| zx_handle_t GetFramebufferResource(); |
| |
| zx_handle_t GetMmioResource(); |
| |
| zx_handle_t GetMsiResource(); |
| |
| zx_handle_t GetPowerResource(); |
| |
| zx_handle_t GetIoportResource(); |
| |
| zx_handle_t GetIrqResource(); |
| |
| zx_handle_t GetSmcResource(); |
| |
| zx::vmo& GetConfigVmo(); |
| |
| zx_status_t GetProperties(device_props_args_t* out_args, |
| const std::string& parent_node_name = "default"); |
| |
| // # Threading notes |
| // |
| // If this method is not called from a task running on |dispatcher|, |
| // this method will schedule its work on that dispatcher then block until it |
| // is done. |
| zx_status_t AddDevice(Device* parent, device_add_args_t* args, zx_device_t** out); |
| zx::result<> SetProfileByRole(zx::unowned_thread thread, std::string_view role); |
| zx::result<std::string> GetVariable(const char* name); |
| |
| zx_status_t GetProtocol(uint32_t proto_id, void* out); |
| zx_status_t GetFragmentProtocol(const char* fragment, uint32_t proto_id, void* out); |
| |
| Device& GetDevice() { return device_; } |
| |
| void CompleteStart(zx::result<> result); |
| |
| // These accessors are used by other classes in the compat driver so we want to expose |
| // them publicly since they are protected in DriverBase. |
| async_dispatcher_t* dispatcher() { return Base::dispatcher(); } |
| const async_dispatcher_t* dispatcher() const { return Base::dispatcher(); } |
| const fdf::Namespace& driver_namespace() { return *incoming(); } |
| fdf::OutgoingDirectory& outgoing() { return *Base::outgoing(); } |
| |
| uint32_t GetNextDeviceId() { return next_device_id_++; } |
| |
| const std::string& driver_path() const { return driver_path_; } |
| |
| fuchsia_device_manager::wire::SystemPowerState system_state() const { return system_state_; } |
| |
| bool stop_triggered() const { return stop_triggered_; } |
| |
| private: |
| bool IsComposite(); |
| |
| bool IsRunningOnDispatcher() const; |
| |
| // If the current thread is running a task from our dispatcher, calls |task| |
| // synchronously. Otherwise, schedules |task| onto our dispatcher and blocks |
| // to receive its result. |
| zx_status_t RunOnDispatcher(fit::callback<zx_status_t()> task); |
| |
| // Loads the driver using the provided `vmos`. |
| zx::result<> LoadDriver(zx::vmo loader_vmo, zx::vmo driver_vmo); |
| // Starts the DFv1 driver. |
| zx::result<> StartDriver(); |
| |
| // Attempts to trigger run_unit_tests driver hook if they are enabled. |
| zx::result<> TryRunUnitTests(); |
| |
| fpromise::promise<void, zx_status_t> ConnectToParentDevices(); |
| fpromise::promise<void, zx_status_t> GetDeviceInfo(); |
| |
| bool ShouldCallRelease() { |
| // 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 record_ != nullptr && record_->ops->release != nullptr && |
| system_state_ == fuchsia_device_manager::SystemPowerState::kFullyOn; |
| } |
| |
| async::Executor executor_; |
| |
| std::shared_ptr<fdf::Logger> inner_logger_; |
| |
| std::string driver_path_; |
| std::string driver_name_; |
| Device device_; |
| |
| fuchsia_device_manager::wire::SystemPowerState system_state_ = |
| fuchsia_device_manager::wire::SystemPowerState::kFullyOn; |
| bool stop_triggered_ = false; |
| |
| // The next unique device id for devices. Starts at 1 because `device_` has id zero. |
| uint32_t next_device_id_ = 1; |
| |
| void* library_ = nullptr; |
| zx_driver_rec_t* record_ = nullptr; |
| void* context_ = nullptr; |
| |
| std::optional<fdf::StartCompleter> start_completer_; |
| |
| // API resources. |
| zx::resource root_resource_; |
| zx::resource mmio_resource_; |
| zx::resource msi_resource_; |
| zx::resource power_resource_; |
| zx::resource ioport_resource_; |
| zx::resource irq_resource_; |
| zx::resource smc_resource_; |
| zx::resource info_resource_; |
| zx::resource iommu_resource_; |
| zx::resource framebuffer_resource_; |
| zx::vmo config_vmo_; |
| |
| fidl::WireClient<fuchsia_driver_compat::Device> parent_client_; |
| std::unordered_map<std::string, fidl::WireClient<fuchsia_driver_compat::Device>> parent_clients_; |
| |
| fdf::async_helpers::TaskGroup async_tasks_; |
| |
| // NOTE: Must be the last member. |
| fpromise::scope scope_; |
| }; |
| |
| // |GlobalLoggerList| is global for the entire driver host process. It maintains a |
| // |LoggerInstances| for each driver_path that is active. |
| class GlobalLoggerList { |
| friend ::zx_driver; |
| // |LoggerInstances| is shared for all drivers with the same driver_path. The |loggers_| set |
| // contains all the active instances. |
| class LoggerInstances { |
| public: |
| explicit LoggerInstances(bool log_node_names) : log_node_names_(log_node_names) {} |
| // Logs a message for the DFv1 driver. |
| void Log(FuchsiaLogSeverity severity, const char* tag, const char* file, int line, |
| const char* msg, va_list args); |
| zx_driver_t* ZxDriver(); |
| void AddLogger(std::shared_ptr<fdf::Logger>& logger, |
| const std::optional<std::string>& node_name); |
| void RemoveLogger(std::shared_ptr<fdf::Logger>& logger, |
| const std::optional<std::string>& node_name); |
| |
| size_t count() { return loggers_.size(); } |
| const std::vector<std::string>& node_names_for_testing() { return node_names_; } |
| |
| private: |
| bool log_node_names_; |
| std::unordered_set<std::shared_ptr<fdf::Logger>> loggers_; |
| std::vector<std::string> node_names_; |
| }; |
| |
| public: |
| explicit GlobalLoggerList(bool log_node_names) : log_node_names_(log_node_names) {} |
| zx_driver_t* AddLogger(const std::string& driver_path, std::shared_ptr<fdf::Logger>& logger, |
| const std::optional<std::string>& node_name); |
| void RemoveLogger(const std::string& driver_path, std::shared_ptr<fdf::Logger>& logger, |
| const std::optional<std::string>& node_name); |
| std::optional<size_t> loggers_count_for_testing(const std::string& driver_path); |
| |
| private: |
| bool log_node_names_; |
| std::unordered_map<std::string, LoggerInstances> instances_; |
| }; |
| |
| } // namespace compat |
| |
| struct zx_driver : public compat::GlobalLoggerList::LoggerInstances { |
| // NOTE: Intentionally empty, do not add to this. |
| }; |
| |
| #endif // SRC_DEVICES_MISC_DRIVERS_COMPAT_DRIVER_H_ |