| // Copyright 2022 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 "acpi_controller.h" |
| |
| #include <bind/fuchsia/acpi/cpp/bind.h> |
| #include <bind/fuchsia/hardware/acpi/cpp/bind.h> |
| |
| namespace acpi_multiply { |
| |
| zx::result<> AcpiMultiplyController::Start() { |
| node_.Bind(std::move(node())); |
| |
| auto server_result = InitializeServer(); |
| if (server_result.is_error()) { |
| FDF_SLOG(ERROR, "Failed to initialize ACPI Server.", |
| KV("status", server_result.status_string())); |
| return server_result.take_error(); |
| } |
| |
| // Serve the fuchsia.hardware.acpi/Device protocol to clients through the |
| // fuchsia.hardware.acpi/Service wrapper. |
| component::ServiceInstanceHandler handler; |
| fuchsia_hardware_acpi::Service::Handler service(&handler); |
| auto result = |
| service.add_device([this](fidl::ServerEnd<fuchsia_hardware_acpi::Device> request) -> void { |
| AcpiDeviceServer::BindDeviceClient(server_, dispatcher(), std::move(request)); |
| }); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| |
| result = context().outgoing()->AddService<fuchsia_hardware_acpi::Service>(std::move(handler)); |
| if (result.is_error()) { |
| FDF_SLOG(ERROR, "Failed to add service.", KV("status", result.status_string())); |
| return result.take_error(); |
| } |
| |
| // Publish `fuchsia.driver.compat.Service` to the outgoing directory. |
| child_ = compat::DeviceServer(std::string(name()), 0, std::string(name())); |
| auto status = child_->Serve(dispatcher(), context().outgoing().get()); |
| if (status != ZX_OK) { |
| FDF_SLOG(ERROR, "Failed to serve compat device server.", |
| KV("status", zx_status_get_string(status))); |
| return zx::error(status); |
| } |
| |
| // Create a child node and offer capabilities to bound drivers. |
| result = AddChild(); |
| if (!result.is_ok()) { |
| FDF_SLOG(ERROR, "Failed to create child node.", KV("status", result.status_string())); |
| return result.take_error(); |
| } |
| |
| return zx::ok(); |
| } |
| |
| zx::result<> AcpiMultiplyController::InitializeServer() { |
| // Allocate an MMIO buffer representing the ACPI multiply device |
| zx::vmo vmo; |
| zx_status_t raw_status = zx::vmo::create(zx_system_get_page_size(), 0, &vmo); |
| if (raw_status != ZX_OK) { |
| FDF_SLOG(ERROR, "Failed to create vmo.", KV("status", zx_status_get_string(raw_status))); |
| return zx::error(raw_status); |
| } |
| |
| auto buffer = fdf::MmioBuffer::Create(0, zx_system_get_page_size(), std::move(vmo), |
| ZX_CACHE_POLICY_UNCACHED_DEVICE); |
| if (buffer.is_error()) { |
| FDF_SLOG(ERROR, "Failed to create mmio buffer.", KV("status", buffer.status_string())); |
| return buffer.take_error(); |
| } |
| |
| // Create an IRQ resource for the ACPI multiply device |
| zx::interrupt irq; |
| raw_status = zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &irq); |
| if (raw_status != ZX_OK) { |
| FDF_SLOG(ERROR, "Failed to create interrupt.", KV("status", zx_status_get_string(raw_status))); |
| return zx::error(raw_status); |
| } |
| |
| server_ = std::make_shared<AcpiDeviceServer>(std::move(irq), std::move(buffer.value()), &logger(), |
| dispatcher()); |
| return zx::ok(); |
| } |
| |
| zx::result<> AcpiMultiplyController::AddChild() { |
| fidl::Arena arena; |
| |
| auto offers = child_->CreateOffers(arena); |
| // Offer fuchsia.hardware.acpi.Service to the driver that binds to the node. |
| auto service_offer = |
| driver::MakeOffer<fuchsia_hardware_acpi::Service>(arena, component::kDefaultInstance); |
| offers.push_back(service_offer); |
| |
| // Set the properties of the node that a driver binds to. |
| auto properties = fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty>(arena, 2); |
| properties[0] = driver::MakeProperty(arena, bind_fuchsia_hardware_acpi::SERVICE, |
| bind_fuchsia_hardware_acpi::SERVICE_ZIRCONTRANSPORT); |
| properties[1] = driver::MakeProperty(arena, bind_fuchsia_acpi::HID, "FDFS0001"); |
| |
| auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) |
| .name(arena, "acpi-child") |
| .offers(offers) |
| .properties(properties) |
| .Build(); |
| |
| // Create endpoints of the `NodeController` for the node. |
| auto endpoints = fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>(); |
| if (endpoints.is_error()) { |
| FDF_SLOG(ERROR, "Failed to create endpoint.", KV("status", endpoints.status_string())); |
| return endpoints.take_error(); |
| } |
| |
| auto result = node_->AddChild(args, std::move(endpoints->server), {}); |
| if (!result.ok()) { |
| FDF_SLOG(ERROR, "Failed to add child.", KV("error_desc", result.FormatDescription().c_str())); |
| return zx::error(result.status()); |
| } |
| |
| if (result->is_error()) { |
| FDF_SLOG(ERROR, "Failed to add child.", |
| KV("status", std::to_string(static_cast<uint32_t>(result->error_value())).c_str())); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| controller_.Bind(std::move(endpoints->client)); |
| |
| return zx::ok(); |
| } |
| |
| } // namespace acpi_multiply |
| |
| FUCHSIA_DRIVER_RECORD_CPP_V2(driver::Record<acpi_multiply::AcpiMultiplyController>); |