| // 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 <fidl/fuchsia.component.decl/cpp/wire_types.h> |
| #include <fidl/fuchsia.driver.framework/cpp/wire.h> |
| #include <fidl/fuchsia.hardware.acpi/cpp/wire_types.h> |
| #include <fidl/fuchsia.mem/cpp/wire_types.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/driver2/record_cpp.h> |
| #include <lib/driver2/structured_logger.h> |
| #include <lib/fidl/cpp/wire/connect_service.h> |
| #include <lib/zx/clock.h> |
| #include <zircon/errors.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/types.h> |
| |
| #include "registers.h" |
| |
| namespace acpi_multiply { |
| |
| zx::status<std::unique_ptr<AcpiMultiplyController>> AcpiMultiplyController::Start( |
| fuchsia_driver_framework::wire::DriverStartArgs &start_args, fdf::UnownedDispatcher dispatcher, |
| fidl::WireSharedClient<fuchsia_driver_framework::Node> node, driver::Namespace ns, |
| driver::Logger logger) { |
| auto driver = std::make_unique<AcpiMultiplyController>( |
| dispatcher->async_dispatcher(), std::move(node), std::move(ns), std::move(logger)); |
| |
| auto result = driver->Run(std::move(start_args.outgoing_dir())); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| |
| return zx::ok(std::move(driver)); |
| } |
| |
| zx::status<> AcpiMultiplyController::Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir) { |
| // 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) { |
| 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()) { |
| return buffer.take_error(); |
| } |
| buffer_ = std::move(buffer.value()); |
| |
| // Create an IRQ resource for the ACPI multiply device |
| raw_status = zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &irq_); |
| if (raw_status != ZX_OK) { |
| return zx::error(raw_status); |
| } |
| |
| // Serve `fuchsia.hardware.acpi/Device` to child device nodes |
| auto service = [this](fidl::ServerEnd<fuchsia_hardware_acpi::Device> server_end) { |
| fidl::BindServer(dispatcher_, std::move(server_end), this); |
| }; |
| |
| auto status = outgoing_.AddProtocol<fuchsia_hardware_acpi::Device>(std::move(service)); |
| if (status.is_error()) { |
| FDF_SLOG(ERROR, "Failed to add protocol", KV("status", status.status_string())); |
| return status; |
| } |
| |
| status = AddChild(); |
| if (status.is_error()) { |
| return status; |
| } |
| |
| return outgoing_.Serve(std::move(outgoing_dir)); |
| } |
| |
| zx::status<> AcpiMultiplyController::AddChild() { |
| fidl::Arena arena; |
| |
| // Offer fuchsia.hardware.acpi.Device to the driver that binds to the node. |
| auto protocol_offer = |
| fuchsia_component_decl::wire::OfferProtocol::Builder(arena) |
| .source_name(arena, fidl::DiscoverableProtocolName<fuchsia_hardware_acpi::Device>) |
| .target_name(arena, fidl::DiscoverableProtocolName<fuchsia_hardware_acpi::Device>) |
| .dependency_type(fuchsia_component_decl::wire::DependencyType::kStrong) |
| .Build(); |
| fuchsia_component_decl::wire::Offer offer = |
| fuchsia_component_decl::wire::Offer::WithProtocol(arena, protocol_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] = |
| fuchsia_driver_framework::wire::NodeProperty::Builder(arena) |
| .key(fuchsia_driver_framework::wire::NodePropertyKey::WithIntValue(1 /* BIND_PROTOCOL */)) |
| .value(fuchsia_driver_framework::wire::NodePropertyValue::WithIntValue( |
| 30 /* ZX_PROTOCOL_ACPI */)) |
| .Build(); |
| |
| properties[1] = fuchsia_driver_framework::wire::NodeProperty::Builder(arena) |
| .key(fuchsia_driver_framework::wire::NodePropertyKey::WithStringValue( |
| arena, fidl::StringView::FromExternal("fuchsia.acpi.hid"))) |
| .value(fuchsia_driver_framework::wire::NodePropertyValue::WithStringValue( |
| arena, fidl::StringView::FromExternal("FDFS0001"))) |
| .Build(); |
| |
| auto args = |
| fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) |
| .name(arena, "acpi-child") |
| .offers(fidl::VectorView<fuchsia_component_decl::wire::Offer>::FromExternal(&offer, 1)) |
| .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 zx::error(endpoints.status_value()); |
| } |
| |
| auto result = node_.sync()->AddChild(args, std::move(endpoints->server), {}); |
| if (!result.ok()) { |
| FDF_SLOG(ERROR, "Failed to add child", KV("status", result.status_string())); |
| return zx::error(result.status()); |
| } |
| |
| return zx::ok(); |
| } |
| |
| // Protocol method for `fuchsia.hardware.acpi/Device` to return a requested MMIO region. |
| void AcpiMultiplyController::GetMmio(GetMmioRequestView request, |
| GetMmioCompleter::Sync &completer) { |
| if (request->index != 0) { |
| completer.ReplyError(ZX_ERR_OUT_OF_RANGE); |
| return; |
| } |
| |
| zx::vmo clone; |
| zx_status_t status = buffer_->get_vmo()->duplicate(ZX_RIGHT_SAME_RIGHTS, &clone); |
| if (status != ZX_OK) { |
| completer.ReplyError(status); |
| return; |
| } |
| |
| completer.ReplySuccess(fuchsia_mem::wire::Range{ |
| .vmo = std::move(clone), |
| .offset = 0, |
| .size = zx_system_get_page_size(), |
| }); |
| } |
| |
| // Protocol method for `fuchsia.hardware.acpi/Device` to return the requested IRQ. |
| void AcpiMultiplyController::MapInterrupt(MapInterruptRequestView request, |
| MapInterruptCompleter::Sync &completer) { |
| if (request->index != 0) { |
| completer.ReplyError(ZX_ERR_OUT_OF_RANGE); |
| return; |
| } |
| |
| zx::interrupt clone; |
| zx_status_t status = irq_.duplicate(ZX_RIGHT_SAME_RIGHTS, &clone); |
| if (status != ZX_OK) { |
| completer.ReplyError(status); |
| return; |
| } |
| |
| completer.ReplySuccess(std::move(clone)); |
| } |
| |
| // Protocol method for `fuchsia.hardware.acpi/Device` to interpret an invoke the control method |
| // associated with the provided object path. |
| void AcpiMultiplyController::EvaluateObject(EvaluateObjectRequestView request, |
| EvaluateObjectCompleter::Sync &completer) { |
| if (request->mode != fuchsia_hardware_acpi::wire::EvaluateObjectMode::kPlainObject) { |
| completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotSupported); |
| return; |
| } |
| |
| if (request->path.get() != "_MUL") { |
| completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotFound); |
| return; |
| } |
| |
| fidl::Arena arena; |
| completer.ReplySuccess(fuchsia_hardware_acpi::wire::EncodedObject()); |
| // To simulate asynchronous hardware, we post a delayed task and trigger an interrupt when the |
| // task is complete. |
| async::PostDelayedTask( |
| dispatcher_, |
| [this]() mutable { |
| auto value1 = acpi_multiply::MultiplyArgumentReg::Get(true).ReadFrom(&buffer_.value()); |
| auto value2 = acpi_multiply::MultiplyArgumentReg::Get(false).ReadFrom(&buffer_.value()); |
| auto status_reg = acpi_multiply::MultiplyStatusReg::Get().FromValue(0); |
| auto result_reg = acpi_multiply::MultiplyResultReg::Get().FromValue(0); |
| |
| uint32_t result = value1.operand() * value2.operand(); |
| // Check for overflow |
| if (value1.operand() != 0 && result / value1.operand() != value2.operand()) { |
| status_reg.set_overflow(true); |
| } |
| result_reg.set_result(result); |
| result_reg.WriteTo(&buffer_.value()); |
| status_reg.set_finished(true).WriteTo(&buffer_.value()); |
| zx_status_t status = irq_.trigger(0, zx::clock::get_monotonic()); |
| if (status != ZX_OK) { |
| FDF_SLOG(ERROR, "Failed to trigger interrupt", |
| KV("status", zx_status_get_string(status))); |
| } |
| }, |
| zx::msec(10)); |
| } |
| |
| } // namespace acpi_multiply |
| |
| FUCHSIA_DRIVER_RECORD_CPP_V1(acpi_multiply::AcpiMultiplyController); |