| // 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_COMPOSITE_DEVICE_H_ |
| #define SRC_DEVICES_BIN_DRIVER_MANAGER_COMPOSITE_DEVICE_H_ |
| |
| #include <fidl/fuchsia.device.manager/cpp/wire.h> |
| #include <lib/ddk/binding.h> |
| |
| #include <memory> |
| #include <string_view> |
| |
| #include <fbl/array.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/string.h> |
| |
| #include "driver.h" |
| #include "metadata.h" |
| |
| // Forward declaration |
| class CompositeDevice; |
| class Coordinator; |
| class Device; |
| |
| // Tags used for container membership identification |
| namespace internal { |
| struct CdfListTag {}; |
| struct CdfDeviceListTag {}; |
| } // namespace internal |
| |
| using StrPropertyValue = std::variant<uint32_t, std::string, bool, std::string>; |
| |
| enum StrPropValueType { Integer = 0, String = 1, Bool = 2, Enum = 3 }; |
| |
| struct StrProperty { |
| std::string key; |
| StrPropertyValue value; |
| }; |
| |
| // A single device that is part of a composite device. |
| // TODO(teisenbe): Should this just be an inner-class of CompositeDevice? |
| class CompositeDeviceFragment |
| : public fbl::ContainableBaseClasses< |
| fbl::TaggedDoublyLinkedListable<std::unique_ptr<CompositeDeviceFragment>, |
| internal::CdfListTag, |
| fbl::NodeOptions::AllowMultiContainerUptr>, |
| fbl::TaggedDoublyLinkedListable<CompositeDeviceFragment*, internal::CdfDeviceListTag>> { |
| public: |
| using ListTag = internal::CdfListTag; |
| using DeviceListTag = internal::CdfDeviceListTag; |
| |
| CompositeDeviceFragment(CompositeDevice* composite, std::string name, uint32_t index, |
| fbl::Array<const zx_bind_inst_t> bind_rules); |
| |
| CompositeDeviceFragment(CompositeDeviceFragment&&) = delete; |
| CompositeDeviceFragment& operator=(CompositeDeviceFragment&&) = delete; |
| |
| CompositeDeviceFragment(const CompositeDeviceFragment&) = delete; |
| CompositeDeviceFragment& operator=(const CompositeDeviceFragment&) = delete; |
| |
| ~CompositeDeviceFragment(); |
| |
| // Attempt to match this fragment against |dev|. Returns true if the match |
| // was successful. |
| bool TryMatch(const fbl::RefPtr<Device>& dev) const; |
| |
| // Bind this fragment to the given device. |
| zx_status_t Bind(const fbl::RefPtr<Device>& dev); |
| |
| // Unbind this fragment. |
| void Unbind(); |
| |
| std::string_view name() const { return name_; } |
| uint32_t index() const { return index_; } |
| CompositeDevice* composite() const { return composite_; } |
| // If not nullptr, this fragment has been bound to this device |
| const fbl::RefPtr<Device>& bound_device() const { return bound_device_; } |
| |
| const fbl::RefPtr<Device>& fragment_device() const { return fragment_device_; } |
| // Registers (or unregisters) the fragment device (i.e. an instance of the |
| // "fragment" driver) that bound to bound_device(). |
| void set_fragment_device(fbl::RefPtr<Device> device) { fragment_device_ = std::move(device); } |
| |
| private: |
| // The CompositeDevice that this is a part of. |
| CompositeDevice* const composite_; |
| |
| // The name of this fragment within its CompositeDevice. |
| const std::string name_; |
| |
| // The index of this fragment within its CompositeDevice. |
| const uint32_t index_; |
| |
| // Bind rules for the fragment. |
| const fbl::Array<const zx_bind_inst_t> bind_rules_; |
| |
| // If this fragment has been bound to a device, this points to that device. |
| fbl::RefPtr<Device> bound_device_ = nullptr; |
| // Once the bound device has the fragment driver attach to it, this points |
| // to the device managed by the fragment driver. |
| fbl::RefPtr<Device> fragment_device_ = nullptr; |
| }; |
| |
| // A device composed of other devices. |
| class CompositeDevice : public fbl::DoublyLinkedListable<std::unique_ptr<CompositeDevice>> { |
| public: |
| // Only public because of make_unique. You probably want Create(). |
| CompositeDevice(fbl::String name, fbl::Array<const zx_device_prop_t> properties, |
| fbl::Array<const StrProperty> str_properties, uint32_t fragments_count, |
| uint32_t primary_fragment_index, bool spawn_colocated, |
| fbl::Array<std::unique_ptr<Metadata>> metadata, bool from_driver_index); |
| |
| CompositeDevice(CompositeDevice&&) = delete; |
| CompositeDevice& operator=(CompositeDevice&&) = delete; |
| |
| CompositeDevice(const CompositeDevice&) = delete; |
| CompositeDevice& operator=(const CompositeDevice&) = delete; |
| |
| ~CompositeDevice(); |
| |
| static zx_status_t Create(std::string_view name, |
| fuchsia_device_manager::wire::CompositeDeviceDescriptor comp_desc, |
| std::unique_ptr<CompositeDevice>* out); |
| |
| static zx::status<std::unique_ptr<CompositeDevice>> CreateForDeviceGroup( |
| std::string_view name, fuchsia_device_manager::wire::DeviceGroupDescriptor group_desc); |
| |
| static zx_status_t CreateFromDriverIndex(MatchedCompositeDriverInfo driver, |
| std::unique_ptr<CompositeDevice>* out); |
| |
| const fbl::String& name() const { return name_; } |
| const fbl::Array<const zx_device_prop_t>& properties() const { return properties_; } |
| const fbl::Array<const StrProperty>& str_properties() const { return str_properties_; } |
| uint32_t fragments_count() const { return fragments_count_; } |
| |
| // Returns a reference to the constructed composite device, if it exists. |
| fbl::RefPtr<Device> device() const { return device_; } |
| |
| // Attempt to match and bind any of the unbound fragments against |dev|. |
| zx_status_t TryMatchBindFragments(const fbl::RefPtr<Device>& dev); |
| |
| // Bind the fragment with the given index to the specified device |
| zx_status_t BindFragment(size_t index, const fbl::RefPtr<Device>& dev); |
| |
| // Mark the given fragment as unbound. Note that since we don't expose |
| // this device's fragments in the API, this method can only be invoked by |
| // CompositeDeviceFragment |
| void UnbindFragment(CompositeDeviceFragment* fragment); |
| |
| // Creates the actual device and orchestrates the creation of the composite |
| // device in a driver_host. |
| // Returns ZX_ERR_SHOULD_WAIT if some fragment is not fully ready (i.e. has |
| // either not been matched or the fragment driver that bound to it has not |
| // yet published its device). |
| zx_status_t TryAssemble(); |
| |
| // Forget about the composite device that was constructed. If TryAssemble() |
| // is invoked after this, it will reassemble the device. |
| void Remove(); |
| |
| using FragmentList = fbl::TaggedDoublyLinkedList<std::unique_ptr<CompositeDeviceFragment>, |
| CompositeDeviceFragment::ListTag>; |
| FragmentList& bound_fragments() { return bound_fragments_; } |
| |
| private: |
| // Returns true if a fragment matches |dev|. Sets |*index_out| will be set to the |
| // matching fragment. |
| bool IsFragmentMatch(const fbl::RefPtr<Device>& dev, size_t* index_out) const; |
| |
| const fbl::String name_; |
| const fbl::Array<const zx_device_prop_t> properties_; |
| const fbl::Array<const StrProperty> str_properties_; |
| |
| const uint32_t fragments_count_; |
| const uint32_t primary_fragment_index_; |
| |
| const bool spawn_colocated_; |
| const fbl::Array<std::unique_ptr<Metadata>> metadata_; |
| |
| // Driver index variables. |driver_index_driver_| is set by CreateFromDriverIndex(). |
| const bool from_driver_index_; |
| const Driver* driver_index_driver_; |
| |
| FragmentList unbound_fragments_; |
| FragmentList bound_fragments_; |
| |
| // Once the composite has been assembled, this refers to the constructed |
| // device. |
| fbl::RefPtr<Device> device_; |
| }; |
| |
| #endif // SRC_DEVICES_BIN_DRIVER_MANAGER_COMPOSITE_DEVICE_H_ |