blob: 00135d8a7f75ea01cb7ff233ce0ead0d19dc0456 [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.hardware.goldfish/cpp/wire.h>
#include <fidl/fuchsia.hardware.sysmem/cpp/wire.h>
#include <fidl/fuchsia.sysmem/cpp/wire.h>
#include <fuchsia/hardware/display/controller/c/banjo.h>
#include <lib/driver/compat/cpp/banjo_server.h>
#include <lib/driver/compat/cpp/device_server.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/driver/logging/cpp/logger.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 <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"
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_sysmem::Allocator>> CreateAndInitializeSysmemAllocator(
fdf::Namespace* incoming) {
zx::result<fidl::ClientEnd<fuchsia_sysmem::Allocator>> connect_sysmem_service_result =
incoming->Connect<fuchsia_hardware_sysmem::Service::AllocatorV1>();
if (connect_sysmem_service_result.is_error()) {
FDF_LOG(ERROR, "Failed to connect to the sysmem Allocator FIDL protocol: %s",
connect_sysmem_service_result.status_string());
return connect_sysmem_service_result.take_error();
}
fidl::ClientEnd<fuchsia_sysmem::Allocator> sysmem_allocator =
std::move(connect_sysmem_service_result).value();
const zx_koid_t pid = GetKoid(zx_process_self());
static constexpr std::string_view kDebugName = "goldfish-display";
fidl::OneWayStatus set_debug_status =
fidl::WireCall(sysmem_allocator)
->SetDebugClientInfo(fidl::StringView::FromExternal(kDebugName), pid);
if (!set_debug_status.ok()) {
FDF_LOG(ERROR, "Failed to set sysmem allocator debug info: %s",
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::GoldfishPipe>>
render_control_connect_pipe_service_result =
incoming->Connect<fuchsia_hardware_goldfish_pipe::Service::Device>();
if (render_control_connect_pipe_service_result.is_error()) {
FDF_LOG(ERROR, "Failed to connect to the goldfish pipe FIDL service: %s",
render_control_connect_pipe_service_result.status_string());
return render_control_connect_pipe_service_result.take_error();
}
fidl::ClientEnd<fuchsia_hardware_goldfish_pipe::GoldfishPipe> 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_LOG(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_LOG(ERROR, "Failed to initialize RenderControl: %d", 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_LOG(ERROR, "Failed to connect to the goldfish Control FIDL service: %s",
connect_control_service_result.status_string());
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::GoldfishPipe>>
connect_pipe_service_result =
incoming()->Connect<fuchsia_hardware_goldfish_pipe::Service::Device>();
if (connect_pipe_service_result.is_error()) {
FDF_LOG(ERROR, "Failed to connect to the goldfish pipe FIDL service: %s",
connect_pipe_service_result.status_string());
return connect_pipe_service_result.take_error();
}
fidl::ClientEnd<fuchsia_hardware_goldfish_pipe::GoldfishPipe> pipe =
std::move(connect_pipe_service_result).value();
zx::result<fidl::ClientEnd<fuchsia_sysmem::Allocator>> create_sysmem_allocator_result =
CreateAndInitializeSysmemAllocator(incoming().get());
if (create_sysmem_allocator_result.is_error()) {
FDF_LOG(ERROR, "Failed to create and initialize sysmem allocator: %s",
create_sysmem_allocator_result.status_string());
return create_sysmem_allocator_result.take_error();
}
fidl::ClientEnd<fuchsia_sysmem::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_LOG(ERROR, "Failed to create and initialize RenderControl: %s",
create_render_control_result.status_string());
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=*/{});
if (create_dispatcher_result.is_error()) {
FDF_LOG(ERROR, "Failed to create display event dispatcher: %s",
create_dispatcher_result.status_string());
return create_dispatcher_result.take_error();
}
display_event_dispatcher_ = std::move(create_dispatcher_result).value();
fbl::AllocChecker alloc_checker;
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());
if (!alloc_checker.check()) {
FDF_LOG(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_LOG(ERROR, "Failed to initialize DisplayEngine: %s", init_result.status_string());
return init_result.take_error();
}
// Serves the [`fuchsia.hardware.display.controller/ControllerImpl`] protocol
// over the compatibility server.
banjo_server_ =
compat::BanjoServer(ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL, /*ctx=*/display_engine_.get(),
/*ops=*/display_engine_->display_controller_impl_protocol_ops());
compat::DeviceServer::BanjoConfig banjo_config;
banjo_config.callbacks[ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL] = banjo_server_->callback();
zx::result<> compat_server_init_result =
compat_server_.Initialize(incoming(), outgoing(), node_name(), name(),
/*forward_metadata=*/compat::ForwardMetadata::None(),
/*banjo_config=*/std::move(banjo_config));
if (compat_server_init_result.is_error()) {
return compat_server_init_result.take_error();
}
const std::vector<fuchsia_driver_framework::NodeProperty> node_properties = {
fdf::MakeProperty(bind_fuchsia::PROTOCOL,
bind_fuchsia_display::BIND_PROTOCOL_CONTROLLER_IMPL),
};
const std::vector<fuchsia_driver_framework::Offer> node_offers = compat_server_.CreateOffers2();
zx::result<fidl::ClientEnd<fuchsia_driver_framework::NodeController>> controller_client_result =
AddChild(name(), node_properties, node_offers);
if (controller_client_result.is_error()) {
FDF_LOG(ERROR, "Failed to add child node: %s", controller_client_result.status_string());
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);