blob: e37d38553c6ccaa69d5bf172f9e977431e2cf617 [file]
// 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/virtio-gpu-display/cpp/gpu-device-driver.h"
#include <lib/driver/component/cpp/driver_export2.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/driver/component/cpp/start_completer.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/driver/outgoing/cpp/outgoing_directory.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/virtio/driver_utils.h>
#include <lib/zx/result.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <cstring>
#include <memory>
#include <utility>
#include <fbl/alloc_checker.h>
#include "src/graphics/display/drivers/virtio-gpu-display/cpp/display-engine.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 virtio_display {
zx::result<> GpuDeviceDriver::InitResources(const fdf::Namespace& incoming) {
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);
}
zx::result sysmem_client_result = incoming.Connect<fuchsia_sysmem2::Allocator>();
if (sysmem_client_result.is_error()) {
fdf::error("Failed to get sysmem protocol: {}", sysmem_client_result);
return sysmem_client_result.take_error();
}
fidl::ClientEnd<fuchsia_sysmem2::Allocator> sysmem_client =
std::move(sysmem_client_result).value();
zx::result<fidl::ClientEnd<fuchsia_hardware_pci::Device>> pci_client_result =
incoming.Connect<fuchsia_hardware_pci::Service::Device>();
if (pci_client_result.is_error()) {
fdf::error("Failed to get pci client: {}", pci_client_result);
return pci_client_result.take_error();
}
zx::result<std::pair<zx::bti, std::unique_ptr<virtio::Backend>>> bti_and_backend_result =
virtio::GetBtiAndBackend(std::move(pci_client_result).value());
if (!bti_and_backend_result.is_ok()) {
fdf::error("GetBtiAndBackend failed: {}", bti_and_backend_result);
return bti_and_backend_result.take_error();
}
auto [bti, backend] = std::move(bti_and_backend_result).value();
zx::result<std::unique_ptr<DisplayEngine>> display_engine_result = DisplayEngine::Create(
std::move(sysmem_client), std::move(bti), std::move(backend), engine_events_.get());
if (display_engine_result.is_error()) {
// DisplayEngine::Create() logs on error.
return display_engine_result.take_error();
}
display_engine_ = std::move(display_engine_result).value();
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);
}
gpu_control_server_ = fbl::make_unique_checked<GpuControlServer>(
&alloc_checker, this, display_engine_->pci_device().GetCapabilitySetLimit());
if (!alloc_checker.check()) {
fdf::error("Failed to allocate memory for GpuControlServer");
return zx::error(ZX_ERR_NO_MEMORY);
}
return zx::ok();
}
zx::result<> GpuDeviceDriver::InitDisplayNode() {
// Serve `fuchsia.hardware.display.engine/Service` at the outgoing directory.
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::Offer> node_offers = {
fdf::MakeOffer2<fuchsia_hardware_display_engine::Service>()};
static constexpr std::string_view kDisplayChildNodeName = "virtio-gpu-display";
zx::result<fidl::ClientEnd<fuchsia_driver_framework::NodeController>>
display_node_controller_client_result =
AddChild(kDisplayChildNodeName, std::span<const fuchsia_driver_framework::NodeProperty>(),
node_offers);
if (display_node_controller_client_result.is_error()) {
fdf::error("Failed to add child node: {}", display_node_controller_client_result);
return display_node_controller_client_result.take_error();
}
display_node_controller_ =
fidl::WireSyncClient(std::move(display_node_controller_client_result).value());
return zx::ok();
}
zx::result<> GpuDeviceDriver::InitGpuControlNode() {
async_dispatcher_t* dispatcher = fdf::Dispatcher::GetCurrent()->async_dispatcher();
zx::result<> add_service_result = outgoing()->AddService<fuchsia_gpu_virtio::Service>(
gpu_control_server_->GetInstanceHandler(dispatcher),
/*instance=*/component::kDefaultInstance);
if (add_service_result.is_error()) {
fdf::error("Failed to add fuchsia.gpu.virtio service to the outgoing directory: {}",
add_service_result);
return add_service_result.take_error();
}
static constexpr std::string_view kGpuControlChildNodeName = "virtio-gpu-control";
const fuchsia_driver_framework::Offer node_offers[] = {
fdf::MakeOffer2<fuchsia_gpu_virtio::Service>(component::kDefaultInstance),
};
zx::result<fidl::ClientEnd<fuchsia_driver_framework::NodeController>>
gpu_control_node_controller_client_result = AddChild(
kGpuControlChildNodeName, std::span<const fuchsia_driver_framework::NodeProperty>(),
std::span<const fuchsia_driver_framework::Offer>(node_offers, 1));
if (gpu_control_node_controller_client_result.is_error()) {
fdf::error("Failed to add child node: {}", gpu_control_node_controller_client_result);
return gpu_control_node_controller_client_result.take_error();
}
gpu_control_node_controller_ =
fidl::WireSyncClient(std::move(gpu_control_node_controller_client_result).value());
return zx::ok();
}
GpuDeviceDriver::GpuDeviceDriver() : fdf::DriverBase2("virtio-gpu-display") {}
GpuDeviceDriver::~GpuDeviceDriver() {}
void GpuDeviceDriver::Start(fdf::DriverContext context, fdf::StartCompleter completer) {
zx::result<> init_resources_result = InitResources(context.incoming());
if (init_resources_result.is_error()) {
fdf::error("Failed to initialize the resources: {}", init_resources_result);
completer(init_resources_result.take_error());
return;
}
zx::result<> init_display_node_result = InitDisplayNode();
if (init_display_node_result.is_error()) {
fdf::error("Failed to initialize the display child node: {}", init_display_node_result);
completer(init_display_node_result.take_error());
return;
}
zx::result<> init_gpu_control_node_result = InitGpuControlNode();
if (init_gpu_control_node_result.is_error()) {
fdf::error("Failed to initialize the gpu control child node: {}", init_gpu_control_node_result);
completer(init_gpu_control_node_result.take_error());
return;
}
start_thread_ = std::thread([this, completer = std::move(completer)]() mutable {
zx_status_t status = display_engine_->Start();
completer(zx::make_result(status));
});
}
void GpuDeviceDriver::Stop(fdf::StopCompleter completer) {
if (start_thread_.joinable()) {
start_thread_.join();
}
completer(zx::ok());
}
void GpuDeviceDriver::SendHardwareCommand(std::span<uint8_t> request,
std::function<void(std::span<uint8_t>)> callback) {
display_engine_->pci_device().ExchangeControlqVariableLengthRequestResponse(std::move(request),
std::move(callback));
}
} // namespace virtio_display
FUCHSIA_DRIVER_EXPORT2(virtio_display::GpuDeviceDriver);