blob: 270a1bf40db7f58113722fdd6d49a3b25cbb42ae [file] [log] [blame]
// 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_