| // Copyright 2019 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_BIN_DRIVER_MANAGER_DEVICE_H_ |
| #define SRC_DEVICES_BIN_DRIVER_MANAGER_DEVICE_H_ |
| |
| #include <fidl/fuchsia.device.manager/cpp/wire.h> |
| #include <fidl/fuchsia.driver.development/cpp/wire.h> |
| #include <fidl/fuchsia.io/cpp/wire.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/async/cpp/wait.h> |
| #include <lib/ddk/device.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/event.h> |
| |
| #include <memory> |
| #include <variant> |
| |
| #include <fbl/array.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/mutex.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/string.h> |
| |
| #include "src/devices/bin/driver_manager/composite_device.h" |
| #include "src/devices/bin/driver_manager/inspect.h" |
| #include "src/devices/bin/driver_manager/metadata.h" |
| #include "src/lib/storage/vfs/cpp/vmo_file.h" |
| |
| namespace fio = fuchsia_io; |
| |
| class Coordinator; |
| class DriverHost; |
| struct Devnode; |
| class InitTask; |
| class RemoveTask; |
| class SuspendTask; |
| class ResumeTask; |
| class UnbindTask; |
| struct UnbindTaskOpts; |
| |
| // clang-format off |
| |
| // This device is never destroyed |
| #define DEV_CTX_IMMORTAL static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kImmortal) |
| |
| // This device requires that children are created in a |
| // new driver_host attached to a proxy device |
| #define DEV_CTX_MUST_ISOLATE static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kMustIsolate) |
| |
| // This device may be bound multiple times |
| #define DEV_CTX_MULTI_BIND static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kMultiBind) |
| |
| // This device is bound and not eligible for binding |
| // again until unbound. Not allowed on MULTI_BIND ctx. |
| #define DEV_CTX_BOUND static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kBound) |
| |
| // Device has been remove()'d |
| #define DEV_CTX_DEAD static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kDead) |
| |
| // This device is a fragment of a composite device and |
| // can be part of multiple composite devices. |
| #define DEV_CTX_ALLOW_MULTI_COMPOSITE static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kAllowMultiComposite) |
| |
| // Device is a proxy -- its "parent" is the device it's |
| // a proxy to. |
| #define DEV_CTX_PROXY static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kProxy) |
| |
| // Device is not visible in devfs or bindable. |
| // Devices may be created in this state, but may not |
| // return to this state once made visible. |
| #define DEV_CTX_INVISIBLE static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kInvisible) |
| |
| // Device should not go through auto-bind process |
| #define DEV_CTX_SKIP_AUTOBIND static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kSkipAutobind) |
| |
| // Device is a bus device. |
| #define DEV_CTX_BUS_DEVICE static_cast<uint32_t>(fuchsia_driver_development::DeviceFlags::kBusDevice) |
| |
| // clang-format on |
| |
| // Tags used for container membership identification |
| namespace internal { |
| struct DeviceChildListTag {}; |
| struct DeviceDriverHostListTag {}; |
| struct DeviceAllDevicesListTag {}; |
| } // namespace internal |
| |
| class Device |
| : public fbl::RefCounted<Device>, |
| public fidl::WireServer<fuchsia_device_manager::Coordinator>, |
| public fbl::ContainableBaseClasses< |
| fbl::TaggedDoublyLinkedListable<Device*, internal::DeviceChildListTag>, |
| fbl::TaggedDoublyLinkedListable<Device*, internal::DeviceDriverHostListTag>, |
| fbl::TaggedDoublyLinkedListable<fbl::RefPtr<Device>, internal::DeviceAllDevicesListTag>> { |
| public: |
| using ChildListTag = internal::DeviceChildListTag; |
| using DriverHostListTag = internal::DeviceDriverHostListTag; |
| using AllDevicesListTag = internal::DeviceAllDevicesListTag; |
| |
| void AddDevice(AddDeviceRequestView request, AddDeviceCompleter::Sync& _completer) override; |
| void ScheduleRemove(ScheduleRemoveRequestView request, |
| ScheduleRemoveCompleter::Sync& _completer) override; |
| void AddCompositeDevice(AddCompositeDeviceRequestView request, |
| AddCompositeDeviceCompleter::Sync& _completer) override; |
| void AddDeviceGroup(AddDeviceGroupRequestView request, |
| AddDeviceGroupCompleter::Sync& _completer) override; |
| void BindDevice(BindDeviceRequestView request, BindDeviceCompleter::Sync& _completer) override; |
| void GetTopologicalPath(GetTopologicalPathRequestView request, |
| GetTopologicalPathCompleter::Sync& _completer) override; |
| void LoadFirmware(LoadFirmwareRequestView request, |
| LoadFirmwareCompleter::Sync& _completer) override; |
| void GetMetadata(GetMetadataRequestView request, GetMetadataCompleter::Sync& _completer) override; |
| void GetMetadataSize(GetMetadataSizeRequestView request, |
| GetMetadataSizeCompleter::Sync& _completer) override; |
| void AddMetadata(AddMetadataRequestView request, AddMetadataCompleter::Sync& _completer) override; |
| void ScheduleUnbindChildren(ScheduleUnbindChildrenRequestView request, |
| ScheduleUnbindChildrenCompleter::Sync& _completer) override; |
| |
| // This iterator provides access to a list of devices that does not provide |
| // mechanisms for mutating that list. With this, a user can get mutable |
| // access to a device in the list. This is achieved by making the linked |
| // list iterator opaque. It is not safe to modify the underlying list while |
| // this iterator is in use. |
| template <typename ChildIterType, typename FragmentIterType, typename DeviceType> |
| class ChildListIterator { |
| public: |
| ChildListIterator() : state_(Done{}) {} |
| explicit ChildListIterator(DeviceType* device) |
| : state_(device->children_.begin()), device_(device) { |
| SkipInvalidStates(); |
| } |
| ChildListIterator operator++(int) { |
| auto other = *this; |
| ++*this; |
| return other; |
| } |
| bool operator==(const ChildListIterator& other) const { return state_ == other.state_; } |
| bool operator!=(const ChildListIterator& other) const { return !(state_ == other.state_); } |
| |
| // The iterator implementation for the child list. This is the source of truth |
| // for what devices are children of the device. |
| ChildListIterator& operator++() { |
| std::visit( |
| [this](auto&& arg) { |
| using T = std::decay_t<decltype(arg)>; |
| if constexpr (std::is_same_v<T, ChildIterType> || std::is_same_v<T, FragmentIterType>) { |
| ++arg; |
| } else if constexpr (std::is_same_v<T, Done>) { |
| state_ = Done{}; |
| } |
| }, |
| state_); |
| SkipInvalidStates(); |
| return *this; |
| } |
| |
| DeviceType& operator*() const { |
| return std::visit( |
| [](auto&& arg) -> DeviceType& { |
| using T = std::decay_t<decltype(arg)>; |
| if constexpr (std::is_same_v<T, ChildIterType>) { |
| return *arg; |
| } else if constexpr (std::is_same_v<T, FragmentIterType>) { |
| return *(arg->composite()->device()); |
| } else { |
| __builtin_trap(); |
| } |
| }, |
| state_); |
| } |
| |
| private: |
| // Advance the iterator to the next valid state or reach the done state. |
| // This is used to handle advancement between the different state variants. |
| void SkipInvalidStates() { |
| bool more = true; |
| while (more) { |
| more = std::visit( |
| [this](auto&& arg) { |
| using T = std::decay_t<decltype(arg)>; |
| if constexpr (std::is_same_v<T, ChildIterType>) { |
| // Check if there are any more children in the list. If |
| // there are, we're in a valid state and can stop. |
| // Otherwise, advance to the next variant and check if |
| // it's a valid state. |
| if (arg != device_->children_.end()) { |
| return false; |
| } |
| |
| // If there are no more children and this is a fragment device, |
| // find children of the fragment device by looking at its parent's |
| // fragment list. |
| if (device_->libname() == device_->coordinator->GetFragmentDriverUrl()) { |
| if (device_->parent_) { |
| state_ = FragmentIterType{device_->parent()->fragments_.begin()}; |
| return true; |
| } |
| state_ = Done{}; |
| return false; |
| } |
| |
| // Some composite devices are added directly as fragments without |
| // a proxy fragment device. These don't appear in the children list. |
| state_ = FragmentIterType{device_->fragments_.begin()}; |
| return true; |
| } else if constexpr (std::is_same_v<T, FragmentIterType>) { |
| if (device_->libname() == device_->coordinator->GetFragmentDriverUrl()) { |
| // This device is an internal fragment device. |
| if (arg == device_->parent()->fragments_.end()) { |
| state_ = Done{}; |
| return false; |
| } |
| |
| // Skip composite devices that aren't yet bound. |
| if (arg->composite()->device() == nullptr) { |
| state_ = FragmentIterType{++arg}; |
| return true; |
| } |
| |
| // Skip any fragments that aren't bound to this fragment device. |
| if (arg->fragment_device().get() != device_) { |
| state_ = FragmentIterType{++arg}; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Check for composite devices that don't have proxy fragment devices. |
| |
| if (arg == device_->fragments_.end()) { |
| state_ = Done{}; |
| return false; |
| } |
| |
| // Skip composite devices that aren't yet bound. |
| if (arg->composite()->device() == nullptr) { |
| state_ = FragmentIterType{++arg}; |
| return true; |
| } |
| |
| // Skip fragments that have a fragment device. |
| if (arg->fragment_device() != nullptr) { |
| state_ = FragmentIterType{++arg}; |
| return true; |
| } |
| |
| return false; |
| } else if constexpr (std::is_same_v<T, Done>) { |
| return false; |
| } |
| }, |
| state_); |
| } |
| } |
| struct Done { |
| bool operator==(Done) const { return true; } |
| }; |
| std::variant<ChildIterType, FragmentIterType, Done> state_; |
| DeviceType* device_; |
| }; |
| |
| // This class exists to allow consumers of the Device class to write |
| // for (auto& child : dev->children()) |
| // and get mutable access to the children without getting mutable access to |
| // the list. |
| template <typename DeviceType, typename IterType> |
| class ChildListIteratorFactory { |
| public: |
| explicit ChildListIteratorFactory(DeviceType* device) : device_(device) {} |
| |
| IterType begin() const { return IterType(device_); } |
| IterType end() const { return IterType(); } |
| |
| bool is_empty() const { return begin() == end(); } |
| |
| private: |
| DeviceType* device_; |
| }; |
| |
| Device(Coordinator* coord, fbl::String name, fbl::String libname, fbl::String args, |
| fbl::RefPtr<Device> parent, uint32_t protocol_id, zx::vmo inspect, |
| zx::channel client_remote, fidl::ClientEnd<fio::Directory> outgoing_dir); |
| ~Device(); |
| |
| // Create a new device with the given parameters. This sets up its |
| // relationship with its parent and driver_host and adds its RPC channel to the |
| // coordinator's async loop. This does not add the device to the |
| // coordinator's devices_ list, or trigger publishing |
| static zx_status_t Create( |
| Coordinator* coordinator, const fbl::RefPtr<Device>& parent, fbl::String name, |
| fbl::String driver_path, fbl::String args, uint32_t protocol_id, |
| fbl::Array<zx_device_prop_t> props, fbl::Array<StrProperty> str_props, |
| fidl::ServerEnd<fuchsia_device_manager::Coordinator> coordinator_request, |
| fidl::ClientEnd<fuchsia_device_manager::DeviceController> device_controller, |
| bool want_init_task, bool skip_autobind, zx::vmo inspect, zx::channel client_remote, |
| fidl::ClientEnd<fio::Directory> outgoing_dir, fbl::RefPtr<Device>* device); |
| static zx_status_t CreateComposite( |
| Coordinator* coordinator, fbl::RefPtr<DriverHost> driver_host, |
| const CompositeDevice& composite, |
| fidl::ServerEnd<fuchsia_device_manager::Coordinator> coordinator_request, |
| fidl::ClientEnd<fuchsia_device_manager::DeviceController> device_controller, |
| fbl::RefPtr<Device>* device); |
| zx_status_t CreateProxy(); |
| zx_status_t CreateNewProxy(); |
| |
| static void Bind(fbl::RefPtr<Device> dev, async_dispatcher_t*, |
| fidl::ServerEnd<fuchsia_device_manager::Coordinator>); |
| |
| // We do not want to expose the list itself for mutation, even if the |
| // children are allowed to be mutated. We manage this by making the |
| // iterator opaque. |
| using NonConstChildListIterator = ChildListIterator< |
| fbl::TaggedDoublyLinkedList<Device*, ChildListTag>::iterator, |
| fbl::TaggedDoublyLinkedList<CompositeDeviceFragment*, |
| CompositeDeviceFragment::DeviceListTag>::iterator, |
| Device>; |
| using ConstChildListIterator = ChildListIterator< |
| fbl::TaggedDoublyLinkedList<Device*, ChildListTag>::const_iterator, |
| fbl::TaggedDoublyLinkedList<CompositeDeviceFragment*, |
| CompositeDeviceFragment::DeviceListTag>::const_iterator, |
| const Device>; |
| |
| using NonConstFragmentListIterator = |
| fbl::TaggedDoublyLinkedList<CompositeDeviceFragment*, |
| CompositeDeviceFragment::DeviceListTag>::iterator; |
| |
| using NonConstChildListIteratorFactory = |
| ChildListIteratorFactory<Device, NonConstChildListIterator>; |
| using ConstChildListIteratorFactory = |
| ChildListIteratorFactory<const Device, ConstChildListIterator>; |
| NonConstChildListIteratorFactory children() { return NonConstChildListIteratorFactory(this); } |
| ConstChildListIteratorFactory children() const { return ConstChildListIteratorFactory(this); } |
| |
| // Signal that this device is ready for bind to happen. This should happen |
| // either immediately after the device is created, if it's created visible, |
| // or after it becomes visible. |
| zx_status_t SignalReadyForBind(zx::duration delay = zx::sec(0)); |
| |
| using InitCompletion = fit::callback<void(zx_status_t)>; |
| // Issue an Init request to this device. When the response comes in, the |
| // given completion will be invoked. |
| void SendInit(InitCompletion completion); |
| |
| using SuspendCompletion = fit::callback<void(zx_status_t)>; |
| // Issue a Suspend request to this device. When the response comes in, the |
| // given completion will be invoked. |
| void SendSuspend(uint32_t flags, SuspendCompletion completion); |
| |
| using ResumeCompletion = fit::callback<void(zx_status_t)>; |
| // Issue a Resume request to this device. When the response comes in, the |
| // given completion will be invoked. |
| void SendResume(uint32_t target_system_state, ResumeCompletion completion); |
| |
| using UnbindCompletion = fit::callback<void(zx_status_t)>; |
| using RemoveCompletion = fit::callback<void(zx_status_t)>; |
| // Issue an Unbind request to this device, which will run the unbind hook. |
| // When the response comes in, the given completion will be invoked. |
| // If successful, returns ZX_OK and takes ownership of |completion|. |
| void SendUnbind(UnbindCompletion& completion); |
| // Issue a CompleteRemove request to this device. |
| // When the response comes in, the given completion will be invoked. |
| // If successful, returns ZX_OK and takes ownership of |completion|. |
| void SendCompleteRemove(RemoveCompletion& completion); |
| |
| // Break the relationship between this device object and its parent |
| void DetachFromParent(); |
| |
| // Sets the properties of this device. |
| zx_status_t SetProps(fbl::Array<const zx_device_prop_t> props); |
| const fbl::Array<const zx_device_prop_t>& props() const { return props_; } |
| |
| const fbl::Array<const StrProperty>& str_props() const { return str_props_; } |
| zx_status_t SetStrProps(fbl::Array<const StrProperty> str_props); |
| |
| const fbl::RefPtr<Device>& parent() { return parent_; } |
| fbl::RefPtr<const Device> parent() const { return parent_; } |
| |
| const fbl::RefPtr<Device>& proxy() { return proxy_; } |
| fbl::RefPtr<const Device> proxy() const { return proxy_; } |
| |
| const fbl::RefPtr<Device>& new_proxy() { return new_proxy_; } |
| fbl::RefPtr<const Device> new_proxy() const { return new_proxy_; } |
| |
| uint32_t protocol_id() const { return protocol_id_; } |
| |
| DeviceInspect& inspect() { return *inspect_; } |
| |
| bool is_bindable() const { |
| return !(flags & (DEV_CTX_BOUND | DEV_CTX_INVISIBLE)) && (state_ != Device::State::kDead); |
| } |
| |
| bool should_skip_autobind() const { return flags & DEV_CTX_SKIP_AUTOBIND; } |
| |
| bool is_visible() const { return !(flags & DEV_CTX_INVISIBLE); } |
| |
| bool is_composite_bindable() const { |
| if (flags & (DEV_CTX_DEAD | DEV_CTX_INVISIBLE | DEV_CTX_SKIP_AUTOBIND)) { |
| return false; |
| } |
| if ((flags & DEV_CTX_BOUND) && !(flags & DEV_CTX_ALLOW_MULTI_COMPOSITE)) { |
| return false; |
| } |
| return true; |
| } |
| |
| void push_fragment(CompositeDeviceFragment* fragment) { fragments_.push_back(fragment); } |
| bool is_fragments_empty() { return fragments_.is_empty(); } |
| |
| fbl::TaggedDoublyLinkedList<CompositeDeviceFragment*, CompositeDeviceFragment::DeviceListTag>& |
| fragments() { |
| return fragments_; |
| } |
| // If the device was created as a composite, this returns its description. |
| CompositeDevice* composite() const { |
| auto val = std::get_if<CompositeDevice*>(&composite_); |
| return val ? *val : nullptr; |
| } |
| void set_composite(CompositeDevice* composite) { |
| ZX_ASSERT(std::holds_alternative<UnassociatedWithComposite>(composite_)); |
| composite_ = composite; |
| } |
| bool is_composite() const { return composite() != nullptr; } |
| void disassociate_from_composite() { composite_ = UnassociatedWithComposite{}; } |
| |
| void set_host(fbl::RefPtr<DriverHost> host); |
| const fbl::RefPtr<DriverHost>& host() const { return host_; } |
| fbl::RefPtr<DriverHost>& host() { return host_; } |
| |
| void set_local_id(uint64_t local_id) { |
| local_id_ = local_id; |
| inspect().set_local_id(local_id); |
| } |
| uint64_t local_id() const { return local_id_; } |
| |
| const fbl::DoublyLinkedList<std::unique_ptr<Metadata>>& metadata() const { return metadata_; } |
| void AddMetadata(std::unique_ptr<Metadata> md) { metadata_.push_front(std::move(md)); } |
| |
| // Creates the init task for the device. |
| void CreateInitTask(); |
| // Returns the in-progress init task if it exists, nullptr otherwise. |
| fbl::RefPtr<InitTask> GetActiveInit() { return active_init_; } |
| |
| // Run the completion for the outstanding init, if any. |
| zx_status_t CompleteInit(zx_status_t status); |
| |
| // Returns the in-progress suspend task if it exists, nullptr otherwise. |
| fbl::RefPtr<SuspendTask> GetActiveSuspend() { return active_suspend_; } |
| // Creates a new suspend task if necessary and returns a reference to it. |
| // If one is already in-progress, a reference to it is returned instead |
| fbl::RefPtr<SuspendTask> RequestSuspendTask(uint32_t suspend_flags); |
| |
| fbl::RefPtr<ResumeTask> GetActiveResume() { return active_resume_; } |
| void SetActiveResume(fbl::RefPtr<ResumeTask> resume_task) { active_resume_ = resume_task; } |
| |
| // Request Resume task |
| fbl::RefPtr<ResumeTask> RequestResumeTask(uint32_t system_resume_state); |
| |
| // Run the completion for the outstanding suspend, if any. This method is |
| // only exposed currently because RemoveDevice is on Coordinator instead of |
| // Device. |
| void CompleteSuspend(zx_status_t status); |
| |
| // Run the completion for the outstanding resume, if any. |
| void CompleteResume(zx_status_t status); |
| |
| // Creates the unbind and remove tasks for the device if they do not already exist. |
| // |opts| is used to configure the unbind task. |
| void CreateUnbindRemoveTasks(UnbindTaskOpts opts); |
| |
| // Returns the in-progress unbind task if it exists, nullptr otherwise. |
| // Unbind tasks are used to facilitate |Unbind| requests. |
| fbl::RefPtr<UnbindTask> GetActiveUnbind() { return active_unbind_; } |
| // Returns the in-progress remove task if it exists, nullptr otherwise. |
| // Remove tasks are used to facilitate |CompleteRemove| requests. |
| fbl::RefPtr<RemoveTask> GetActiveRemove() { return active_remove_; } |
| |
| // Run the completion for the outstanding unbind, if any. |
| zx_status_t CompleteUnbind(zx_status_t status = ZX_OK); |
| // Run the completion for the outstanding remove, if any. |
| zx_status_t CompleteRemove(zx_status_t status = ZX_OK); |
| |
| // Drops the reference to the task. |
| // This should be called if the device will not send an init, suspend, unbind or remove request. |
| void DropInitTask() { active_init_ = nullptr; } |
| void DropSuspendTask() { active_suspend_ = nullptr; } |
| void DropUnbindTask() { active_unbind_ = nullptr; } |
| void DropRemoveTask() { active_remove_ = nullptr; } |
| |
| zx::channel take_client_remote() { return std::move(client_remote_); } |
| bool has_outgoing_directory() { return outgoing_dir_.is_valid(); } |
| fidl::ClientEnd<fio::Directory> take_outgoing_dir() { return std::move(outgoing_dir_); } |
| |
| const fbl::String& name() const { return name_; } |
| const fbl::String& libname() const { return libname_; } |
| const fbl::String& args() const { return args_; } |
| |
| Coordinator* coordinator; |
| uint32_t flags = 0; |
| |
| // The backoff between each driver retry. This grows exponentially. |
| zx::duration backoff = zx::msec(250); |
| // The number of retries left for the driver. |
| uint32_t retries = 4; |
| Devnode* self = nullptr; |
| Devnode* link = nullptr; |
| |
| Devnode* devnode() { return self; } |
| |
| const fbl::String& link_name() const { return link_name_; } |
| void set_link_name(fbl::String link_name) { link_name_ = std::move(link_name); } |
| |
| fbl::RefPtr<fs::VmoFile>& inspect_file() { return inspect_file_; } |
| |
| // TODO(teisenbe): We probably want more states. |
| #define STATE_VALUES(macro) \ |
| macro(kActive) \ |
| macro(kInitializing) /* The driver_host is in the process of running the device init hook.*/ \ |
| macro(kSuspending) /* The driver_host is in the process of suspending the device.*/ \ |
| macro(kSuspended) \ |
| macro(kResuming) /* The driver_host is in the process of resuming the device.*/ \ |
| macro(kResumed) /* Resume is complete. Will be marked active, after all children resume.*/ \ |
| macro( \ |
| kUnbinding) /* The driver_host is in the process of unbinding and removing the device.*/ \ |
| macro(kDead) /* The device has been remove()'d*/ |
| |
| #define MAKE_ENUM_VALUE(state) state, |
| enum class State { STATE_VALUES(MAKE_ENUM_VALUE) }; |
| #undef ENUM_VALUE |
| #define MAKE_SWITCH_STATEMENT(state) \ |
| case State::state: \ |
| return #state; |
| static std::string StateToString(State state) { |
| switch (state) { STATE_VALUES(MAKE_SWITCH_STATEMENT) } |
| } |
| #undef MAKE_SWITCH_STATEMENT |
| #undef STATE_VALUES |
| |
| void set_state(Device::State state) { |
| state_ = state; |
| inspect().set_state(StateToString(state)); |
| |
| if (state == Device::State::kDead) { |
| if (std::optional binding = std::exchange(coordinator_binding_, std::nullopt); |
| binding.has_value()) { |
| binding.value().Unbind(); |
| } |
| } |
| } |
| |
| State state() const { return state_; } |
| |
| void inc_num_removal_attempts() { num_removal_attempts_++; } |
| size_t num_removal_attempts() const { return num_removal_attempts_; } |
| |
| void InitializeInspectValues(); |
| |
| void clear_active_resume() { active_resume_ = nullptr; } |
| |
| const fidl::WireSharedClient<fuchsia_device_manager::DeviceController>& device_controller() |
| const { |
| return device_controller_; |
| } |
| |
| fidl::WireSharedClient<fuchsia_device_manager::DeviceController>& device_controller() { |
| return device_controller_; |
| } |
| |
| const std::optional<fidl::ServerBindingRef<fuchsia_device_manager::Coordinator>>& |
| coordinator_binding() const { |
| return coordinator_binding_; |
| } |
| |
| const fidl::ServerEnd<fuchsia_device_manager::DeviceController> ConnectDeviceController( |
| async_dispatcher_t* dispatcher) { |
| auto endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>(); |
| device_controller_.Bind(std::move(endpoints->client), dispatcher); |
| return std::move(endpoints->server); |
| } |
| |
| bool DriverLivesInSystemStorage() const; |
| |
| // Returns true if this device already has a driver bound. |
| bool IsAlreadyBound() const; |
| |
| private: |
| fidl::WireSharedClient<fuchsia_device_manager::DeviceController> device_controller_; |
| std::optional<fidl::ServerBindingRef<fuchsia_device_manager::Coordinator>> coordinator_binding_; |
| |
| const fbl::String name_; |
| const fbl::String libname_; |
| const fbl::String args_; |
| |
| fbl::RefPtr<Device> parent_; |
| const uint32_t protocol_id_; |
| |
| fbl::RefPtr<Device> proxy_; |
| fbl::RefPtr<Device> new_proxy_; |
| |
| fbl::Array<const zx_device_prop_t> props_; |
| |
| fbl::Array<const StrProperty> str_props_; |
| |
| async::TaskClosure publish_task_; |
| |
| // List of all child devices of this device, except for composite devices. |
| // Composite devices are excluded because their multiple-parent nature |
| // precludes using the same intrusive nodes as single-parent devices. |
| fbl::TaggedDoublyLinkedList<Device*, ChildListTag> children_; |
| |
| // Metadata entries associated to this device. |
| fbl::DoublyLinkedList<std::unique_ptr<Metadata>> metadata_; |
| |
| // list of all fragments that this device bound to. |
| fbl::TaggedDoublyLinkedList<CompositeDeviceFragment*, CompositeDeviceFragment::DeviceListTag> |
| fragments_; |
| |
| // - If this device is part of a composite device, this is inhabited by |
| // CompositeDeviceFragment* and it points to the fragment that matched it. |
| // Note that this is only set on the device that matched the fragment, not |
| // the "fragment device" added by the fragment driver. |
| // - If this device is a composite device, this is inhabited by |
| // CompositeDevice* and it points to the composite that describes it. |
| // - Otherwise, it is inhabited by UnassociatedWithComposite |
| struct UnassociatedWithComposite {}; |
| std::variant<UnassociatedWithComposite, CompositeDevice*> composite_; |
| |
| fbl::RefPtr<DriverHost> host_; |
| // The id of this device from the perspective of the driver_host. This can be |
| // used to communicate with the driver_host about this device. |
| uint64_t local_id_ = 0; |
| |
| // The current state of the device |
| State state_ = State::kActive; |
| |
| // If an init is in-progress, this task represents it. |
| fbl::RefPtr<InitTask> active_init_; |
| // If an init is in-progress, this completion will be invoked when it is |
| // completed. It will likely mark |active_init_| as completed and clear it. |
| InitCompletion init_completion_; |
| |
| // If a suspend is in-progress, this task represents it. |
| fbl::RefPtr<SuspendTask> active_suspend_; |
| // If a suspend is in-progress, this completion will be invoked when it is |
| // completed. It will likely mark |active_suspend_| as completed and clear |
| // it. |
| SuspendCompletion suspend_completion_; |
| |
| // If a resume is in-progress, this task represents it. |
| fbl::RefPtr<ResumeTask> active_resume_; |
| // If a Resume is in-progress, this completion will be invoked when it is |
| // completed. |
| ResumeCompletion resume_completion_; |
| |
| // If an unbind is in-progress, this task represents it. |
| fbl::RefPtr<UnbindTask> active_unbind_; |
| // If an unbind is in-progress, this completion will be invoked when it is |
| // completed. It will likely mark |active_unbind_| as completed and clear |
| // it. |
| UnbindCompletion unbind_completion_; |
| |
| // If a remove is in-progress, this task represents it. |
| fbl::RefPtr<RemoveTask> active_remove_; |
| // If a remove is in-progress, this completion will be invoked when it is |
| // completed. It will likely mark |active_remove_| as completed and clear |
| // it. |
| RemoveCompletion remove_completion_; |
| |
| // Name of the inspect vmo file as it appears in diagnostics class directory |
| fbl::String link_name_; |
| |
| fbl::RefPtr<fs::VmoFile> inspect_file_; |
| |
| // For attaching as an open connection to the proxy device, |
| // or once the device becomes visible. |
| zx::channel client_remote_; |
| |
| // Provides incoming directory for the driver which binds to this device. |
| fidl::ClientEnd<fio::Directory> outgoing_dir_; |
| |
| // This lets us check for unexpected removals and is for testing use only. |
| size_t num_removal_attempts_ = 0; |
| |
| std::optional<DeviceInspect> inspect_; |
| }; |
| |
| #endif // SRC_DEVICES_BIN_DRIVER_MANAGER_DEVICE_H_ |