blob: 6b1d6ed889edb53031e8acaf596b70679f1b1518 [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.
#include "composite_device.h"
#include <zircon/status.h>
#include <utility>
#include "binding_internal.h"
#include "coordinator.h"
#include "fidl.h"
#include "log.h"
// CompositeDevice methods
CompositeDevice::CompositeDevice(fbl::String name, fbl::Array<const zx_device_prop_t> properties,
uint32_t fragments_count, uint32_t coresident_device_index,
fbl::Array<std::unique_ptr<Metadata>> metadata)
: name_(std::move(name)),
properties_(std::move(properties)),
fragments_count_(fragments_count),
coresident_device_index_(coresident_device_index),
metadata_(std::move(metadata)) {}
CompositeDevice::~CompositeDevice() = default;
zx_status_t CompositeDevice::Create(
const fbl::StringPiece& name,
llcpp::fuchsia::device::manager::CompositeDeviceDescriptor comp_desc,
std::unique_ptr<CompositeDevice>* out) {
fbl::String name_obj(name);
fbl::Array<zx_device_prop_t> properties(new zx_device_prop_t[comp_desc.props.count()],
comp_desc.props.count());
memcpy(properties.data(), comp_desc.props.data(),
comp_desc.props.count() * sizeof(comp_desc.props.data()[0]));
fbl::Array<std::unique_ptr<Metadata>> metadata(
new std::unique_ptr<Metadata>[comp_desc.metadata.count()], comp_desc.metadata.count());
for (size_t i = 0; i < comp_desc.metadata.count(); i++) {
std::unique_ptr<Metadata> md;
zx_status_t status = Metadata::Create(comp_desc.metadata[i].data.count(), &md);
if (status != ZX_OK) {
return status;
}
md->type = comp_desc.metadata[i].key;
md->length = comp_desc.metadata[i].data.count();
memcpy(md->Data(), comp_desc.metadata[i].data.data(), md->length);
metadata[i] = std::move(md);
}
auto dev = std::make_unique<CompositeDevice>(
std::move(name), std::move(properties), comp_desc.fragments.count(),
comp_desc.coresident_device_index, std::move(metadata));
for (uint32_t i = 0; i < comp_desc.fragments.count(); ++i) {
const auto& fidl_fragment = comp_desc.fragments[i];
size_t parts_count = fidl_fragment.parts_count;
fbl::Array<FragmentPartDescriptor> parts(new FragmentPartDescriptor[parts_count], parts_count);
for (size_t j = 0; j < parts_count; ++j) {
const auto& fidl_part = fidl_fragment.parts[j];
size_t program_count = fidl_part.match_program_count;
fbl::Array<zx_bind_inst_t> match_program(new zx_bind_inst_t[program_count], program_count);
for (size_t k = 0; k < program_count; ++k) {
match_program[k] = zx_bind_inst_t{
.op = fidl_part.match_program[k].op,
.arg = fidl_part.match_program[k].arg,
};
}
parts[j] = {std::move(match_program)};
}
auto fragment = std::make_unique<CompositeDeviceFragment>(dev.get(), i, std::move(parts));
dev->unbound_.push_back(std::move(fragment));
}
*out = std::move(dev);
return ZX_OK;
}
bool CompositeDevice::TryMatchFragments(const fbl::RefPtr<Device>& dev, size_t* index_out) {
for (auto itr = bound_.begin(); itr != bound_.end(); ++itr) {
if (itr->TryMatch(dev)) {
log(ERROR, "driver_manager: ambiguous composite bind! composite='%s', dev1='%s', dev2='%s'\n",
name_.data(), itr->bound_device()->name().data(), dev->name().data());
return false;
}
}
for (auto itr = unbound_.begin(); itr != unbound_.end(); ++itr) {
if (itr->TryMatch(dev)) {
log(SPEW, "driver_manager: found match for composite='%s', dev='%s'\n", name_.data(),
dev->name().data());
*index_out = itr->index();
return true;
}
}
log(SPEW, "driver_manager: no match for composite='%s', dev='%s'\n", name_.data(),
dev->name().data());
return false;
}
zx_status_t CompositeDevice::BindFragment(size_t index, const fbl::RefPtr<Device>& dev) {
// Find the fragment we're binding
CompositeDeviceFragment* fragment = nullptr;
for (auto& unbound_fragment : unbound_) {
if (unbound_fragment.index() == index) {
fragment = &unbound_fragment;
break;
}
}
ZX_ASSERT_MSG(fragment != nullptr, "Attempted to bind fragment that wasn't unbound!\n");
zx_status_t status = fragment->Bind(dev);
if (status != ZX_OK) {
return status;
}
bound_.push_back(unbound_.erase(*fragment));
return ZX_OK;
}
zx_status_t CompositeDevice::TryAssemble() {
ZX_ASSERT(device_ == nullptr);
if (!unbound_.is_empty()) {
return ZX_ERR_SHOULD_WAIT;
}
Devhost* devhost = nullptr;
for (auto& fragment : bound_) {
// Find the devhost to put everything in (if we don't find one, nullptr
// means "a new devhost").
if (fragment.index() == coresident_device_index_) {
devhost = fragment.bound_device()->host();
}
// Make sure the fragment driver has created its device
if (fragment.fragment_device() == nullptr) {
return ZX_ERR_SHOULD_WAIT;
}
}
Coordinator* coordinator = nullptr;
uint64_t fragment_local_ids[fuchsia_device_manager_FRAGMENTS_MAX] = {};
// Create all of the proxies for the fragment devices, in the same process
for (auto& fragment : bound_) {
const auto& fragment_dev = fragment.fragment_device();
auto bound_dev = fragment.bound_device();
coordinator = fragment_dev->coordinator;
// If the device we're bound to is proxied, we care about its proxy
// rather than it, since that's the side that we communicate with.
if (bound_dev->proxy()) {
bound_dev = bound_dev->proxy();
}
// Check if we need to use the proxy. If not, share a reference to
// the instance of the fragment device.
if (bound_dev->host() == devhost) {
fragment_local_ids[fragment.index()] = fragment_dev->local_id();
continue;
}
// We need to create it. Double check that we haven't ended up in a state
// where the proxies would need to be in different processes.
if (devhost != nullptr && fragment_dev->proxy() != nullptr &&
fragment_dev->proxy()->host() != nullptr && fragment_dev->proxy()->host() != devhost) {
log(ERROR, "driver_manager: cannot create composite, proxies in different processes\n");
return ZX_ERR_BAD_STATE;
}
zx_status_t status = coordinator->PrepareProxy(fragment_dev, devhost);
if (status != ZX_OK) {
return status;
}
// If we hadn't picked a devhost, use the one that was created just now.
if (devhost == nullptr) {
devhost = fragment_dev->proxy()->host();
ZX_ASSERT(devhost != nullptr);
}
// Stash the local ID after the proxy has been created
fragment_local_ids[fragment.index()] = fragment_dev->proxy()->local_id();
}
zx::channel coordinator_rpc_local, coordinator_rpc_remote;
zx_status_t status = zx::channel::create(0, &coordinator_rpc_local, &coordinator_rpc_remote);
if (status != ZX_OK) {
return status;
}
zx::channel device_controller_rpc_local, device_controller_rpc_remote;
status = zx::channel::create(0, &device_controller_rpc_local, &device_controller_rpc_remote);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<Device> new_device;
status = Device::CreateComposite(coordinator, devhost, *this, std::move(coordinator_rpc_local),
std::move(device_controller_rpc_remote), &new_device);
if (status != ZX_OK) {
return status;
}
coordinator->devices().push_back(new_device);
// Create the composite device in the devhost
status = dh_send_create_composite_device(devhost, new_device.get(), *this, fragment_local_ids,
std::move(coordinator_rpc_remote),
std::move(device_controller_rpc_local));
if (status != ZX_OK) {
log(ERROR, "driver_manager: create composite device request failed: %s\n",
zx_status_get_string(status));
return status;
}
device_ = std::move(new_device);
device_->set_composite(this);
// Add metadata
for (size_t i = 0; i < metadata_.size(); i++) {
// Making a copy of metadata, instead of transfering ownership, so that
// metadata can be added again if device is recreated
status = coordinator->AddMetadata(device_, metadata_[i]->type, metadata_[i]->Data(),
metadata_[i]->length);
if (status != ZX_OK) {
log(ERROR, "driver_manager: Failed to add metadata: %s\n", zx_status_get_string(status));
return status;
}
}
status = device_->SignalReadyForBind();
if (status != ZX_OK) {
return status;
}
return ZX_OK;
}
void CompositeDevice::UnbindFragment(CompositeDeviceFragment* fragment) {
// If the composite was fully instantiated, diassociate from it. It will be
// reinstantiated when this fragment is re-bound.
if (device_ != nullptr) {
Remove();
}
ZX_ASSERT(device_ == nullptr);
ZX_ASSERT(fragment->composite() == this);
unbound_.push_back(bound_.erase(*fragment));
}
void CompositeDevice::Remove() {
device_->disassociate_from_composite();
device_ = nullptr;
}
// CompositeDeviceFragment methods
CompositeDeviceFragment::CompositeDeviceFragment(CompositeDevice* composite, uint32_t index,
fbl::Array<const FragmentPartDescriptor> parts)
: composite_(composite), index_(index), parts_(std::move(parts)) {}
CompositeDeviceFragment::~CompositeDeviceFragment() = default;
bool CompositeDeviceFragment::TryMatch(const fbl::RefPtr<Device>& dev) {
if (parts_.size() > UINT32_MAX) {
return false;
}
auto match = ::internal::MatchParts(dev, parts_.data(), static_cast<uint32_t>(parts_.size()));
if (match != ::internal::Match::One) {
return false;
}
return true;
}
zx_status_t CompositeDeviceFragment::Bind(const fbl::RefPtr<Device>& dev) {
ZX_ASSERT(bound_device_ == nullptr);
zx_status_t status = dev->coordinator->BindDriverToDevice(
dev, dev->coordinator->fragment_driver(), true /* autobind */);
if (status != ZX_OK) {
return status;
}
bound_device_ = dev;
dev->push_fragment(this);
return ZX_OK;
}
void CompositeDeviceFragment::Unbind() {
ZX_ASSERT(bound_device_ != nullptr);
composite_->UnbindFragment(this);
// Drop our reference to the device added by the fragment driver
fragment_device_ = nullptr;
bound_device_->disassociate_from_composite();
bound_device_ = nullptr;
}