https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ee9914c71211175b1df887baf5eaf0bba14351d1
commit ee9914c71211175b1df887baf5eaf0bba14351d1 Author: George Bișoc <george.bi...@reactos.org> AuthorDate: Sat Dec 14 22:40:36 2024 +0100 Commit: George Bișoc <george.bi...@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