Files
2026-10-27 09:28:31 +01:00

142 lines
5.3 KiB
C

// Optional firmware-event driver using the resident ACPICA namespace.
#include <ntlite/ddk/module.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 <intrin.h>
#pragma intrinsic(_enable)
#pragma intrinsic(_disable)
#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;
}
DbgPrint("acpi: S5 firmware preparation complete\n");
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.
unsigned __int64 rflags = __readeflags();
_disable();
ACPI_STATUS status = AcpiEnterSleepState(ACPI_STATE_S5);
if (rflags & (1ULL << 9)) _enable();
DbgPrint("acpi: S5 transition returned status=0x%x\n", (unsigned)status);
return NTLSTATUS_NOT_SUPPORTED;
}
static uint32_t gFixedButtonEvents;
static uint32_t gMethodButtonEvents;
static uint32_t gMethodButtons;
static void AcpiNtlDeliverButton(void* context) {
(void)context;
KSTATUS result = KeNotifyPowerButton();
DbgPrint("acpi: button press delivered st=0x%x\n", result);
}
// Fixed-event handlers run in the SCI and only queue passive work.
static UINT32 AcpiNtlFixedButton(void* context) {
(void)context;
gFixedButtonEvents++;
(void)AcpiOsExecute(OSL_NOTIFY_HANDLER, AcpiNtlDeliverButton, NULL);
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: firmware tables unavailable st=0x%x; driver inactive\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: events active 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);
DbgPrint("acpi: S5 firmware provider registration st=0x%x\n", status);
return NTLSTATUS_SUCCESS;
fail:
DbgPrint("acpi: hardware/namespace initialization failed status=0x%x; driver inactive\n",
(unsigned)result);
AcpiNtlEcTerminate();
(void)AcpiTerminate();
AcpiNtlStopExecutor();
AcpiNtlReleaseMmioRegisters();
return NTLSTATUS_NOT_SUPPORTED;
}
NTL_DECLARE_MODULE(AcpiDriverInitialize);