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

commit a6d4998c6c89abfb51cb561ce19f1f5aabc1bbe3
Author:     George Bișoc <george.bi...@reactos.org>
AuthorDate: Sat Dec 14 22:48:52 2024 +0100
Commit:     George Bișoc <george.bi...@reactos.org>
CommitDate: Wed Jan 8 23:20:08 2025 +0100

    [COMPBATT] Implement CompBattQueryStatus and CompBattSetStatusNotify
---
 drivers/bus/acpi/compbatt/compbatt.c | 543 ++++++++++++++++++++++++++++++++++-
 1 file changed, 539 insertions(+), 4 deletions(-)

diff --git a/drivers/bus/acpi/compbatt/compbatt.c 
b/drivers/bus/acpi/compbatt/compbatt.c
index 9e0e5d3a782..7e55887be60 100644
--- a/drivers/bus/acpi/compbatt/compbatt.c
+++ b/drivers/bus/acpi/compbatt/compbatt.c
@@ -481,6 +481,115 @@ CompBattDisableStatusNotify(
     return STATUS_SUCCESS;
 }
 
+static
+BOOLEAN
+CompBattCalculateTotalRateAndLinkedBatteries(
+    _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,
+    _Out_ PULONG TotalRate,
+    _Out_ PULONG BatteriesCount)
+{
+    PCOMPBATT_BATTERY_DATA BatteryData;
+    PLIST_ENTRY ListHead, NextEntry;
+    BOOLEAN BadBattery = FALSE;
+    ULONG LinkedBatteries = 0;
+    ULONG BadBatteriesCount = 0;
+    ULONG ComputedRate = 0;
+
+    /* Loop over the linked batteries and sum up the total capacity rate */
+    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;
+
+        /*
+         * Ensure this battery has a valid tag and that its rate capacity
+         * is not unknown. Reject unknown rates when calculating the total 
rate.
+         */
+        if ((BatteryData->Tag != BATTERY_TAG_INVALID) &&
+            (BatteryData->BatteryStatus.Rate != BATTERY_UNKNOWN_RATE))
+        {
+            /*
+             * Now the ultimate judgement for this battery is to determine
+             * if the battery behaves optimally based on its current power
+             * state it is and the rate flow of the battery.
+             *
+             * If the rate flow is positive the battery is receiving power
+             * which increases the chemical potential energy as electrons
+             * move around, THIS MEANS the battery is CHARGING. If the rate
+             * flow is negative the battery cells are producing way less
+             * electrical energy, thus the battery is DISCHARGING.
+             *
+             * A consistent battery is a battery of which power state matches
+             * the rate flow. If that were the case, then we have found a bad
+             * battery. The worst case is that a battery is physically 
damanged.
+             */
+            if ((BatteryData->BatteryStatus.PowerState & BATTERY_DISCHARGING) 
&&
+                (BatteryData->BatteryStatus.Rate >= 0))
+            {
+                if (CompBattDebug & COMPBATT_DEBUG_WARN)
+                    DbgPrint("CompBatt: The battery is discharging but in 
reality it is charging... (Rate %d)\n",
+                             BatteryData->BatteryStatus.Rate);
+
+                BadBattery = TRUE;
+                BadBatteriesCount++;
+            }
+
+            if ((BatteryData->BatteryStatus.PowerState & BATTERY_CHARGING) &&
+                (BatteryData->BatteryStatus.Rate <= 0))
+            {
+                if (CompBattDebug & COMPBATT_DEBUG_WARN)
+                    DbgPrint("CompBatt: The battery is charging but in reality 
it is discharging... (Rate %d)\n",
+                             BatteryData->BatteryStatus.Rate);
+
+                BadBattery = TRUE;
+                BadBatteriesCount++;
+            }
+
+            if (((BatteryData->BatteryStatus.PowerState & (BATTERY_CHARGING | 
BATTERY_DISCHARGING)) == 0) &&
+                (BatteryData->BatteryStatus.Rate != 0))
+            {
+                if (CompBattDebug & COMPBATT_DEBUG_WARN)
+                    DbgPrint("CompBatt: The battery is neither charging or 
discharging but has a contradicting rate... (Rate %d)\n",
+                             BatteryData->BatteryStatus.Rate);
+
+                BadBattery = TRUE;
+                BadBatteriesCount++;
+            }
+
+            /*
+             * Sum up the rate of this battery to make up the total, even if 
that means
+             * the battery may have incosistent rate. This is because it is 
still a linked
+             * battery to the composite battery and it is used to power up the 
system nonetheless.
+             */
+            ComputedRate += BatteryData->BatteryStatus.Rate;
+        }
+
+        /* We are done with this individual battery */
+        LinkedBatteries++;
+        IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
+    }
+
+    /* Release the lock as we are no longer poking through the batteries list 
*/
+    ExReleaseFastMutex(&DeviceExtension->Lock);
+
+    /* Print out the total count of bad batteries we have found */
+    if (BadBatteriesCount > 0)
+    {
+        if (CompBattDebug & COMPBATT_DEBUG_WARN)
+            DbgPrint("CompBatt: %lu bad batteries have been found!\n", 
BadBatteriesCount);
+    }
+
+    *TotalRate = ComputedRate;
+    *BatteriesCount = LinkedBatteries;
+    return BadBattery;
+}
+
 NTSTATUS
 NTAPI
 CompBattSetStatusNotify(
@@ -488,8 +597,226 @@ CompBattSetStatusNotify(
     _In_ ULONG BatteryTag,
     _In_ PBATTERY_NOTIFY BatteryNotify)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    BOOLEAN BadBattery;
+    ULONG TotalRate;
+    ULONG BatteriesCount;
+    ULONG HighestCapacity;
+    ULONG LowCapDifference, HighCapDifference, LowDelta, HighDelta;
+    BATTERY_STATUS BatteryStatus;
+    PCOMPBATT_BATTERY_DATA BatteryData;
+    PLIST_ENTRY ListHead, NextEntry;
+
+    /*
+     * The caller wants to set new status notification settings but the 
composite
+     * battery does not have a valid tag assigned, or the tag does not 
actually match.
+     */
+    if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) ||
+        (DeviceExtension->Tag != BatteryTag))
+    {
+        if (CompBattDebug & COMPBATT_DEBUG_WARN)
+            DbgPrint("CompBatt: Composite battery tag not assigned or not 
matching (Tag -> %lu, Composite Tag -> %lu)\n",
+                     BatteryTag, DeviceExtension->Tag);
+
+        return STATUS_NO_SUCH_DEVICE;
+    }
+
+    /*
+     * Before we are setting up new status wait notification points we need to
+     * refresh the composite status so that we get to know what values should 
be
+     * set for the current notification wait status.
+     */
+    Status = CompBattQueryStatus(DeviceExtension,
+                                 BatteryTag,
+                                 &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 setting 
notify status)\n"
+                 "          PowerState -> 0x%lx\n"
+                 "          Capacity -> %u\n"
+                 "          Voltage -> %u\n"
+                 "          Rate -> %d\n",
+                 BatteryStatus.PowerState,
+                 BatteryStatus.Capacity,
+                 BatteryStatus.Voltage,
+                 BatteryStatus.Rate);
+
+    /* Calculate the high and low capacity differences based on the real 
summed capacity of the composite */
+    LowCapDifference = DeviceExtension->BatteryStatus.Capacity - 
BatteryNotify->LowCapacity;
+    HighCapDifference = BatteryNotify->HighCapacity - 
DeviceExtension->BatteryStatus.Capacity;
+
+    /* Cache the notification parameters provided for later usage when polling 
for battery status */
+    DeviceExtension->WaitNotifyStatus.PowerState = BatteryNotify->PowerState;
+    DeviceExtension->WaitNotifyStatus.LowCapacity = BatteryNotify->LowCapacity;
+    DeviceExtension->WaitNotifyStatus.HighCapacity = 
BatteryNotify->HighCapacity;
+
+    /* Toggle the valid notify flag as these are the newer notification 
settings */
+    DeviceExtension->Flags |= COMPBATT_STATUS_NOTIFY_SET;
+
+    /*
+     * Get the number of currently linked batteries to composite and total 
rate,
+     * we will use these counters later to determine the wait values for each
+     * individual battery.
+     */
+    BadBattery = CompBattCalculateTotalRateAndLinkedBatteries(DeviceExtension,
+                                                              &TotalRate,
+                                                              &BatteriesCount);
+
+    /*
+     * Of course we have to be sure that we have at least one battery linked
+     * with the composite battery at this time of getting invoked to set new
+     * notification wait settings.
+     */
+    ASSERT(BatteriesCount != 0);
+
+    /* Walk over the linked batteries list and set up new wait configuration 
settings */
+    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);
+
+        /* Make sure this battery has a tag before setting new wait values */
+        if (BatteryData->Tag != BATTERY_TAG_INVALID)
+        {
+            /*
+             * And also make sure this battery does not have an unknown
+             * capacity, we cannot set up new configuration wait settings
+             * based on that. Default the low and high wait capacities.
+             */
+            if (BatteryData->BatteryStatus.Capacity != 
BATTERY_UNKNOWN_CAPACITY)
+            {
+                /*
+                 * Calculate the low capacity wait setting. If at least one
+                 * bad battery was found while we computed the total composite
+                 * rate, then divide the difference between the total 
batteries.
+                 * Otherwise compute the battery deltas of the composite based
+                 * on total summed capacity rate. Otherwise if the total rate
+                 * is 0, then the real wait low and high capacities will be on
+                 * par with the real capacity.
+                 */
+                if (BadBattery)
+                {
+                    LowDelta = LowCapDifference / BatteriesCount;
+                    HighDelta = HighCapDifference / BatteriesCount;
+                }
+                else
+                {
+                    if (TotalRate)
+                    {
+                        LowDelta = COMPUTE_BATT_CAP_DELTA(LowCapDifference, 
BatteryData, TotalRate);
+                        HighDelta = COMPUTE_BATT_CAP_DELTA(HighCapDifference, 
BatteryData, TotalRate);
+                    }
+                    else
+                    {
+                        LowDelta = 0;
+                        HighDelta = 0;
+                    }
+                }
+
+                /*
+                 * Assign the wait low capacity setting ONLY if the battery 
delta
+                 * is not high. Otherwise it has overflowed and we cannot use 
that
+                 * for low capacity, of which we have to default it to 0.
+                 */
+                if (BatteryData->BatteryStatus.Capacity > LowDelta)
+                {
+                    BatteryData->WaitStatus.LowCapacity = 
BatteryData->BatteryStatus.Capacity - LowDelta;
+                }
+                else
+                {
+                    BatteryData->WaitStatus.LowCapacity = 
COMPBATT_WAIT_MIN_LOW_CAPACITY;
+                }
+
+                /*
+                 * Assign the wait high capacity setting ONLY if the real 
capacity
+                 * is not above the maximum highest capacity constant.
+                 */
+                HighestCapacity = COMPBATT_WAIT_MAX_HIGH_CAPACITY - HighDelta;
+                if (HighestCapacity < BatteryData->BatteryStatus.Capacity)
+                {
+                    BatteryData->WaitStatus.HighCapacity = HighestCapacity;
+                }
+                else
+                {
+                    BatteryData->WaitStatus.HighCapacity = 
BatteryData->BatteryStatus.Capacity + HighDelta;
+                }
+
+                /*
+                 * We have set up the wait values but they are in conflict 
with the
+                 * ones set up by the IRP complete worker. We have to cancel 
the IRP
+                 * so the worker will copy our wait configuration values.
+                 */
+                if ((BatteryData->Mode == COMPBATT_READ_STATUS) &&
+                    (BatteryData->WaitStatus.PowerState != 
BatteryData->WorkerBuffer.WorkerWaitStatus.PowerState ||
+                     BatteryData->WaitStatus.LowCapacity != 
BatteryData->WorkerBuffer.WorkerWaitStatus.LowCapacity ||
+                     BatteryData->WaitStatus.HighCapacity != 
BatteryData->WorkerBuffer.WorkerWaitStatus.HighCapacity))
+                {
+                    if (CompBattDebug & COMPBATT_DEBUG_INFO)
+                        DbgPrint("CompBatt: Configuration wait values are in 
conflict\n"
+                                 "          BatteryData->WaitStatus.PowerState 
-> 0x%lx\n"
+                                 "          
BatteryData->WorkerBuffer.WorkerWaitStatus.PowerState -> 0x%lx\n"
+                                 "          
BatteryData->WaitStatus.LowCapacity -> %u\n"
+                                 "          
BatteryData->WorkerBuffer.WorkerWaitStatus.LowCapacity -> %u\n"
+                                 "          
BatteryData->WaitStatus.HighCapacity -> %u\n"
+                                 "          
BatteryData->WorkerBuffer.WorkerWaitStatus.HighCapacity -> %u\n",
+                                 BatteryData->WaitStatus.PowerState,
+                                 
BatteryData->WorkerBuffer.WorkerWaitStatus.PowerState,
+                                 BatteryData->WaitStatus.LowCapacity,
+                                 
BatteryData->WorkerBuffer.WorkerWaitStatus.LowCapacity,
+                                 BatteryData->WaitStatus.HighCapacity,
+                                 
BatteryData->WorkerBuffer.WorkerWaitStatus.HighCapacity);
+
+                    IoCancelIrp(BatteryData->Irp);
+                }
+            }
+            else
+            {
+                BatteryData->WaitStatus.LowCapacity = BATTERY_UNKNOWN_CAPACITY;
+                BatteryData->WaitStatus.HighCapacity = 
BATTERY_UNKNOWN_CAPACITY;
+            }
+        }
+
+        /* 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);
+
+    /* Ensure the composite battery did not incur in drastic changes of tag */
+    if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) ||
+        (DeviceExtension->Tag != BatteryTag))
+    {
+        /*
+         * Either the last battery was removed (in this case the composite is 
no
+         * longer existing) or a battery was removed of which the whole battery
+         * information must be recomputed and such.
+         */
+        if (CompBattDebug & COMPBATT_DEBUG_WARN)
+            DbgPrint("CompBatt: Last battery or a battery was removed, the 
whole composite data must be recomputed\n");
+
+        return STATUS_NO_SUCH_DEVICE;
+    }
+
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
@@ -499,8 +826,216 @@ CompBattQueryStatus(
     _In_ ULONG Tag,
     _Out_ PBATTERY_STATUS BatteryStatus)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PCOMPBATT_BATTERY_DATA BatteryData;
+    BATTERY_WAIT_STATUS Wait;
+    PLIST_ENTRY ListHead, NextEntry;
+    ULONGLONG LastReadTime, CurrentReadTime;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /*
+     * The caller wants to update the composite battery status but the 
composite
+     * itself does not have a valid tag assigned, or the tag does not actually 
match.
+     */
+    if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) ||
+        (DeviceExtension->Tag != Tag))
+    {
+        if (CompBattDebug & COMPBATT_DEBUG_WARN)
+            DbgPrint("CompBatt: Composite battery tag not assigned or not 
matching (Tag -> %lu, Composite Tag -> %lu)\n",
+                     Tag, DeviceExtension->Tag);
+
+        return STATUS_NO_SUCH_DEVICE;
+    }
+
+    /* Initialize the status and wait fields with zeros */
+    RtlZeroMemory(BatteryStatus, sizeof(*BatteryStatus));
+    RtlZeroMemory(&Wait, sizeof(Wait));
+
+    /*
+     * The battery status was already updated when the caller queried for new
+     * status. We do not need to update the status again for no reason.
+     * Just give them the data outright.
+     */
+    CurrentReadTime = KeQueryInterruptTime();
+    LastReadTime = CurrentReadTime - DeviceExtension->InterruptTime;
+    if (LastReadTime < COMPBATT_FRESH_STATUS_TIME)
+    {
+        if (CompBattDebug & COMPBATT_DEBUG_WARN)
+            DbgPrint("CompBatt: Composite battery status data is fresh, no 
need to update it again\n");
+
+        RtlCopyMemory(BatteryStatus, &DeviceExtension->BatteryStatus, 
sizeof(BATTERY_STATUS));
+        return STATUS_SUCCESS;
+    }
+
+    /*
+     * Initialize the battery status context with unknown defaults, until we 
get
+     * to retrieve the real data from each battery and compute the exact 
status.
+     * Assume the system is powered by AC source for now until we find out it 
is
+     * not the case.
+     */
+    BatteryStatus->PowerState = BATTERY_POWER_ON_LINE;
+    BatteryStatus->Capacity = BATTERY_UNKNOWN_CAPACITY;
+    BatteryStatus->Voltage = BATTERY_UNKNOWN_VOLTAGE;
+    BatteryStatus->Rate = BATTERY_UNKNOWN_RATE;
+
+    /* Iterate over all the present linked batteries and retrieve their status 
*/
+    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);
+
+        /* Setup the battery tag for the status wait which is needed to send 
off the IOCTL */
+        Wait.BatteryTag = BatteryData->Tag;
+
+        /* Make sure this battery has a tag before we send off the IOCTL */
+        if (BatteryData->Tag != BATTERY_TAG_INVALID)
+        {
+            /* Only query new battery status data if it is no longer fresh */
+            LastReadTime = CurrentReadTime - BatteryData->InterruptTime;
+            if (LastReadTime > COMPBATT_FRESH_STATUS_TIME)
+            {
+                RtlZeroMemory(&BatteryData->BatteryStatus,
+                              sizeof(BatteryData->BatteryStatus));
+                Status = BatteryIoctl(IOCTL_BATTERY_QUERY_STATUS,
+                                      BatteryData->DeviceObject,
+                                      &Wait,
+                                      sizeof(Wait),
+                                      &BatteryData->BatteryStatus,
+                                      sizeof(BatteryData->BatteryStatus),
+                                      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);
+                    break;
+                }
+
+                /* Update the timestamp of the current read of battery status 
*/
+                BatteryData->InterruptTime = CurrentReadTime;
+            }
+
+            /*
+             * Now it is time to combine the data into the composite status.
+             * The battery is either charging or discharging. AC is present
+             * only if the charger supplies current to all batteries. And
+             * the composite is deemed as critical if at least one battery
+             * is discharging and it is in crtitical state.
+             */
+            BatteryStatus->PowerState |= 
(BatteryData->BatteryStatus.PowerState & (BATTERY_CHARGING | 
BATTERY_DISCHARGING));
+            BatteryStatus->PowerState &= 
(BatteryData->BatteryStatus.PowerState | ~BATTERY_POWER_ON_LINE);
+            if ((BatteryData->BatteryStatus.PowerState & BATTERY_CRITICAL) &&
+                (BatteryData->BatteryStatus.PowerState & BATTERY_DISCHARGING))
+            {
+                BatteryStatus->PowerState |= BATTERY_CRITICAL;
+            }
+
+            /* Add up the battery capacity if it is not unknown */
+            if (BatteryData->BatteryStatus.Capacity != 
BATTERY_UNKNOWN_CAPACITY)
+            {
+                if (BatteryStatus->Capacity != BATTERY_UNKNOWN_CAPACITY)
+                {
+                    BatteryStatus->Capacity += 
BatteryData->BatteryStatus.Capacity;
+                }
+                else
+                {
+                    BatteryStatus->Capacity = 
BatteryData->BatteryStatus.Capacity;
+                }
+            }
+
+            /* Always pick up the greatest voltage for the composite battery */
+            if (BatteryData->BatteryStatus.Voltage != BATTERY_UNKNOWN_VOLTAGE)
+            {
+                if (BatteryStatus->Voltage != BATTERY_UNKNOWN_VOLTAGE)
+                {
+                    BatteryStatus->Voltage = max(BatteryStatus->Voltage,
+                                                 
BatteryData->BatteryStatus.Voltage);
+                }
+                else
+                {
+                    BatteryStatus->Voltage = 
BatteryData->BatteryStatus.Voltage;
+                }
+            }
+
+            /* Add up the battery discharge rate if it is not unknown */
+            if (BatteryData->BatteryStatus.Rate != BATTERY_UNKNOWN_RATE)
+            {
+                if (BatteryStatus->Rate != BATTERY_UNKNOWN_RATE)
+                {
+                    BatteryStatus->Rate += BatteryData->BatteryStatus.Rate;
+                }
+                else
+                {
+                    BatteryStatus->Rate = BatteryData->BatteryStatus.Rate;
+                }
+            }
+        }
+
+        /* We are done combining data from 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);
+
+    /* Ensure the composite battery did not incur in drastic changes of tag */
+    if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) ||
+        (DeviceExtension->Tag != Tag))
+    {
+        /*
+         * Either the last battery was removed (in this case the composite is 
no
+         * longer existing) or a battery was removed of which the whole battery
+         * information must be recomputed and such.
+         */
+        if (CompBattDebug & COMPBATT_DEBUG_WARN)
+            DbgPrint("CompBatt: Last battery or a battery was removed, the 
whole composite data must be recomputed\n");
+
+        return STATUS_NO_SUCH_DEVICE;
+    }
+
+    /*
+     * If there is a battery that is charging while another one discharging,
+     * then tell the caller the composite battery is actually discharging.
+     * This is less likely to happen on a multi-battery system like laptops
+     * as the charger would provide electricity to all the batteries.
+     * Perhaps the most likely case scenario would be if the system were
+     * to be powered by a UPS.
+     */
+    if ((BatteryStatus->PowerState & BATTERY_CHARGING) &&
+        (BatteryStatus->PowerState & BATTERY_DISCHARGING))
+    {
+        BatteryStatus->PowerState &= ~BATTERY_CHARGING;
+    }
+
+    /* Copy the combined status information to the composite battery */
+    if (NT_SUCCESS(Status))
+    {
+        RtlCopyMemory(&DeviceExtension->BatteryStatus,
+                      BatteryStatus,
+                      sizeof(DeviceExtension->BatteryStatus));
+
+        /* Update the last read battery status timestamp as well */
+        DeviceExtension->InterruptTime = CurrentReadTime;
+    }
+
+    return Status;
 }
 
 NTSTATUS

Reply via email to