blob: 36284ccda3e45bb461c9a6b254a99e85ec249de5 [file] [log] [blame]
// Copyright 2024 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/bus/drivers/platform/test/power-integration-test/test-power-parent-device.h"
#include <fidl/fuchsia.hardware.platform.device/cpp/fidl.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/driver/power/cpp/power-support.h>
#include <bind/fuchsia/cpp/bind.h>
#include <bind/fuchsia/test/platform/cpp/bind.h>
namespace fake_parent_device {
zx::result<> FakeParent::Start() {
node_.Bind(std::move(node()));
fdf::Arena arena('TEST');
auto device_service =
incoming()->Connect<fuchsia_hardware_platform_device::Service::Device>("platform-device");
if (!device_service.is_ok()) {
return zx::error(ZX_ERR_INTERNAL);
}
fidl::WireSyncClient<fuchsia_hardware_platform_device::Device> device_client(
std::move(device_service.value()));
auto config_result = device_client->GetPowerConfiguration();
auto power_config = config_result->value()->config.data();
// connect to power broker
auto power_broker_req = incoming()->Connect<fuchsia_power_broker::Topology>();
{
if (power_broker_req.is_error()) {
return zx::error(ZX_ERR_INTERNAL);
}
fidl::Endpoints<fuchsia_power_broker::RequiredLevel> required_level =
fidl::CreateEndpoints<fuchsia_power_broker::RequiredLevel>().value();
fidl::Endpoints<fuchsia_power_broker::CurrentLevel> current_level =
fidl::CreateEndpoints<fuchsia_power_broker::CurrentLevel>().value();
fidl::Endpoints<fuchsia_power_broker::Lessor> lessor =
fidl::CreateEndpoints<fuchsia_power_broker::Lessor>().value();
zx::event active, passive;
fidl::ClientEnd<fuchsia_power_broker::Topology> broker = std::move(power_broker_req.value());
fit::result<fdf_power::Error, fdf_power::TokenMap> token_result =
fdf_power::GetDependencyTokens(*incoming(), power_config[0]);
if (token_result.is_error()) {
return zx::error(ZX_ERR_INTERNAL);
}
fdf_power::TokenMap tokens = std::move(token_result.value());
fit::result<fdf_power::Error, fuchsia_power_broker::TopologyAddElementResponse> add_result =
fdf_power::AddElement(
broker, power_config[0], std::move(tokens), active.borrow(), passive.borrow(),
std::pair{std::move(current_level.server), std::move(required_level.server)},
std::move(lessor.server));
topology_client_ =
fidl::WireClient<fuchsia_power_broker::Topology>(std::move(broker), dispatcher());
if (!add_result.is_ok()) {
return zx::error(ZX_ERR_INTERNAL);
}
element_ctrl_ = std::move(add_result->element_control_channel());
}
// Add a child
{
std::string power_element_name = std::string(power_config[0].element().name().data(),
power_config[0].element().name().size());
// First we'll set up a FIDL server that provides a token which gives
// our child access to our power element.
server_ = std::make_unique<fake_parent_device::FakeParentServer>(power_element_name);
{
auto result = outgoing()->AddService<fuchsia_hardware_power::PowerTokenService>(
fuchsia_hardware_power::PowerTokenService::InstanceHandler({
.token_provider =
bindings_.CreateHandler(server_.get(), dispatcher(), fidl::kIgnoreBindingClosure),
}),
power_element_name);
if (!result.is_ok()) {
return zx::error(ZX_ERR_INTERNAL);
}
}
// Next create a node for our child to bind to.
fuchsia_driver_framework::NodeAddArgs node_args;
node_args.name() = "fake-child";
auto properties = std::vector{
fdf::MakeProperty(bind_fuchsia::PLATFORM_DEV_VID,
bind_fuchsia_test_platform::BIND_PLATFORM_DEV_VID_TEST),
fdf::MakeProperty(bind_fuchsia::PLATFORM_DEV_PID,
bind_fuchsia_test_platform::BIND_PLATFORM_DEV_PID_POWER_TEST),
fdf::MakeProperty(bind_fuchsia::PLATFORM_DEV_DID,
bind_fuchsia_test_platform::BIND_PLATFORM_DEV_DID_FAKE_POWER_CHILD),
};
node_args.properties() = properties;
// Create the offer for our token provider so the child can access it.
{
std::vector<fuchsia_component_decl::NameMapping> mappings =
std::vector<fuchsia_component_decl::NameMapping>{fuchsia_component_decl::NameMapping{{
.source_name = power_element_name,
.target_name = power_element_name,
}}};
fuchsia_component_decl::OfferService services = fuchsia_component_decl::OfferService{{
.source_name = std::string(fuchsia_hardware_power::PowerTokenService::Name),
.target_name = std::string(fuchsia_hardware_power::PowerTokenService::Name),
.source_instance_filter = std::vector<std::string>{power_element_name},
.renamed_instances = mappings,
}};
auto component_offer_decl = fuchsia_component_decl::Offer::WithService(services);
node_args.offers2() = std::vector<fuchsia_driver_framework::Offer>{
fuchsia_driver_framework::Offer::WithZirconTransport(component_offer_decl),
};
}
// The token service is ready, the child is defined, so we're ready to
// add the child device node
{
auto endpoints = fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>().value();
auto result = node_->AddChild(fidl::ToWire(arena, std::move(node_args)),
std::move(endpoints.server), {});
if (!result.ok()) {
return zx::error(ZX_ERR_INTERNAL);
}
if (result->is_error()) {
return zx::error(ZX_ERR_INTERNAL);
}
child_controller_.Bind(std::move(endpoints.client));
}
}
return zx::ok();
}
void FakeParentServer::GetToken(GetTokenCompleter::Sync& completer) {
zx_handle_t mine;
zx_event_create(0, &mine);
zx_handle_t yours;
zx_handle_duplicate(mine, ZX_RIGHT_SAME_RIGHTS, &yours);
completer.ReplySuccess(zx::event(yours), fidl::StringView::FromExternal(element_name_));
}
void FakeParentServer::handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_hardware_power::PowerTokenProvider> md,
fidl::UnknownMethodCompleter::Sync& completer) {}
} // namespace fake_parent_device
FUCHSIA_DRIVER_EXPORT(fake_parent_device::FakeParent);