From 571ac5e608fa8a7113ac184c90026256a8d87410 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Tue, 29 Jul 2025 00:51:50 +0300 Subject: [PATCH] [NTOS:PO] Implement SystemBatteryState in NtPowerInformation This should get us proper battery status display. --- ntoskrnl/po/power.c | 138 +++++++++++++++++++++++++++++++++++--- sdk/include/ddk/ntpoapi.h | 3 +- 2 files changed, 131 insertions(+), 10 deletions(-) diff --git a/ntoskrnl/po/power.c b/ntoskrnl/po/power.c index c2f998e7688..18ba3f6e62b 100644 --- a/ntoskrnl/po/power.c +++ b/ntoskrnl/po/power.c @@ -784,6 +784,115 @@ NtInitiatePowerAction(IN POWER_ACTION SystemAction, return STATUS_NOT_IMPLEMENTED; } +static +NTSTATUS +PopQueryBatteryState( + _Out_ PSYSTEM_BATTERY_STATE BatteryState) +{ + static const UNICODE_STRING CompBattDeviceName = + RTL_CONSTANT_STRING(L"\\Device\\CompositeBattery"); + OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES( + &CompBattDeviceName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE); + HANDLE CompBattHandle; + IO_STATUS_BLOCK IoStatusBlock; + BATTERY_QUERY_INFORMATION BatteryQueryInfo = {0}; + BATTERY_INFORMATION BatteryInfo = { 0 }; + BATTERY_STATUS BatteryStatus; + NTSTATUS Status; + + /* Open the CompositeBattery device */ + Status = ZwOpenFile(&CompBattHandle, + GENERIC_READ | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open CompositeBattery device: Status: 0x%08lX\n", Status); + return Status; + } + + /* Query the battery tag */ + ULONG Wait = 0; + Status = ZwDeviceIoControlFile(CompBattHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_BATTERY_QUERY_TAG, + &Wait, + sizeof(Wait), + &BatteryQueryInfo.BatteryTag, + sizeof(BatteryQueryInfo.BatteryTag)); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to query battery tag: Status: 0x%08lX\n", Status); + goto Exit; + } + + /* Query the battery information */ + BatteryQueryInfo.InformationLevel = BatteryInformation; + Status = ZwDeviceIoControlFile(CompBattHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_BATTERY_QUERY_INFORMATION, + &BatteryQueryInfo, + sizeof(BatteryQueryInfo), + &BatteryInfo, + sizeof(BatteryInfo)); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to query battery information: Status: 0x%08lX\n", Status); + } + + /* Query the battery status */ + BATTERY_WAIT_STATUS BatteryWait = {0}; + BatteryWait.BatteryTag = BatteryQueryInfo.BatteryTag; + BatteryWait.PowerState = 0xF ; // Wait for all power states + BatteryWait.HighCapacity = MAXULONG; + Status = ZwDeviceIoControlFile(CompBattHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_BATTERY_QUERY_STATUS, + &BatteryWait, + sizeof(BatteryWait), + &BatteryStatus, + sizeof(BatteryStatus)); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to query battery status: Status: 0x%08lX\n", Status); + goto Exit; + } + + RtlZeroMemory(BatteryState, sizeof(*BatteryState)); + BatteryState->BatteryPresent = TRUE; + BatteryState->AcOnLine = BooleanFlagOn(BatteryStatus.PowerState, BATTERY_POWER_ON_LINE); + BatteryState->Charging = BooleanFlagOn(BatteryStatus.PowerState, BATTERY_CHARGING); + BatteryState->Discharging = BooleanFlagOn(BatteryStatus.PowerState, BATTERY_DISCHARGING); + BatteryState->MaxCapacity = BatteryInfo.FullChargedCapacity; + BatteryState->RemainingCapacity = BatteryStatus.Capacity; + BatteryState->Rate = BatteryStatus.Rate; + if (BatteryState->Discharging && BatteryStatus.Rate < 0) + { + BatteryState->EstimatedTime = + 3600 * BatteryInfo.FullChargedCapacity / -BatteryStatus.Rate; + } + else + { + BatteryState->EstimatedTime = 0; + } + +Exit: + ZwClose(CompBattHandle); + return Status; +} + + /* * @unimplemented */ @@ -824,23 +933,34 @@ NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel, { case SystemBatteryState: { - PSYSTEM_BATTERY_STATE BatteryState = (PSYSTEM_BATTERY_STATE)OutputBuffer; + SYSTEM_BATTERY_STATE BatteryState; if (InputBuffer != NULL) return STATUS_INVALID_PARAMETER; if (OutputBufferLength < sizeof(SYSTEM_BATTERY_STATE)) return STATUS_BUFFER_TOO_SMALL; + if (PopCapabilities.SystemBatteriesPresent) + { + /* Open the battery driver and query the battery state */ + Status = PopQueryBatteryState(&BatteryState); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to query battery state: Status: 0x%08X\n", Status); + return Status; + } + } + else + { + /* No batteries present, set defaults */ + RtlZeroMemory(&BatteryState, sizeof(BatteryState)); + BatteryState.AcOnLine = TRUE; + } + _SEH2_TRY { - /* Just zero the struct */ - RtlZeroMemory(BatteryState, sizeof(*BatteryState)); - BatteryState->EstimatedTime = MAXULONG; - BatteryState->BatteryPresent = PopCapabilities.SystemBatteriesPresent; -// BatteryState->AcOnLine = TRUE; -// BatteryState->MaxCapacity = ; -// BatteryState->RemainingCapacity = ; - + /* Copy the battery state to the output buffer */ + RtlCopyMemory(OutputBuffer, &BatteryState, sizeof(BatteryState)); Status = STATUS_SUCCESS; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) diff --git a/sdk/include/ddk/ntpoapi.h b/sdk/include/ddk/ntpoapi.h index 7856fff043d..044829c22df 100644 --- a/sdk/include/ddk/ntpoapi.h +++ b/sdk/include/ddk/ntpoapi.h @@ -386,7 +386,8 @@ typedef struct { BOOLEAN BatteryPresent; BOOLEAN Charging; BOOLEAN Discharging; - BOOLEAN Spare1[4]; + BOOLEAN Spare1[3]; + UCHAR Tag; ULONG MaxCapacity; ULONG RemainingCapacity; ULONG Rate;