https://git.reactos.org/?p=reactos.git;a=commitdiff;h=c439c5849b60920d9aec1…
commit c439c5849b60920d9aec198e4f5234339ef7c9a5
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sat Dec 14 22:52:58 2024 +0100
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Wed Jan 8 23:20:08 2025 +0100
[COMPBATT] Implement CompBattGetEstimatedTime
---
drivers/bus/acpi/compbatt/compbatt.c | 219 ++++++++++++++++++++++++++++++++++-
1 file changed, 217 insertions(+), 2 deletions(-)
diff --git a/drivers/bus/acpi/compbatt/compbatt.c b/drivers/bus/acpi/compbatt/compbatt.c
index 7e55887be60..740f1f18cb4 100644
--- a/drivers/bus/acpi/compbatt/compbatt.c
+++ b/drivers/bus/acpi/compbatt/compbatt.c
@@ -1282,14 +1282,229 @@ CompBattGetBatteryGranularity(
return STATUS_SUCCESS;
}
+static
+LONG
+CompBattCalculateAtRateTime(
+ _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
+{
+ NTSTATUS Status;
+ PCOMPBATT_BATTERY_DATA BatteryData;
+ BATTERY_QUERY_INFORMATION QueryInformation;
+ PLIST_ENTRY ListHead, NextEntry;
+ ULONG Time;
+ LONG ComputedAtRate = 0;
+
+ /* Walk over the linked batteries list to poll for "At Rate" value of each
battery */
+ ExAcquireFastMutex(&DeviceExtension->Lock);
+ ListHead = &DeviceExtension->BatteryList;
+ for (NextEntry = ListHead->Flink;
+ NextEntry != ListHead;
+ NextEntry = NextEntry->Flink)
+ {
+ /* Acquire the remove lock so this battery does not disappear under us */
+ BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
+ if (!NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock,
BatteryData->Irp)))
+ continue;
+
+ /* Now release the device lock since the battery can't go away */
+ ExReleaseFastMutex(&DeviceExtension->Lock);
+
+ /* Build the necessary information in order to query the battery estimated time
*/
+ QueryInformation.BatteryTag = BatteryData->Tag;
+ QueryInformation.InformationLevel = BatteryEstimatedTime;
+ QueryInformation.AtRate = 0;
+
+ /* Make sure this battery has a valid tag before issuing the IOCTL */
+ if (BatteryData->Tag != BATTERY_TAG_INVALID)
+ {
+ /*
+ * Now it is time to issue the IOCTL to the battery device.
+ * We are calculating the "At Rate" counter based on each linked
+ * battery that is discharging, one at a time. This ensures
+ * that when we will actually retrieve the estimation time of each
+ * individual battery and sum it all up as one time for the composite
+ * battery, that the estimated time is accurate enough.
+ */
+ Status = BatteryIoctl(IOCTL_BATTERY_QUERY_INFORMATION,
+ BatteryData->DeviceObject,
+ &QueryInformation,
+ sizeof(QueryInformation),
+ &Time,
+ sizeof(Time),
+ FALSE);
+ if (NT_SUCCESS(Status))
+ {
+ if ((Time != 0) && (Time != BATTERY_UNKNOWN_TIME))
+ {
+ ComputedAtRate -= COMPUTE_ATRATE_DRAIN(BatteryData, Time);
+ }
+ }
+ }
+
+ /* We are done with this battery */
+ ExAcquireFastMutex(&DeviceExtension->Lock);
+ IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
+ }
+
+ /* Release the lock as we are no longer poking through the batteries list */
+ ExReleaseFastMutex(&DeviceExtension->Lock);
+ return ComputedAtRate;
+}
+
NTSTATUS
NTAPI
CompBattGetEstimatedTime(
_Out_ PULONG Time,
_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ NTSTATUS Status;
+ PCOMPBATT_BATTERY_DATA BatteryData;
+ BATTERY_STATUS BatteryStatus;
+ BATTERY_QUERY_INFORMATION QueryInformation;
+ PLIST_ENTRY ListHead, NextEntry;
+ ULONG ReturnedTime;
+ LONG ComputedAtRate;
+
+ /* Assume the battery time is not estimated yet */
+ *Time = BATTERY_UNKNOWN_TIME;
+
+ /*
+ * Before we are querying the composite estimated battery time we must
+ * refresh the battery status cache if we have not done it so.
+ */
+ Status = CompBattQueryStatus(DeviceExtension,
+ DeviceExtension->Tag,
+ &BatteryStatus);
+ if (!NT_SUCCESS(Status))
+ {
+ if (CompBattDebug & COMPBATT_DEBUG_ERR)
+ DbgPrint("CompBatt: Failed to refresh composite battery's status
(Status 0x%08lx)\n", Status);
+
+ return Status;
+ }
+
+ /* Print out battery status data that has been polled */
+ if (CompBattDebug & COMPBATT_DEBUG_INFO)
+ DbgPrint("CompBatt: Latest composite battery status (when querying for
estimated time)\n"
+ " PowerState -> 0x%lx\n"
+ " Capacity -> %u\n"
+ " Voltage -> %u\n"
+ " Rate -> %d\n",
+ BatteryStatus.PowerState,
+ BatteryStatus.Capacity,
+ BatteryStatus.Voltage,
+ BatteryStatus.Rate);
+
+ /*
+ * If the batteries are not being discharged and the system is directly
+ * being powered by external AC source then it makes no sense to
+ * compute the battery estimated time because that construct is for
+ * WHEN the system is powered directly from batteries and it drains power.
+ */
+ if (DeviceExtension->BatteryStatus.PowerState & BATTERY_POWER_ON_LINE)
+ {
+ if (CompBattDebug & COMPBATT_DEBUG_WARN)
+ {
+ DbgPrint("CompBatt: The system is powered by AC source, estimated time
is not available\n");
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ /* Determine the draining "At Rate" counter for all batteries */
+ ComputedAtRate = CompBattCalculateAtRateTime(DeviceExtension);
+
+ /*
+ * A rate of 0 indicates none of the batteries that are linked with
+ * the composite are being drained therefore we cannot estimate the
+ * run time of the composite as it is not discharging.
+ */
+ if (ComputedAtRate == 0)
+ {
+ if (CompBattDebug & COMPBATT_DEBUG_WARN)
+ DbgPrint("CompBatt: No battery is discharging and no power is being
drained, cannot estimate the run time\n");
+
+ return STATUS_SUCCESS;
+ }
+
+ /* Walk over the linked batteries list and determine the exact estimated time */
+ ExAcquireFastMutex(&DeviceExtension->Lock);
+ ListHead = &DeviceExtension->BatteryList;
+ for (NextEntry = ListHead->Flink;
+ NextEntry != ListHead;
+ NextEntry = NextEntry->Flink)
+ {
+ /* Acquire the remove lock so this battery does not disappear under us */
+ BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
+ if (!NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock,
BatteryData->Irp)))
+ continue;
+
+ /* Now release the device lock since the battery can't go away */
+ ExReleaseFastMutex(&DeviceExtension->Lock);
+
+ /* Build the necessary information in order to query the battery estimated time
*/
+ QueryInformation.BatteryTag = BatteryData->Tag;
+ QueryInformation.InformationLevel = BatteryEstimatedTime;
+ QueryInformation.AtRate = ComputedAtRate;
+
+ /* Make sure this battery has a valid tag before issuing the IOCTL */
+ if (BatteryData->Tag != BATTERY_TAG_INVALID)
+ {
+ Status = BatteryIoctl(IOCTL_BATTERY_QUERY_INFORMATION,
+ BatteryData->DeviceObject,
+ &QueryInformation,
+ sizeof(QueryInformation),
+ &ReturnedTime,
+ sizeof(ReturnedTime),
+ FALSE);
+ if (!NT_SUCCESS(Status))
+ {
+ /*
+ * If the device is being suddenly removed then we must invalidate
+ * both this battery and composite tags.
+ */
+ if (Status == STATUS_DEVICE_REMOVED)
+ {
+ Status = STATUS_NO_SUCH_DEVICE;
+ }
+
+ ExAcquireFastMutex(&DeviceExtension->Lock);
+ IoReleaseRemoveLock(&BatteryData->RemoveLock,
BatteryData->Irp);
+
+ /*
+ * In other places we are ceasing the execution of the loop but
+ * here we want to continue looking for other linked batteries.
+ * This is because we are querying for the estimated battery time
+ * at the time the last battery status was valid. Also bear in
+ * mind IOCTL_BATTERY_QUERY_INFORMATION with InformationLevel as
+ * BatteryEstimatedTime might not be a valid request supported
+ * by this battery.
+ */
+ continue;
+ }
+
+ /* Now sum up the estimated battery time */
+ if (ReturnedTime != BATTERY_UNKNOWN_TIME)
+ {
+ if (*Time != BATTERY_UNKNOWN_TIME)
+ {
+ *Time += ReturnedTime;
+ }
+ else
+ {
+ *Time = ReturnedTime;
+ }
+ }
+ }
+
+ /* We are done with this battery */
+ ExAcquireFastMutex(&DeviceExtension->Lock);
+ IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
+ }
+
+ /* Release the lock as we are no longer poking through the batteries list */
+ ExReleaseFastMutex(&DeviceExtension->Lock);
+ return Status;
}
NTSTATUS