blob: 97c1f8312f7d4d96222a22e55f379a2ffb6bc98f [file] [log] [blame]
// 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;
}
FDF_LOG(INFO, "Received map interrupt request.");
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;
}
FDF_LOG(INFO, "Received MMIO region request.");
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