Files

158 lines
5.8 KiB
C

// Optional firmware-event driver using the resident ACPICA namespace.
#include <ntlite/ddk/module.h>
#include <ntlite/atomic.h>
#include <ntlite/ddk/platform.h>
#include <ntlite/ddk/debug.h>
#include <ntlite/ddk/power.h>
#include "third_party/acpica/include/acpi.h"
#include "third_party/acpica/include/aclocal.h"
#include "third_party/acpica/include/acobject.h"
#include "third_party/acpica/include/acglobal.h"
#include "acpi_local.h"
#include <ntlite/ddk/irql.h>
#include <ntlite/syscall.h>
// Firmware callbacks remain resident until the final ACPI S5 transition.
static KSTATUS AcpiNtlPreparePower(void* context, uint32_t action) {
(void)context;
if (action != SYS_POWER_ACTION_SHUTDOWN) return NTLSTATUS_NOT_SUPPORTED;
ACPI_STATUS status = AcpiEnterSleepStatePrep(ACPI_STATE_S5);
if (ACPI_FAILURE(status)) {
DbgPrint("acpi: S5 preparation failed status=0x%x\n", (unsigned)status);
return NTLSTATUS_NOT_SUPPORTED;
}
return NTLSTATUS_SUCCESS;
}
static KSTATUS AcpiNtlEnterPower(void* context, uint32_t action) {
(void)context;
if (action != SYS_POWER_ACTION_SHUTDOWN) return NTLSTATUS_NOT_SUPPORTED;
// ACPICA requires interrupts disabled for the final sleep-control writes.
uintptr_t flags = KeDisableInterrupts();
ACPI_STATUS status = AcpiEnterSleepState(ACPI_STATE_S5);
KeRestoreInterrupts(flags);
DbgPrint("acpi: S5 transition returned status=0x%x\n", (unsigned)status);
return NTLSTATUS_NOT_SUPPORTED;
}
static volatile int32_t gFixedButtonPending;
static uint32_t gFixedButtonEvents;
static uint32_t gMethodButtonEvents;
static uint32_t gMethodButtons;
static void AcpiNtlDeliverButton(void* context) {
(void)context;
KSTATUS result = KeNotifyPowerButton();
if (!KSUCCESS(result)) DbgPrint("acpi: power button delivery failed status=0x%x\n", result);
}
static void AcpiNtlDeliverFixedButton(void* context) {
AcpiNtlDeliverButton(context);
NtlAtomicExchange32(&gFixedButtonPending, 0);
}
bool AcpiNtlFixedButtonHasDeferred(void) {
return NtlAtomicLoad32(&gFixedButtonPending) == 2;
}
void AcpiNtlFixedButtonDrainDeferred(void) {
if (NtlAtomicCompareExchange32(&gFixedButtonPending, 1, 2) == 2)
AcpiNtlDeliverFixedButton(NULL);
}
// Fixed-event handlers run in the SCI and delegate delivery to passive work.
static UINT32 AcpiNtlFixedButton(void* context) {
(void)context;
gFixedButtonEvents++;
if (NtlAtomicCompareExchange32(&gFixedButtonPending, 1, 0) == 0 &&
ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, AcpiNtlDeliverFixedButton, NULL))) {
NtlAtomicExchange32(&gFixedButtonPending, 2);
AcpiNtlWakeExecutor();
}
return ACPI_INTERRUPT_HANDLED;
}
// 0x80 is the normal S0 press event; 0x02 is a wake notification.
static void AcpiNtlButtonNotify(ACPI_HANDLE handle, UINT32 event, void* context) {
(void)handle; (void)context;
if (event == 0x80u) {
gMethodButtonEvents++;
AcpiNtlDeliverButton(NULL);
}
}
static ACPI_STATUS AcpiNtlFindButton(ACPI_HANDLE handle, UINT32 depth,
void* context, void** returnValue) {
(void)depth; (void)context; (void)returnValue;
ACPI_STATUS result = AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
AcpiNtlButtonNotify, NULL);
if (ACPI_SUCCESS(result)) gMethodButtons++;
return AE_OK;
}
static KSTATUS AcpiDriverInitialize(NTL_MODULE* module) {
(void)module;
uint64_t root = 0;
KSTATUS status = KeFirmwareQueryAcpiRsdp(&root);
if (!KSUCCESS(status)) {
DbgPrint("acpi: RSDP query failed status=0x%x\n", status);
return status;
}
ACPI_STATUS result = AcpiInitializeSubsystem();
if (ACPI_FAILURE(result)) goto fail;
result = AcpiInitializeTables(NULL, 32, TRUE);
if (ACPI_FAILURE(result)) goto fail;
result = AcpiLoadTables();
if (ACPI_FAILURE(result)) goto fail;
AcpiNtlRestrictRuntimeMappings();
status = AcpiNtlPrepareMmioRegisters();
if (!KSUCCESS(status)) {
DbgPrint("acpi: MMIO register mappings unavailable st=0x%x\n", status);
result = AE_NO_MEMORY;
goto fail;
}
status = AcpiNtlStartExecutor();
if (!KSUCCESS(status)) {
DbgPrint("acpi: event worker unavailable st=0x%x\n", status);
result = AE_NO_MEMORY;
goto fail;
}
result = AcpiEnableSubsystem(0);
if (ACPI_FAILURE(result)) goto fail;
ACPI_STATUS ecStatus = AcpiNtlEcInitialize();
if (ACPI_FAILURE(ecStatus))
DbgPrint("acpi: EC enumeration failed status=0x%x\n", (unsigned)ecStatus);
result = AcpiInitializeObjects(0);
if (ACPI_FAILURE(result)) goto fail;
AcpiNtlEcActivate();
ACPI_STATUS fixedStatus = AE_SUPPORT;
if (!(AcpiGbl_FADT.Flags & ACPI_FADT_POWER_BUTTON)) {
fixedStatus = AcpiInstallFixedEventHandler(
ACPI_EVENT_POWER_BUTTON, AcpiNtlFixedButton, NULL);
}
ACPI_STATUS methodStatus = AcpiGetDevices("PNP0C0C", AcpiNtlFindButton, NULL, NULL);
DbgPrint("acpi: ready rsdp=0x%016llX fixed=0x%x method=0x%x buttons=%u ec=%u\n",
(unsigned long long)root, (unsigned)fixedStatus,
(unsigned)methodStatus, gMethodButtons, AcpiNtlEcCount());
const NTL_FIRMWARE_POWER_PROVIDER provider = {
.Size = sizeof(NTL_FIRMWARE_POWER_PROVIDER),
.Context = NULL,
.Prepare = AcpiNtlPreparePower,
.Enter = AcpiNtlEnterPower,
};
status = KeRegisterFirmwarePowerProvider(&provider);
if (!KSUCCESS(status)) DbgPrint("acpi: power provider registration failed status=0x%x\n", status);
return NTLSTATUS_SUCCESS;
fail:
DbgPrint("acpi: namespace initialization failed status=0x%x\n",
(unsigned)result);
AcpiNtlEcTerminate();
(void)AcpiTerminate();
AcpiNtlStopExecutor();
AcpiNtlReleaseMmioRegisters();
return NTLSTATUS_NOT_SUPPORTED;
}
NTL_DECLARE_MODULE(AcpiDriverInitialize);