From: Alex Henrie Subject: [PATCH 4/6] ntdll: Implement NtPowerInformation(SystemBatteryState) on Linux Message-Id: <20190925064629.119467-4-alexhenrie24@gmail.com> Date: Wed, 25 Sep 2019 00:45:57 -0600 In-Reply-To: <20190925064629.119467-1-alexhenrie24@gmail.com> References: <20190925064629.119467-1-alexhenrie24@gmail.com> Signed-off-by: Alex Henrie --- MSDN says that "Charging" and "Discharging" are set based on all batteries in the computer, but then it makes it sound like the remaining fields are set based on a single battery. Since I do not have a computer with multiple batteries to test, I am proposing that we implement this function according to MSDN's description and change it later if that turns out to be incorrect. --- dlls/ntdll/nt.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index efc4c8adcf..fa2330ba66 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -3141,6 +3141,73 @@ static ULONG mhz_from_cpuinfo(void) } #endif +#ifdef linux + +static const char * get_sys_str(const char *path) +{ + static char s[16]; + FILE *f = fopen(path, "r"); + const char *ret = NULL; + if (f) + { + if (fgets(s, sizeof(s), f)) + ret = s; + fclose(f); + } + return ret; +} + +static int get_sys_int(const char *path, int def) +{ + const char *s = get_sys_str(path); + return s ? atoi(s) : def; +} + +static NTSTATUS fill_battery_state(SYSTEM_BATTERY_STATE *bs) +{ + char path[64]; + const char *s; + unsigned int i = 0; + LONG64 voltage; /* microvolts */ + + bs->AcOnLine = get_sys_int("/sys/class/power_supply/AC/online", 1); + + for (;;) + { + sprintf(path, "/sys/class/power_supply/BAT%u/status", i); + s = get_sys_str(path); + if (!s) break; + bs->Charging |= (strcmp(s, "Charging\n") == 0); + bs->Discharging |= (strcmp(s, "Discharging\n") == 0); + bs->BatteryPresent = TRUE; + i++; + } + + if (bs->BatteryPresent) + { + voltage = get_sys_int("/sys/class/power_supply/BAT0/voltage_now", 0); + bs->MaxCapacity = get_sys_int("/sys/class/power_supply/BAT0/charge_full", 0) * voltage / 1e9; + bs->RemainingCapacity = get_sys_int("/sys/class/power_supply/BAT0/charge_now", 0) * voltage / 1e9; + bs->Rate = -get_sys_int("/sys/class/power_supply/BAT0/current_now", 0) * voltage / 1e9; + if (!bs->Charging && (LONG)bs->Rate < 0) + bs->EstimatedTime = 3600 * bs->RemainingCapacity / -(LONG)bs->Rate; + else + bs->EstimatedTime = ~0u; + } + + return STATUS_SUCCESS; +} + +#else + +static NTSTATUS fill_battery_state(SYSTEM_BATTERY_STATE *) +{ + FIXME("SystemBatteryState not implemented on this platform\n"); + return STATUS_NOT_IMPLEMENTED; +} + +#endif + /****************************************************************************** * NtPowerInformation [NTDLL.@] * @@ -3194,6 +3261,12 @@ NTSTATUS WINAPI NtPowerInformation( PowerCaps->DefaultLowLatencyWake = PowerSystemUnspecified; return STATUS_SUCCESS; } + case SystemBatteryState: { + if (nOutputBufferSize < sizeof(SYSTEM_BATTERY_STATE)) + return STATUS_BUFFER_TOO_SMALL; + memset(lpOutputBuffer, 0, sizeof(SYSTEM_BATTERY_STATE)); + return fill_battery_state(lpOutputBuffer); + } case SystemExecutionState: { PULONG ExecutionState = lpOutputBuffer; WARN("semi-stub: SystemExecutionState\n"); /* Needed for .NET Framework, but using a FIXME is really noisy. */ -- 2.23.0