142 lines
5.3 KiB
C
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);
|