| // 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_server.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/driver2/structured_logger.h> |
| #include <lib/zx/clock.h> |
| |
| #include "registers.h" |
| |
| namespace acpi_multiply { |
| |
| // Static |
| // Handle incoming connection requests from FIDL clients |
| fidl::ServerBindingRef<fuchsia_hardware_acpi::Device> AcpiDeviceServer::BindDeviceClient( |
| std::shared_ptr<AcpiDeviceServer> server_impl, async_dispatcher_t* dispatcher, |
| fidl::ServerEnd<fuchsia_hardware_acpi::Device> request) { |
| // Bind each connection request to the shared instance. |
| return fidl::BindServer(dispatcher, std::move(request), server_impl, |
| std::mem_fn(&AcpiDeviceServer::OnUnbound)); |
| } |
| |
| // This method is called when a server connection is torn down. |
| void AcpiDeviceServer::OnUnbound(fidl::UnbindInfo info, |
| fidl::ServerEnd<fuchsia_hardware_acpi::Device> server_end) { |
| if (info.is_peer_closed()) { |
| FDF_LOG(DEBUG, "Client disconnected"); |
| } else if (!info.is_user_initiated()) { |
| FDF_LOG(ERROR, "Client connection unbound: %s", info.status_string()); |
| } |
| } |
| |
| // Protocol method for `fuchsia.hardware.acpi/Device` to return the requested IRQ. |
| void AcpiDeviceServer::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 return a requested MMIO region. |
| void AcpiDeviceServer::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 interpret an invoke the control method |
| // associated with the provided object path. |
| void AcpiDeviceServer::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_); |
| auto value2 = acpi_multiply::MultiplyArgumentReg::Get(false).ReadFrom(&buffer_); |
| 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_); |
| status_reg.set_finished(true).WriteTo(&buffer_); |
| 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 |