blob: 4ebd225f931e31f65687c459cf3b352a37c63223 [file] [log] [blame]
// Copyright 2022 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.
#include "src/devices/bin/driver_manager/v2/composite_assembler.h"
#include "src/devices/lib/log/log.h"
namespace dfv2 {
namespace fdf {
using namespace fuchsia_driver_framework;
}
fbl::Array<const zx_device_prop_t> NodeToProps(Node* node) {
std::vector<zx_device_prop_t> props;
for (auto& prop : node->properties()) {
if (prop.key().is_int_value() && prop.value().is_int_value()) {
zx_device_prop_t device_prop;
device_prop.id = static_cast<uint16_t>(prop.key().int_value());
device_prop.value = prop.value().int_value();
props.push_back(device_prop);
}
}
fbl::Array<zx_device_prop_t> props_array(new zx_device_prop_t[props.size()], props.size());
for (size_t i = 0; i < props.size(); i++) {
props_array[i] = props[i];
}
return props_array;
}
zx::status<CompositeDeviceFragment> CompositeDeviceFragment::Create(
fuchsia_device_manager::DeviceFragment fragment) {
auto composite_fragment = CompositeDeviceFragment();
if (fragment.parts().size() != 1) {
LOGF(ERROR, "Composite fragments with multiple parts are deprecated. %s has %zd parts.",
fragment.name().c_str(), fragment.parts().size());
return zx::error(ZX_ERR_INTERNAL);
}
auto& program = fragment.parts()[0].match_program();
std::vector<zx_bind_inst_t> rules(program.size());
for (size_t i = 0; i < program.size(); i++) {
rules[i] = zx_bind_inst_t{
.op = program[i].op(),
.arg = program[i].arg(),
};
}
composite_fragment.name_ = fragment.name();
composite_fragment.bind_rules_ = std::move(rules);
return zx::ok(std::move(composite_fragment));
}
bool CompositeDeviceFragment::BindNode(std::shared_ptr<Node> node) {
// If we have a bound node, then don't match.
if (bound_node_.lock()) {
return false;
}
internal::BindProgramContext context = {};
context.binding = bind_rules_.data();
context.binding_size = bind_rules_.size() * sizeof(zx_bind_inst_t);
auto props = NodeToProps(node.get());
context.props = &props;
if (!EvaluateBindProgram(&context)) {
return false;
}
// We matched! Store our node.
bound_node_ = node;
return true;
}
zx::status<std::unique_ptr<CompositeDeviceAssembler>> CompositeDeviceAssembler::Create(
std::string name, fuchsia_device_manager::CompositeDeviceDescriptor descriptor,
DriverBinder* binder, async_dispatcher_t* dispatcher) {
auto assembler = std::make_unique<CompositeDeviceAssembler>();
assembler->name_ = std::move(name);
assembler->binder_ = binder;
assembler->dispatcher_ = dispatcher;
if (descriptor.primary_fragment_index() >= descriptor.fragments().size()) {
LOGF(ERROR,
"Composite fragments with bad primary_fragment_index. primary is %ul but composite has "
"%zd parts.",
descriptor.primary_fragment_index(), descriptor.fragments().size());
return zx::error(ZX_ERR_INTERNAL);
}
// Create the properties.
for (auto& prop : descriptor.props()) {
auto property = fdf::wire::NodeProperty::Builder(assembler->arena_);
property.key(fdf::wire::NodePropertyKey::WithIntValue(prop.id()));
property.value(fdf::wire::NodePropertyValue::WithIntValue(prop.value()));
assembler->properties_.push_back(property.Build());
}
// Create the string properties.
for (auto& prop : descriptor.str_props()) {
auto property = fdf::wire::NodeProperty::Builder(assembler->arena_);
property.key(fdf::wire::NodePropertyKey::WithStringValue(assembler->arena_, prop.key()));
switch (prop.value().Which()) {
case fuchsia_device_manager::PropertyValue::Tag::kBoolValue:
property.value(
fdf::wire::NodePropertyValue::WithBoolValue(prop.value().bool_value().value()));
break;
case fuchsia_device_manager::PropertyValue::Tag::kIntValue:
property.value(
fdf::wire::NodePropertyValue::WithIntValue(prop.value().int_value().value()));
break;
case fuchsia_device_manager::PropertyValue::Tag::kStrValue:
property.value(fdf::wire::NodePropertyValue::WithStringValue(
assembler->arena_, prop.value().str_value().value()));
break;
case fuchsia_device_manager::PropertyValue::Tag::kEnumValue:
property.value(fdf::wire::NodePropertyValue::WithEnumValue(
assembler->arena_, prop.value().enum_value().value()));
break;
}
assembler->properties_.push_back(property.Build());
}
// Make the primary fragment first.
auto fragment =
CompositeDeviceFragment::Create(descriptor.fragments()[descriptor.primary_fragment_index()]);
if (fragment.is_error()) {
return fragment.take_error();
}
assembler->fragments_.push_back(std::move(fragment.value()));
// Make the other fragments.
for (size_t i = 0; i < descriptor.fragments().size(); i++) {
if (i == descriptor.primary_fragment_index()) {
continue;
}
auto fragment = CompositeDeviceFragment::Create(descriptor.fragments()[i]);
if (fragment.is_error()) {
return fragment.take_error();
}
assembler->fragments_.push_back(std::move(fragment.value()));
}
return zx::ok(std::move(assembler));
}
bool CompositeDeviceAssembler::BindNode(std::shared_ptr<Node> node) {
bool matched = false;
for (auto& fragment : fragments_) {
if (fragment.BindNode(node)) {
matched = true;
LOGF(INFO, "Found a match for composite device '%s': fragment %s: device '%s'", name_.c_str(),
std::string(fragment.name()).c_str(), node->TopoName().c_str());
break;
}
}
if (!matched) {
return false;
}
TryToAssemble();
return true;
}
void CompositeDeviceAssembler::TryToAssemble() {
std::vector<std::shared_ptr<Node>> strong_parents;
std::vector<Node*> parents;
std::vector<std::string> parents_names;
for (auto& fragment : fragments_) {
auto node = fragment.bound_node();
// A fragment is missing a node, don't assemble.
if (!node) {
return;
}
parents.push_back(node.get());
parents_names.emplace_back(fragment.name());
strong_parents.push_back(std::move(node));
}
auto node = Node::CreateCompositeNode(name_, std::move(parents), parents_names,
std::move(properties_), binder_, dispatcher_);
if (node.is_error()) {
return;
}
LOGF(INFO, "Built composite device at '%s'", node->TopoName().c_str());
// Bind the node we just created.
binder_->Bind(*node.value(), nullptr);
}
CompositeDeviceManager::CompositeDeviceManager(DriverBinder* binder, async_dispatcher_t* dispatcher)
: binder_(binder), dispatcher_(dispatcher) {}
zx_status_t CompositeDeviceManager::AddCompositeDevice(
std::string name, fuchsia_device_manager::CompositeDeviceDescriptor descriptor) {
auto assembler = CompositeDeviceAssembler::Create(std::move(name), std::move(descriptor), binder_,
dispatcher_);
if (assembler.is_error()) {
return assembler.error_value();
}
assemblers_.push_back(std::move(assembler.value()));
return ZX_OK;
}
bool CompositeDeviceManager::BindNode(std::shared_ptr<Node> node) {
for (auto& assembler : assemblers_) {
if (assembler->BindNode(node)) {
return true;
}
}
return false;
}
} // namespace dfv2