https://git.reactos.org/?p=reactos.git;a=commitdiff;h=c439c5849b60920d9aec198e4f5234339ef7c9a5

commit c439c5849b60920d9aec198e4f5234339ef7c9a5
Author:     George Bișoc <george.bi...@reactos.org>
AuthorDate: Sat Dec 14 22:52:58 2024 +0100
Commit:     George Bișoc <george.bi...@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

Reply via email to