blob: de394309faacfe412aa83aa88e556ffa18c051d7 [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 "fake-display-device-tree.h"
#include <lib/async/cpp/task.h>
#include <zxtest/zxtest.h>
#include "src/graphics/display/drivers/fake/fake-display.h"
namespace display {
zx_device_t* Binder::display() { return display_->zxdev(); }
zx_status_t Binder::DeviceAdd(zx_driver_t* drv, zx_device_t* parent, device_add_args_t* args,
zx_device_t** out) {
*out = reinterpret_cast<zx_device_t*>(reinterpret_cast<char*>(kFakeChild) + total_children_);
children_++;
total_children_++;
devices_[parent].children.push_back(*out);
if (args && args->ops && args->ops->message) {
auto loop = std::make_unique<fake_ddk::FidlMessenger>(&kAsyncLoopConfigNoAttachToCurrentThread);
loop->SetMessageOp(args->ctx, args->ops->message);
fidl_loops_.insert({*out, std::move(loop)});
}
DeviceState state;
constexpr device_add_args_t null_args = {};
state.args = args ? *args : null_args;
devices_.insert({*out, state});
return ZX_OK;
}
void Binder::RemoveHelper(DeviceState* state) {
if (state->args.ops->unbind) {
state->args.ops->unbind(state->args.ctx);
}
// unbind all children
for (zx_device_t* dev : state->children) {
auto child = devices_.find(dev);
if (child != devices_.end()) {
RemoveHelper(&child->second);
children_--;
devices_.erase(child);
}
}
if (state->args.ops->release) {
state->args.ops->release(state->args.ctx);
}
}
void Binder::DeviceAsyncRemove(zx_device_t* device) {
auto state = devices_.find(device);
if (state == devices_.end()) {
printf("Unrecognized device %p\n", device);
return;
}
RemoveHelper(&state->second);
devices_.erase(state);
}
zx_status_t Binder::DeviceGetProtocol(const zx_device_t* device, uint32_t proto_id,
void* protocol) {
auto out = reinterpret_cast<fake_ddk::Protocol*>(protocol);
if (proto_id == ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL) {
const auto& p = display_->dcimpl_proto();
out->ops = p->ops;
out->ctx = p->ctx;
return ZX_OK;
}
if (proto_id == ZX_PROTOCOL_DISPLAY_CLAMP_RGB_IMPL) {
const auto& p = display_->clamp_rgbimpl_proto();
out->ops = p->ops;
out->ctx = p->ctx;
return ZX_OK;
}
for (const auto& proto : protocols_) {
if (proto_id == proto.id) {
out->ops = proto.proto.ops;
out->ctx = proto.proto.ctx;
return ZX_OK;
}
}
return ZX_ERR_NOT_SUPPORTED;
}
bool Binder::Ok() {
if (devices_.empty()) {
EXPECT_EQ(children_, 0);
return children_ == 0;
} else {
EXPECT_TRUE(devices_.size() == 1);
EXPECT_TRUE(devices_.begin()->first == fake_ddk::kFakeParent);
return devices_.size() == 1 && devices_.begin()->first == fake_ddk::kFakeParent;
}
}
zx_status_t Binder::DeviceGetMetadataSize(zx_device_t* dev, uint32_t type, size_t* out_size) {
if (type == SYSMEM_METADATA) {
*out_size = sizeof(sysmem_metadata_);
return ZX_OK;
}
return ZX_ERR_INVALID_ARGS;
}
zx_status_t Binder::DeviceGetMetadata(zx_device_t* dev, uint32_t type, void* data, size_t length,
size_t* actual) {
if (type == SYSMEM_METADATA) {
*actual = sizeof(sysmem_metadata_);
if (length < *actual) {
return ZX_ERR_NO_MEMORY;
}
*static_cast<sysmem_metadata_t*>(data) = sysmem_metadata_;
return ZX_OK;
}
return ZX_ERR_INVALID_ARGS;
}
FakeDisplayDeviceTree::FakeDisplayDeviceTree(std::unique_ptr<SysmemDeviceWrapper> sysmem,
bool start_vsync)
: composite_(fake_ddk::kFakeParent), sysmem_(std::move(sysmem)) {
fbl::Array<fake_ddk::ProtocolEntry> protocols(new fake_ddk::ProtocolEntry[4], 4);
protocols[0] = {ZX_PROTOCOL_COMPOSITE,
*reinterpret_cast<const fake_ddk::Protocol*>(composite_.proto())};
protocols[1] = {ZX_PROTOCOL_PBUS, *reinterpret_cast<const fake_ddk::Protocol*>(pbus_.proto())};
protocols[2] = {ZX_PROTOCOL_PDEV, *reinterpret_cast<const fake_ddk::Protocol*>(pdev_.proto())};
protocols[3] = {ZX_PROTOCOL_SYSMEM,
*reinterpret_cast<const fake_ddk::Protocol*>(sysmem_->proto())};
ddk_.SetProtocols(std::move(protocols));
EXPECT_OK(sysmem_->Bind());
display_ = new fake_display::FakeDisplay(fake_ddk::kFakeParent);
ASSERT_OK(display_->Bind(start_vsync));
ddk_.SetDisplay(display_);
std::unique_ptr<display::Controller> c(new Controller(display_->zxdev()));
// Save a copy for test cases.
controller_ = c.get();
ASSERT_OK(c->Bind(&c));
}
FakeDisplayDeviceTree::~FakeDisplayDeviceTree() {
// AsyncShutdown() must be called before ~FakeDisplayDeviceTree().
ZX_ASSERT(shutdown_);
}
void FakeDisplayDeviceTree::AsyncShutdown() {
if (shutdown_) {
// AsyncShutdown() was already called.
return;
}
shutdown_ = true;
// FIDL loops must be destroyed first to avoid races between cleanup tasks and loop_.
ddk_.ShutdownFIDL();
controller_->DdkAsyncRemove();
display_->DdkAsyncRemove();
ddk_.DeviceAsyncRemove(const_cast<zx_device_t*>(sysmem_->device()));
}
} // namespace display