blob: 2e2793fca911901f1d7763e0cf3ac20bac377f4e [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/graphics/display/drivers/goldfish-display/display-driver.h"
#include <fidl/fuchsia.driver.framework/cpp/wire.h>
#include <fidl/fuchsia.hardware.display.engine/cpp/driver/wire.h>
#include <fidl/fuchsia.hardware.goldfish/cpp/wire.h>
#include <fidl/fuchsia.sysmem2/cpp/wire.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/driver/outgoing/cpp/outgoing_directory.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/zx/result.h>
#include <zircon/errors.h>
#include <zircon/process.h>
#include <zircon/status.h>
#include <memory>
#include <string_view>
#include <vector>
#include <bind/fuchsia/cpp/bind.h>
#include <bind/fuchsia/display/cpp/bind.h>
#include <fbl/alloc_checker.h>
#include "src/graphics/display/drivers/goldfish-display/display-engine.h"
#include "src/graphics/display/drivers/goldfish-display/render_control.h"
#include "src/graphics/display/lib/api-protocols/cpp/display-engine-events-fidl.h"
#include "src/graphics/display/lib/api-protocols/cpp/display-engine-fidl-adapter.h"
namespace goldfish {
namespace {
zx_koid_t GetKoid(zx_handle_t handle) {
zx_info_handle_basic_t info;
zx_status_t status =
zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
}
zx::result<fidl::ClientEnd<fuchsia_sysmem2::Allocator>> CreateAndInitializeSysmemAllocator(
fdf::Namespace* incoming) {
zx::result<fidl::ClientEnd<fuchsia_sysmem2::Allocator>> connect_sysmem_protocol_result =
incoming->Connect<fuchsia_sysmem2::Allocator>();
if (connect_sysmem_protocol_result.is_error()) {
fdf::error("Failed to connect to the sysmem Allocator FIDL protocol: {}",
connect_sysmem_protocol_result);
return connect_sysmem_protocol_result.take_error();
}
fidl::ClientEnd<fuchsia_sysmem2::Allocator> sysmem_allocator =
std::move(connect_sysmem_protocol_result).value();
const zx_koid_t pid = GetKoid(zx_process_self());
static constexpr std::string_view kDebugName = "goldfish-display";
fidl::Arena arena;
fidl::OneWayStatus set_debug_status =
fidl::WireCall(sysmem_allocator)
->SetDebugClientInfo(
fuchsia_sysmem2::wire::AllocatorSetDebugClientInfoRequest::Builder(arena)
.name(kDebugName)
.id(pid)
.Build());
if (!set_debug_status.ok()) {
fdf::error("Failed to set sysmem allocator debug info: {}", set_debug_status.status_string());
return zx::error(set_debug_status.status());
}
return zx::ok(std::move(sysmem_allocator));
}
zx::result<std::unique_ptr<RenderControl>> CreateAndInitializeRenderControl(
fdf::Namespace* incoming) {
zx::result<fidl::ClientEnd<fuchsia_hardware_goldfish_pipe::Bus>>
render_control_connect_pipe_service_result =
incoming->Connect<fuchsia_hardware_goldfish_pipe::Service::Device>();
if (render_control_connect_pipe_service_result.is_error()) {
fdf::error("Failed to connect to the goldfish pipe FIDL service: {}",
render_control_connect_pipe_service_result);
return render_control_connect_pipe_service_result.take_error();
}
fidl::ClientEnd<fuchsia_hardware_goldfish_pipe::Bus> render_control_pipe =
std::move(render_control_connect_pipe_service_result).value();
fbl::AllocChecker alloc_checker;
auto render_control = fbl::make_unique_checked<RenderControl>(&alloc_checker);
if (!alloc_checker.check()) {
fdf::error("Failed to allocate memory for RenderControl");
return zx::error(ZX_ERR_NO_MEMORY);
}
zx_status_t status =
render_control->InitRcPipe(fidl::WireSyncClient(std::move(render_control_pipe)));
if (status != ZX_OK) {
fdf::error("Failed to initialize RenderControl: {}", status);
return zx::error(status);
}
return zx::ok(std::move(render_control));
}
} // namespace
DisplayDriver::DisplayDriver(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: fdf::DriverBase("goldfish-display", std::move(start_args), std::move(driver_dispatcher)) {}
DisplayDriver::~DisplayDriver() = default;
zx::result<> DisplayDriver::Start() {
zx::result<fidl::ClientEnd<fuchsia_hardware_goldfish::ControlDevice>>
connect_control_service_result =
incoming()->Connect<fuchsia_hardware_goldfish::ControlService::Device>();
if (connect_control_service_result.is_error()) {
fdf::error("Failed to connect to the goldfish Control FIDL service: {}",
connect_control_service_result);
return connect_control_service_result.take_error();
}
fidl::ClientEnd<fuchsia_hardware_goldfish::ControlDevice> control =
std::move(connect_control_service_result).value();
zx::result<fidl::ClientEnd<fuchsia_hardware_goldfish_pipe::Bus>> connect_pipe_service_result =
incoming()->Connect<fuchsia_hardware_goldfish_pipe::Service::Device>();
if (connect_pipe_service_result.is_error()) {
fdf::error("Failed to connect to the goldfish pipe FIDL service: {}",
connect_pipe_service_result);
return connect_pipe_service_result.take_error();
}
fidl::ClientEnd<fuchsia_hardware_goldfish_pipe::Bus> pipe =
std::move(connect_pipe_service_result).value();
zx::result<fidl::ClientEnd<fuchsia_sysmem2::Allocator>> create_sysmem_allocator_result =
CreateAndInitializeSysmemAllocator(incoming().get());
if (create_sysmem_allocator_result.is_error()) {
fdf::error("Failed to create and initialize sysmem allocator: {}",
create_sysmem_allocator_result);
return create_sysmem_allocator_result.take_error();
}
fidl::ClientEnd<fuchsia_sysmem2::Allocator> sysmem_allocator =
std::move(create_sysmem_allocator_result).value();
zx::result<std::unique_ptr<RenderControl>> create_render_control_result =
CreateAndInitializeRenderControl(incoming().get());
if (create_render_control_result.is_error()) {
fdf::error("Failed to create and initialize RenderControl: {}", create_render_control_result);
return create_render_control_result.take_error();
}
std::unique_ptr<RenderControl> render_control = std::move(create_render_control_result).value();
zx::result<fdf::SynchronizedDispatcher> create_dispatcher_result =
fdf::SynchronizedDispatcher::Create(fdf::SynchronizedDispatcher::Options{},
"display-event-dispatcher",
/*shutdown_handler=*/[](fdf_dispatcher_t*) {});
if (create_dispatcher_result.is_error()) {
fdf::error("Failed to create display event dispatcher: {}", create_dispatcher_result);
return create_dispatcher_result.take_error();
}
display_event_dispatcher_ = std::move(create_dispatcher_result).value();
fbl::AllocChecker alloc_checker;
engine_events_ = fbl::make_unique_checked<display::DisplayEngineEventsFidl>(&alloc_checker);
if (!alloc_checker.check()) {
fdf::error("Failed to allocate memory for DisplayEngineEventsFidl");
return zx::error(ZX_ERR_NO_MEMORY);
}
display_engine_ = fbl::make_unique_checked<DisplayEngine>(
&alloc_checker, std::move(control), std::move(pipe), std::move(sysmem_allocator),
std::move(render_control), display_event_dispatcher_.async_dispatcher(),
engine_events_.get());
if (!alloc_checker.check()) {
fdf::error("Failed to allocate memory for DisplayEngine");
return zx::error(ZX_ERR_NO_MEMORY);
}
zx::result<> init_result = display_engine_->Initialize();
if (init_result.is_error()) {
fdf::error("Failed to initialize DisplayEngine: {}", init_result);
return init_result.take_error();
}
engine_fidl_adapter_ = fbl::make_unique_checked<display::DisplayEngineFidlAdapter>(
&alloc_checker, display_engine_.get(), engine_events_.get());
if (!alloc_checker.check()) {
fdf::error("Failed to allocate memory for DisplayEngineFidlAdapter");
return zx::error(ZX_ERR_NO_MEMORY);
}
fuchsia_hardware_display_engine::Service::InstanceHandler service_handler(
{.engine = engine_fidl_adapter_->CreateHandler(*(driver_dispatcher()->get()))});
zx::result<> add_service_result =
outgoing()->AddService<fuchsia_hardware_display_engine::Service>(std::move(service_handler));
if (add_service_result.is_error()) {
fdf::error("Failed to add service: {}", add_service_result);
return add_service_result.take_error();
}
const std::vector<fuchsia_driver_framework::NodeProperty> node_properties = {
fdf::MakeProperty(bind_fuchsia::PROTOCOL, bind_fuchsia_display::BIND_PROTOCOL_ENGINE),
};
const std::vector<fuchsia_driver_framework::Offer> node_offers = {
fdf::MakeOffer2<fuchsia_hardware_display_engine::Service>()};
zx::result<fidl::ClientEnd<fuchsia_driver_framework::NodeController>> controller_client_result =
AddChild(name(), node_properties, node_offers);
if (controller_client_result.is_error()) {
fdf::error("Failed to add child node: {}", controller_client_result);
return controller_client_result.take_error();
}
controller_ = fidl::WireSyncClient(std::move(controller_client_result).value());
return zx::ok();
}
} // namespace goldfish
FUCHSIA_DRIVER_EXPORT(goldfish::DisplayDriver);