blob: 93b8755cb529c54e73667907f02b51a6404071f5 [file] [log] [blame]
// Copyright 2020 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 "sysmem-proxy-device.h"
#include <fuchsia/hardware/platform/bus/c/banjo.h>
#include <fuchsia/hardware/platform/bus/cpp/banjo.h>
#include <fuchsia/sysmem/c/fidl.h>
#include <fuchsia/sysmem/llcpp/fidl.h>
#include <fuchsia/sysmem2/llcpp/fidl.h>
#include <inttypes.h>
#include <lib/async/cpp/task.h>
#include <lib/async/dispatcher.h>
#include <lib/ddk/device.h>
#include <lib/ddk/platform-defs.h>
#include <lib/fdio/directory.h>
#include <lib/fidl-async-2/simple_binding.h>
#include <lib/fidl-utils/bind.h>
#include <lib/sync/completion.h>
#include <lib/sysmem-version/sysmem-version.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <zircon/assert.h>
#include <memory>
namespace display {
namespace {
fuchsia_sysmem_DriverConnector_ops_t driver_connector_ops = {
.Connect = fidl::Binder<SysmemProxyDevice>::BindMember<&SysmemProxyDevice::Connect>,
};
} // namespace
// severity can be ERROR, WARN, INFO, DEBUG, TRACE. See ddk/debug.h.
//
// Using ## __VA_ARGS__ instead of __VA_OPT__(,) __VA_ARGS__ for now, since
// __VA_OPT__ doesn't seem to be available yet.
#define LOG(severity, fmt, ...) \
zxlogf(severity, "[%s:%s:%d] " fmt "\n", "display", __func__, __LINE__, ##__VA_ARGS__)
SysmemProxyDevice::SysmemProxyDevice(zx_device_t* parent_device,
sysmem_driver::Driver* parent_driver)
: DdkDeviceType2(parent_device),
parent_driver_(parent_driver),
loop_(&kAsyncLoopConfigNeverAttachToThread),
in_proc_sysmem_protocol_{.ops = &sysmem_protocol_ops_, .ctx = this} {
ZX_DEBUG_ASSERT(parent_);
ZX_DEBUG_ASSERT(parent_driver_);
zx_status_t status = loop_.StartThread("sysmem", &loop_thrd_);
ZX_ASSERT(status == ZX_OK);
}
zx_status_t SysmemProxyDevice::Connect(zx_handle_t allocator_request) {
return SysmemConnect(zx::channel(allocator_request));
}
zx_status_t SysmemProxyDevice::SysmemConnect(zx::channel allocator_request) {
const char* kSvcPath = "/svc/fuchsia.sysmem.Allocator";
LOG(INFO, "fdio_service_connect to service service: %s", kSvcPath);
return fdio_service_connect(kSvcPath, allocator_request.release());
}
zx_status_t SysmemProxyDevice::SysmemRegisterHeap(uint64_t heap, zx::channel heap_connection) {
ZX_ASSERT(false);
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t SysmemProxyDevice::SysmemRegisterSecureMem(zx::channel tee_connection) {
ZX_ASSERT(false);
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t SysmemProxyDevice::SysmemUnregisterSecureMem() {
ZX_ASSERT(false);
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t SysmemProxyDevice::Bind() {
zx_status_t status = ddk::PDevProtocolClient::CreateFromDevice(parent_, &pdev_);
if (status != ZX_OK) {
LOG(ERROR, "Failed device_get_protocol() ZX_PROTOCOL_PDEV - status: %d", status);
return status;
}
ddk::PBusProtocolClient pbus;
status = ddk::PBusProtocolClient::CreateFromDevice(parent_, &pbus);
if (status != ZX_OK) {
LOG(ERROR, "ZX_PROTOCOL_PBUS not available %d \n", status);
return status;
}
status = DdkAdd(ddk::DeviceAddArgs("sysmem")
.set_flags(DEVICE_ADD_ALLOW_MULTI_COMPOSITE)
.set_inspect_vmo(inspector_.DuplicateVmo()));
if (status != ZX_OK) {
LOG(ERROR, "Failed to bind device");
return status;
}
// Register the sysmem protocol with the platform bus.
//
// This is essentially the in-proc version of
// fuchsia.sysmem.DriverConnector.
//
// We should only pbus_register_protocol() if device_add() succeeded, but if
// pbus_register_protocol() fails, we should remove the device without it
// ever being visible.
// TODO(ZX-3746) Remove this after all clients have switched to using composite protocol.
status = pbus.RegisterProtocol(ZX_PROTOCOL_SYSMEM,
reinterpret_cast<uint8_t*>(&in_proc_sysmem_protocol_),
sizeof(in_proc_sysmem_protocol_));
if (status != ZX_OK) {
DdkAsyncRemove();
return status;
}
return ZX_OK;
}
zx_status_t SysmemProxyDevice::DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
return fuchsia_sysmem_DriverConnector_dispatch(this, txn, msg, &driver_connector_ops);
}
void SysmemProxyDevice::DdkUnbind(ddk::UnbindTxn txn) {
// Ensure all tasks started before this call finish before shutting down the loop.
async::PostTask(loop_.dispatcher(), [this]() { loop_.Quit(); });
// JoinThreads waits for the Quit() to execute and cause the thread to exit.
loop_.JoinThreads();
loop_.Shutdown();
// After this point the FIDL servers should have been shutdown and all DDK and other protocol
// methods will error out because posting tasks to the dispatcher fails.
txn.Reply();
}
} // namespace display