blob: c92e8a3e5a96df02fe2a442388c2ea3593e6c7eb [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_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);
}
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;
}
}
FakeDisplayDeviceTree::FakeDisplayDeviceTree(std::unique_ptr<SysmemDeviceWrapper> sysmem,
bool start_vsync)
: sysmem_(std::move(sysmem)) {
pdev_.UseFakeBti();
ddk_.SetMetadata(SYSMEM_METADATA, &sysmem_metadata_, sizeof(sysmem_metadata_));
// Protocols for sysmem
ddk_.SetProtocol(ZX_PROTOCOL_PBUS, pbus_.proto());
ddk_.SetProtocol(ZX_PROTOCOL_PDEV, pdev_.proto());
EXPECT_OK(sysmem_->Bind());
// Fragments for fake-display
fbl::Array<fake_ddk::FragmentEntry> fragments(new fake_ddk::FragmentEntry[2], 2);
fragments[0] = pdev_.fragment();
fragments[1].name = "sysmem";
fragments[1].protocols.emplace_back(fake_ddk::ProtocolEntry{
ZX_PROTOCOL_SYSMEM, *reinterpret_cast<const fake_ddk::Protocol*>(sysmem_->proto())});
ddk_.SetFragments(std::move(fragments));
display_ = new fake_display::FakeDisplay(fake_ddk::kFakeParent);
ASSERT_OK(display_->Bind(start_vsync));
// Protocols for display controller.
ddk_.SetProtocol(ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL, display_->dcimpl_proto());
ddk_.SetProtocol(ZX_PROTOCOL_DISPLAY_CAPTURE_IMPL, display_->capture_proto());
ddk_.SetProtocol(ZX_PROTOCOL_DISPLAY_CLAMP_RGB_IMPL, display_->clamp_rgbimpl_proto());
std::unique_ptr<display::Controller> c(new Controller(fake_ddk::kFakeParent));
// 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();
display_->DdkChildPreRelease(controller_);
controller_->DdkAsyncRemove();
display_->DdkAsyncRemove();
ddk_.DeviceAsyncRemove(const_cast<zx_device_t*>(sysmem_->device()));
}
} // namespace display