blob: ac0c83578a3d217352e798faa18e986b8ee20913 [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 <algorithm>
#include <ddk/protocol/composite.h>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include "driver_host.h"
#include "zx_device.h"
namespace {
class CompositeDeviceInstance {
public:
CompositeDeviceInstance(zx_device_t* zxdev, CompositeFragments&& fragments)
: zxdev_(zxdev), fragments_(std::move(fragments)) {}
static zx_status_t Create(fbl::RefPtr<zx_device> zxdev, CompositeFragments&& fragments,
std::unique_ptr<CompositeDeviceInstance>* device) {
// Leak a reference to the zxdev here. It will be cleaned up by the
// device_unbind_reply() in Unbind().
auto dev = std::make_unique<CompositeDeviceInstance>(fbl::ExportToRawPtr(&zxdev),
std::move(fragments));
*device = std::move(dev);
return ZX_OK;
}
uint32_t GetFragmentCount() { return static_cast<uint32_t>(fragments_.size()); }
void GetFragments(composite_device_fragment_t* comp_list, size_t comp_count,
size_t* comp_actual) {
size_t actual = std::min(comp_count, fragments_.size());
for (size_t i = 0; i < actual; ++i) {
strncpy(comp_list[i].name, fragments_[i].name.c_str(), 32);
comp_list[i].device = fragments_[i].device.get();
}
*comp_actual = actual;
}
bool GetFragment(const char* name, zx_device_t** out) {
for (auto& fragment : fragments_) {
if (strncmp(name, fragment.name.c_str(), 32) == 0) {
*out = fragment.device.get();
return true;
}
}
return false;
}
void Release() { delete this; }
void Unbind() {
for (auto& fragment : fragments_) {
// Drop the reference to the composite device.
fragment.device->take_composite();
}
fragments_.reset();
device_unbind_reply(zxdev_);
}
const CompositeFragments& fragments() { return fragments_; }
private:
zx_device_t* zxdev_;
CompositeFragments fragments_;
};
} // namespace
// Get the placeholder driver structure for the composite driver
fbl::RefPtr<zx_driver> GetCompositeDriver(DriverHostContext* ctx) {
static fbl::Mutex lock;
static fbl::RefPtr<zx_driver> composite TA_GUARDED(lock);
fbl::AutoLock guard(&lock);
if (composite == nullptr) {
zx_status_t status =
zx_driver::Create("<internal:composite>", ctx->inspect().drivers(), &composite);
if (status != ZX_OK) {
return nullptr;
}
composite->set_name("internal:composite");
}
return composite;
}
zx_status_t InitializeCompositeDevice(const fbl::RefPtr<zx_device>& dev,
CompositeFragments&& fragments) {
static const zx_protocol_device_t composite_device_ops = []() {
zx_protocol_device_t ops = {};
ops.unbind = [](void* ctx) { static_cast<CompositeDeviceInstance*>(ctx)->Unbind(); };
ops.release = [](void* ctx) { static_cast<CompositeDeviceInstance*>(ctx)->Release(); };
return ops;
}();
static composite_protocol_ops_t composite_ops = []() {
composite_protocol_ops_t ops = {};
ops.get_fragment_count = [](void* ctx) {
return static_cast<CompositeDeviceInstance*>(ctx)->GetFragmentCount();
};
ops.get_fragments = [](void* ctx, composite_device_fragment_t* comp_list, size_t comp_count,
size_t* comp_actual) {
static_cast<CompositeDeviceInstance*>(ctx)->GetFragments(comp_list, comp_count, comp_actual);
};
ops.get_fragment = [](void* ctx, const char* name, zx_device_t** out) -> bool {
return static_cast<CompositeDeviceInstance*>(ctx)->GetFragment(name, out);
};
return ops;
}();
auto composite = fbl::MakeRefCounted<CompositeDevice>(dev);
std::unique_ptr<CompositeDeviceInstance> new_device;
zx_status_t status = CompositeDeviceInstance::Create(dev, std::move(fragments), &new_device);
if (status != ZX_OK) {
return status;
}
for (auto& fragment : new_device->fragments()) {
fragment.device->set_composite(composite);
}
dev->set_protocol_id(ZX_PROTOCOL_COMPOSITE);
dev->protocol_ops = &composite_ops;
dev->set_ops(&composite_device_ops);
dev->ctx = new_device.release();
// Flag that when this is cleaned up, we should run its release hook.
dev->set_flag(DEV_FLAG_ADDED);
return ZX_OK;
}
CompositeDevice::~CompositeDevice() = default;