blob: aab177e9e44a313f25d5fb0ffdfcab00b54fe779 [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 "devhost.h"
#include "zx-device.h"
namespace devmgr {
namespace {
class CompositeDeviceInstance {
public:
CompositeDeviceInstance(zx_device_t* zxdev, CompositeComponents&& components)
: zxdev_(zxdev), components_(std::move(components)) { }
static zx_status_t Create(fbl::RefPtr<zx_device> zxdev, CompositeComponents&& components,
std::unique_ptr<CompositeDeviceInstance>* device) {
// Leak a reference to the zxdev here. It will be cleaned up by the
// device_remove() in Unbind().
auto dev = std::make_unique<CompositeDeviceInstance>(zxdev.leak_ref(),
std::move(components));
*device = std::move(dev);
return ZX_OK;
}
uint32_t GetComponentCount() {
return static_cast<uint32_t>(components_.size());
}
void GetComponents(zx_device_t** comp_list, size_t comp_count, size_t* comp_actual) {
size_t actual = std::min(comp_count, components_.size());
for (size_t i = 0; i < actual; ++i) {
comp_list[i] = components_[i].get();
}
*comp_actual = actual;
}
void Release() {
delete this;
}
void Unbind() {
components_.reset();
device_remove(zxdev_);
}
const CompositeComponents& components() { return components_; }
private:
zx_device_t* zxdev_;
CompositeComponents components_;
};
// Get the placeholder driver structure for the composite driver
fbl::RefPtr<zx_driver> GetCompositeDriver() {
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(&composite);
if (status != ZX_OK) {
return nullptr;
}
composite->set_name("internal:composite");
composite->set_libname("<internal:composite>");
}
return composite;
}
} // namespace
zx_status_t InitializeCompositeDevice(const fbl::RefPtr<zx_device>& dev,
CompositeComponents&& components) {
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_component_count = [](void* ctx) {
return static_cast<CompositeDeviceInstance*>(ctx)->GetComponentCount();
};
ops.get_components = [](void* ctx, zx_device_t** comp_list, size_t comp_count,
size_t* comp_actual) {
static_cast<CompositeDeviceInstance*>(ctx)->GetComponents(comp_list, comp_count,
comp_actual);
};
return ops;
}();
auto driver = GetCompositeDriver();
if (driver == nullptr) {
return ZX_ERR_INTERNAL;
}
auto composite = fbl::MakeRefCounted<CompositeDevice>(dev);
std::unique_ptr<CompositeDeviceInstance> new_device;
zx_status_t status = CompositeDeviceInstance::Create(dev, std::move(components),
&new_device);
if (status != ZX_OK) {
return status;
}
for (auto& component : new_device->components()) {
component->set_composite(composite);
}
dev->protocol_id = ZX_PROTOCOL_COMPOSITE;
dev->protocol_ops = &composite_ops;
dev->driver = driver.get();
dev->ops = &composite_device_ops;
dev->ctx = new_device.release();
// Flag that when this is cleaned up, we should run its release hook.
dev->flags |= DEV_FLAG_ADDED;
return ZX_OK;
}
CompositeDevice::~CompositeDevice() = default;
} // namespace devmgr