https://git.reactos.org/?p=reactos.git;a=commitdiff;h=a6d4998c6c89abfb51cb5…
commit a6d4998c6c89abfb51cb561ce19f1f5aabc1bbe3
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sat Dec 14 22:48:52 2024 +0100
Commit: George Bișoc <george.bisoc(a)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
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ee9914c71211175b1df88…
commit ee9914c71211175b1df887baf5eaf0bba14351d1
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sat Dec 14 22:40:36 2024 +0100
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Wed Jan 8 23:20:08 2025 +0100
[COMPBATT] Implement the battery monitor IRP worker
---
drivers/bus/acpi/compbatt/compbatt.c | 275 ++++++++++++++++++++++++++++++++++-
1 file changed, 271 insertions(+), 4 deletions(-)
diff --git a/drivers/bus/acpi/compbatt/compbatt.c b/drivers/bus/acpi/compbatt/compbatt.c
index ea1e287ece0..9e0e5d3a782 100644
--- a/drivers/bus/acpi/compbatt/compbatt.c
+++ b/drivers/bus/acpi/compbatt/compbatt.c
@@ -74,8 +74,19 @@ CompBattMonitorIrpComplete(
_In_ PIRP Irp,
_In_ PVOID Context)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ PIO_STACK_LOCATION IoStackLocation;
+ PCOMPBATT_BATTERY_DATA BatteryData;
+
+ /* We do not care about the device object */
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ /* Grab the composite battery data from the I/O stack packet */
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+ BatteryData = IoStackLocation->Parameters.Others.Argument2;
+
+ /* Request the IRP complete worker to do the deed */
+ ExQueueWorkItem(&BatteryData->WorkItem, DelayedWorkQueue);
+ return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
@@ -83,8 +94,264 @@ NTAPI
CompBattMonitorIrpCompleteWorker(
_In_ PCOMPBATT_BATTERY_DATA BatteryData)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ NTSTATUS Status;
+ PIRP Irp;
+ UCHAR Mode;
+ ULONG PrevPowerState;
+ PDEVICE_OBJECT DeviceObject;
+ BATTERY_STATUS BatteryStatus;
+ PIO_STACK_LOCATION IoStackLocation;
+ PCOMPBATT_DEVICE_EXTENSION DeviceExtension;
+
+ /* Cache the necessary battery data */
+ Irp = BatteryData->Irp;
+ DeviceObject = BatteryData->DeviceObject;
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+ DeviceExtension = IoStackLocation->Parameters.Others.Argument1;
+
+ /* Switch to the next stack as we have to setup the control function data there */
+ IoStackLocation = IoGetNextIrpStackLocation(Irp);
+
+ /* Has the I/O composite battery request succeeded? */
+ Status = Irp->IoStatus.Status;
+ if (!NT_SUCCESS(Status) && Status != STATUS_CANCELLED)
+ {
+ /*
+ * This battery is being removed from the composite, perform
+ * cleanups and do not inquire I/O requests again on this battery.
+ */
+ if (Status == STATUS_DEVICE_REMOVED)
+ {
+ if (CompBattDebug & COMPBATT_DEBUG_WARN)
+ DbgPrint("CompBatt: Battery (0x%p) is being removed from composite battery\n", BatteryData);
+
+ IoFreeIrp(Irp);
+ CompBattRemoveBattery(&BatteryData->BatteryName, DeviceExtension);
+ return;
+ }
+
+ /*
+ * This is the first time a battery is being added into the composite
+ * (we understand that if Status was STATUS_DEVICE_NOT_CONNECTED).
+ * We must invalidate the composite tag and request a recalculation
+ * of the battery tag.
+ */
+ if (CompBattDebug & COMPBATT_DEBUG_WARN)
+ DbgPrint("CompBatt: Battery arrived for first time or disappeared (Status 0x%08lx)\n", Status);
+
+ BatteryData->Tag = BATTERY_TAG_INVALID;
+
+ /*
+ * Invalidate the last read status interrupt time as well since the last
+ * battery status data no longer applies. Same for the composite battery
+ * as well.
+ */
+ BatteryData->InterruptTime = 0;
+ DeviceExtension->InterruptTime = 0;
+
+ /* Notify Battery Class the battery status incurs in a change */
+ BatteryClassStatusNotify(DeviceExtension->ClassData);
+
+ /* Setup the necessary I/O data to query the battery tag */
+ IoStackLocation->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_TAG;
+ IoStackLocation->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
+ IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ULONG);
+ BatteryData->Mode = COMPBATT_QUERY_TAG;
+ BatteryData->WorkerBuffer.WorkerTag = 0xFFFFFFFF;
+
+ /* Dispatch our request now to the battery's driver */
+ goto DispatchRequest;
+ }
+
+ /* Our I/O request has been completed successfully, check what did we get */
+ Mode = BatteryData->Mode;
+ switch (Mode)
+ {
+ case COMPBATT_QUERY_TAG:
+ {
+ /*
+ * This battery has just gotten a tag, acknowledge the composite battery
+ * about that so it can recalculate its own composite tag.
+ */
+ if (CompBattDebug & COMPBATT_DEBUG_WARN)
+ DbgPrint("CompBatt: Battery (Device 0x%p) has a tag of %lu\n", DeviceObject, BatteryData->WorkerBuffer.WorkerTag);
+
+ /* Ensure the battery tag is not bogus, getting a tag of 0 is illegal */
+ ASSERT(BatteryData->WorkerBuffer.WorkerTag != 0);
+
+ /* Assign the battery tag */
+ BatteryData->Tag = BatteryData->WorkerBuffer.WorkerTag;
+ BatteryData->Flags |= COMPBATT_TAG_ASSIGNED;
+
+ /* Punt the composite battery flags, as the previous cached data no longer applies */
+ DeviceExtension->Flags = 0;
+
+ /* Notify the Battery Class driver this battery has got a tag */
+ BatteryClassStatusNotify(DeviceExtension->ClassData);
+ break;
+ }
+
+ case COMPBATT_READ_STATUS:
+ {
+ /*
+ * Read the battery status only if the IRP has not been cancelled,
+ * otherwise the request must be re-issued again. This typically
+ * happens if the wait values are in conflict which it might
+ * end up in inconsistent battery status results.
+ */
+ if (Status != STATUS_CANCELLED && !Irp->Cancel)
+ {
+ /*
+ * If we reach here then the battery has entered into a change of
+ * power state or its charge capacity has changed.
+ */
+ if (CompBattDebug & COMPBATT_DEBUG_WARN)
+ DbgPrint("CompBatt: Battery state (Device 0x%p) has changed\n", DeviceObject);
+
+ /* Copy the battery status of this battery */
+ RtlCopyMemory(&BatteryData->BatteryStatus,
+ &BatteryData->WorkerBuffer.WorkerStatus,
+ sizeof(BatteryData->BatteryStatus));
+
+ /* Update the interrupt time as this is the most recent read of the battery status */
+ BatteryData->InterruptTime = KeQueryInterruptTime();
+
+ /*
+ * Ensure we have not gotten unknown capacities while we waited for new
+ * battery status. The battery might have malfunctioned or something.
+ */
+ if (BatteryData->WorkerBuffer.WorkerStatus.Capacity == BATTERY_UNKNOWN_CAPACITY)
+ {
+ /* We do not know the capacity of this battery, default the low and high capacities */
+ BatteryData->WaitStatus.LowCapacity = BATTERY_UNKNOWN_CAPACITY;
+ BatteryData->WaitStatus.HighCapacity = BATTERY_UNKNOWN_CAPACITY;
+ }
+ else
+ {
+ /* We know the capacity, adjust the low and high capacities accordingly */
+ if (BatteryData->WaitStatus.LowCapacity >
+ BatteryData->WorkerBuffer.WorkerStatus.Capacity)
+ {
+ BatteryData->WaitStatus.LowCapacity = BatteryData->WorkerBuffer.WorkerStatus.Capacity;
+ }
+
+ if (BatteryData->WaitStatus.HighCapacity <
+ BatteryData->WorkerBuffer.WorkerStatus.Capacity)
+ {
+ BatteryData->WaitStatus.HighCapacity = BatteryData->WorkerBuffer.WorkerStatus.Capacity;
+ }
+ }
+
+ /* Copy the current last read power state for the next wait */
+ BatteryData->WaitStatus.PowerState = BatteryData->WorkerBuffer.WorkerStatus.PowerState;
+
+ /*
+ * Cache the previous power state of the composite battery and invalidate
+ * the last computed battery status interrupt time. This is because,
+ * logically, this specific battery incurred in a state change therefore
+ * the previous composite status is no longer consistent.
+ */
+ PrevPowerState = DeviceExtension->BatteryStatus.PowerState;
+ DeviceExtension->InterruptTime = 0;
+
+ /* Compute a new battery status for the composite battery */
+ Status = CompBattQueryStatus(DeviceExtension,
+ DeviceExtension->Tag,
+ &BatteryStatus);
+
+ /* Print out the current battery status of the composite to the debugger */
+ if ((CompBattDebug & COMPBATT_DEBUG_INFO) && NT_SUCCESS(Status))
+ DbgPrint("CompBatt: Latest composite battery status\n"
+ " PowerState -> 0x%lx\n"
+ " Capacity -> %u\n"
+ " Voltage -> %u\n"
+ " Rate -> %d\n",
+ BatteryStatus.PowerState,
+ BatteryStatus.Capacity,
+ BatteryStatus.Voltage,
+ BatteryStatus.Rate);
+
+ /*
+ * Now determine whether should we notify the Battery Class driver due to
+ * changes in power state settings in the composite battery. This could
+ * happen in two following conditions:
+ *
+ * 1. The status notify flag was set for the respective power notification
+ * settings, and the composite battery incurred in a change of such
+ * settings. In this case we have to probe the current settings that
+ * they have changed.
+ *
+ * 2. The status notify flag was not set, therefore we do not know the
+ * exact configuration of the notification settings. We only care that
+ * the power state has changed at this point.
+ *
+ * Why do we have to do this is because we have to warn the Battery Class
+ * about the data that has changed.
+ */
+ if (!(DeviceExtension->Flags & COMPBATT_STATUS_NOTIFY_SET))
+ {
+ if (PrevPowerState != DeviceExtension->BatteryStatus.PowerState)
+ {
+ /* The previous power state is no longer valid, notify Battery Class */
+ BatteryClassStatusNotify(DeviceExtension->ClassData);
+ }
+ }
+ else
+ {
+ /*
+ * Unlike the condition above, we check for power state change against
+ * the current notify wait set since the notify set flag bit is assigned.
+ */
+ if (DeviceExtension->WaitNotifyStatus.PowerState != DeviceExtension->BatteryStatus.PowerState ||
+ DeviceExtension->WaitNotifyStatus.LowCapacity > DeviceExtension->BatteryStatus.Capacity ||
+ DeviceExtension->WaitNotifyStatus.HighCapacity < DeviceExtension->BatteryStatus.Capacity)
+ {
+ /* The following configuration settings have changed, notify Battery Class */
+ BatteryClassStatusNotify(DeviceExtension->ClassData);
+ }
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ ASSERTMSG("CompBatt: BAD!!! WE SHOULD NOT BE HERE!\n", FALSE);
+ UNREACHABLE;
+ }
+ }
+
+ /* Setup the necessary data to read battery status */
+ BatteryData->WaitStatus.BatteryTag = BatteryData->Tag;
+ BatteryData->WaitStatus.Timeout = 3000; // FIXME: Hardcoded (wait for 3 seconds) because we do not have ACPI notifications implemented yet...
+
+ RtlCopyMemory(&BatteryData->WorkerBuffer.WorkerWaitStatus,
+ &BatteryData->WaitStatus,
+ sizeof(BatteryData->WaitStatus));
+
+ IoStackLocation->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_STATUS;
+ IoStackLocation->Parameters.DeviceIoControl.InputBufferLength = sizeof(BatteryData->WorkerBuffer.WorkerWaitStatus);
+ IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength = sizeof(BatteryData->WorkerBuffer.WorkerStatus);
+ BatteryData->Mode = COMPBATT_READ_STATUS;
+
+DispatchRequest:
+ /* Setup the system buffer to that of the battery data which it will hold the returned data */
+ IoStackLocation->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+ Irp->AssociatedIrp.SystemBuffer = &BatteryData->WorkerBuffer;
+ Irp->Cancel = FALSE;
+ Irp->PendingReturned = FALSE;
+
+ /* Setup the worker completion routine which it will invoke the worker later on */
+ IoSetCompletionRoutine(Irp,
+ CompBattMonitorIrpComplete,
+ NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ /* Dispatch the I/O request now */
+ IoCallDriver(DeviceObject, Irp);
}
VOID
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=6a06db09cac016fcc90dd…
commit 6a06db09cac016fcc90dd654570add0c6b30f288
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sat Dec 14 22:34:22 2024 +0100
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Wed Jan 8 23:20:08 2025 +0100
[COMPBATT] Use FALSE for BOOLEANs on BatteryIoctl, not integers
---
drivers/bus/acpi/compbatt/compbatt.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/bus/acpi/compbatt/compbatt.c b/drivers/bus/acpi/compbatt/compbatt.c
index 76e339e1ee6..ea1e287ece0 100644
--- a/drivers/bus/acpi/compbatt/compbatt.c
+++ b/drivers/bus/acpi/compbatt/compbatt.c
@@ -286,7 +286,7 @@ CompBattGetBatteryInformation(
sizeof(InputBuffer),
&BatteryData->BatteryInformation,
sizeof(BatteryData->BatteryInformation),
- 0);
+ FALSE);
if (!NT_SUCCESS(Status))
{
/* Fail if the query had a problem */
@@ -442,7 +442,7 @@ CompBattGetBatteryGranularity(
sizeof(InputBuffer),
&BatteryScale,
sizeof(BatteryScale),
- 0);
+ FALSE);
if (!NT_SUCCESS(Status))
{
/* Fail if the query had a problem */
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=28bb8c6ff7b82b8216dfa…
commit 28bb8c6ff7b82b8216dfa6bfb052f1d169959062
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sat Dec 14 21:52:24 2024 +0100
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Wed Jan 8 23:20:07 2025 +0100
[COMPBATT] Assign a memory allocation tag for the I/O remove lock
IoInitializeRemoveLock expects an allocation tag to be provided when it allocates debug data in the kernel.
Passing 0 leads to a bunch of ASSERTs in the kernel as such data is allocated by ExAllocatePoolWithTag of which a tag has to be supplied, it's not optional.
---
drivers/bus/acpi/compbatt/comppnp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/bus/acpi/compbatt/comppnp.c b/drivers/bus/acpi/compbatt/comppnp.c
index 7e8523537ae..31b05030b4f 100644
--- a/drivers/bus/acpi/compbatt/comppnp.c
+++ b/drivers/bus/acpi/compbatt/comppnp.c
@@ -177,7 +177,7 @@ CompBattAddNewBattery(
ExReleaseFastMutex(&DeviceExtension->Lock);
/* Initialize the work item and delete lock */
- IoInitializeRemoveLock(&BatteryData->RemoveLock, 0, 0, 0);
+ IoInitializeRemoveLock(&BatteryData->RemoveLock, COMPBATT_TAG, 0, 0);
ExInitializeWorkItem(&BatteryData->WorkItem,
(PVOID)CompBattMonitorIrpCompleteWorker,
BatteryData);
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f9ea9220d8612368be66d…
commit f9ea9220d8612368be66db2182ebb6837ac68306
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sat Dec 14 23:07:18 2024 +0100
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Wed Jan 8 23:20:06 2025 +0100
[COMPBATT] Remove the useless WaitFlag field in the code which is never used
---
drivers/bus/acpi/compbatt/comppnp.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/bus/acpi/compbatt/comppnp.c b/drivers/bus/acpi/compbatt/comppnp.c
index 141ec2ebbe4..f16bfd2ed3d 100644
--- a/drivers/bus/acpi/compbatt/comppnp.c
+++ b/drivers/bus/acpi/compbatt/comppnp.c
@@ -165,7 +165,6 @@ CompBattAddNewBattery(IN PUNICODE_STRING BatteryName,
/* Set IRP data */
IoSetNextIrpStackLocation(Irp);
Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
- BatteryData->WaitFlag = 0;
/* Insert this battery in the list */
ExAcquireFastMutex(&DeviceExtension->Lock);