blob: 6f42b93b5c917f3be0dc847c341c2c0c500d323e [file] [log] [blame]
// Copyright 2021 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_host/proxy_device.h"
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include "lib/fdio/directory.h"
#include "lib/zx/status.h"
#include "src/devices/bin/driver_host/driver_host.h"
namespace {
class ProxyDeviceInstance {
public:
ProxyDeviceInstance(zx_device_t* zxdev, fidl::ClientEnd<fuchsia_io::Directory> incoming_dir)
: zxdev_(zxdev), incoming_dir_(std::move(incoming_dir)) {}
static std::unique_ptr<ProxyDeviceInstance> Create(
fbl::RefPtr<zx_device> zxdev, fidl::ClientEnd<fuchsia_io::Directory> incoming_dir) {
// Leak a reference to the zxdev here. It will be cleaned up by the
// device_unbind_reply() in Unbind().
return std::make_unique<ProxyDeviceInstance>(fbl::ExportToRawPtr(&zxdev),
std::move(incoming_dir));
}
zx::status<> ConnectToProtocol(const char* protocol, zx::channel request) {
fbl::StringBuffer<fuchsia_io::wire::kMaxPath> path;
path.Append("svc/");
path.Append(protocol);
return zx::make_status(
fdio_service_connect_at(incoming_dir_.channel().get(), path.c_str(), request.release()));
}
void Release() { delete this; }
void Unbind() { device_unbind_reply(zxdev_); }
private:
zx_device_t* zxdev_;
fidl::ClientEnd<fuchsia_io::Directory> incoming_dir_;
};
} // namespace
void InitializeProxyDevice(const fbl::RefPtr<zx_device>& dev,
fidl::ClientEnd<fuchsia_io::Directory> incoming_dir) {
static const zx_protocol_device_t proxy_device_ops = []() {
zx_protocol_device_t ops = {};
ops.unbind = [](void* ctx) { static_cast<ProxyDeviceInstance*>(ctx)->Unbind(); };
ops.release = [](void* ctx) { static_cast<ProxyDeviceInstance*>(ctx)->Release(); };
return ops;
}();
auto proxy = fbl::MakeRefCounted<ProxyDevice>(dev);
auto new_device = ProxyDeviceInstance::Create(dev, std::move(incoming_dir));
dev->set_proxy(proxy);
dev->set_ops(&proxy_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);
}
fbl::RefPtr<zx_driver> GetProxyDriver(DriverHostContext* ctx) {
static fbl::Mutex lock;
static fbl::RefPtr<zx_driver> proxy TA_GUARDED(lock);
fbl::AutoLock guard(&lock);
if (proxy == nullptr) {
auto status = zx_driver::Create("<internal:proxy>", ctx->inspect().drivers(), &proxy);
if (status != ZX_OK) {
return nullptr;
}
proxy->set_name("internal:proxy");
}
return proxy;
}
zx::status<> ProxyDevice::ConnectToProtocol(const char* protocol, zx::channel request) {
return static_cast<ProxyDeviceInstance*>(device_->ctx)
->ConnectToProtocol(protocol, std::move(request));
}