1*a8da29e8SJustin Miller /*
2*a8da29e8SJustin Miller * PROJECT: ReactOS Composite Battery Driver
3*a8da29e8SJustin Miller * LICENSE: MIT (https://spdx.org/licenses/MIT)
4*a8da29e8SJustin Miller * PURPOSE: Main Initialization Code and IRP Handling
5*a8da29e8SJustin Miller * COPYRIGHT: Copyright 2010 ReactOS Portable Systems Group <ros.arm@reactos.org>
6*a8da29e8SJustin Miller * Copyright 2024 George Bișoc <george.bisoc@reactos.org>
7*a8da29e8SJustin Miller */
8*a8da29e8SJustin Miller
9*a8da29e8SJustin Miller /* INCLUDES *******************************************************************/
10*a8da29e8SJustin Miller
11*a8da29e8SJustin Miller #include "compbatt.h"
12*a8da29e8SJustin Miller
13*a8da29e8SJustin Miller #include <debug.h>
14*a8da29e8SJustin Miller
15*a8da29e8SJustin Miller /* GLOBALS ********************************************************************/
16*a8da29e8SJustin Miller
17*a8da29e8SJustin Miller ULONG CompBattDebug;
18*a8da29e8SJustin Miller
19*a8da29e8SJustin Miller /* FUNCTIONS ******************************************************************/
20*a8da29e8SJustin Miller
21*a8da29e8SJustin Miller NTSTATUS
22*a8da29e8SJustin Miller NTAPI
CompBattOpenClose(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)23*a8da29e8SJustin Miller CompBattOpenClose(
24*a8da29e8SJustin Miller _In_ PDEVICE_OBJECT DeviceObject,
25*a8da29e8SJustin Miller _In_ PIRP Irp)
26*a8da29e8SJustin Miller {
27*a8da29e8SJustin Miller PAGED_CODE();
28*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING OpenClose\n");
29*a8da29e8SJustin Miller
30*a8da29e8SJustin Miller /* Complete the IRP with success */
31*a8da29e8SJustin Miller Irp->IoStatus.Status = STATUS_SUCCESS;
32*a8da29e8SJustin Miller Irp->IoStatus.Information = 0;
33*a8da29e8SJustin Miller IoCompleteRequest(Irp, IO_NO_INCREMENT);
34*a8da29e8SJustin Miller
35*a8da29e8SJustin Miller /* Return success */
36*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: Exiting OpenClose\n");
37*a8da29e8SJustin Miller return STATUS_SUCCESS;
38*a8da29e8SJustin Miller }
39*a8da29e8SJustin Miller
40*a8da29e8SJustin Miller NTSTATUS
41*a8da29e8SJustin Miller NTAPI
CompBattSystemControl(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)42*a8da29e8SJustin Miller CompBattSystemControl(
43*a8da29e8SJustin Miller _In_ PDEVICE_OBJECT DeviceObject,
44*a8da29e8SJustin Miller _In_ PIRP Irp)
45*a8da29e8SJustin Miller {
46*a8da29e8SJustin Miller PCOMPBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
47*a8da29e8SJustin Miller NTSTATUS Status;
48*a8da29e8SJustin Miller PAGED_CODE();
49*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING System Control\n");
50*a8da29e8SJustin Miller
51*a8da29e8SJustin Miller /* Are we attached yet? */
52*a8da29e8SJustin Miller if (DeviceExtension->AttachedDevice)
53*a8da29e8SJustin Miller {
54*a8da29e8SJustin Miller /* Send it up the stack */
55*a8da29e8SJustin Miller IoSkipCurrentIrpStackLocation(Irp);
56*a8da29e8SJustin Miller Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
57*a8da29e8SJustin Miller }
58*a8da29e8SJustin Miller else
59*a8da29e8SJustin Miller {
60*a8da29e8SJustin Miller /* We don't support WMI */
61*a8da29e8SJustin Miller Status = STATUS_NOT_SUPPORTED;
62*a8da29e8SJustin Miller Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
63*a8da29e8SJustin Miller IoCompleteRequest(Irp, IO_NO_INCREMENT);
64*a8da29e8SJustin Miller }
65*a8da29e8SJustin Miller
66*a8da29e8SJustin Miller /* Return status */
67*a8da29e8SJustin Miller return Status;
68*a8da29e8SJustin Miller }
69*a8da29e8SJustin Miller
70*a8da29e8SJustin Miller /**
71*a8da29e8SJustin Miller * @brief
72*a8da29e8SJustin Miller * Queues a work item thread worker which is bound to the individual
73*a8da29e8SJustin Miller * CM (Control Method) ACPI battery to handle the IRP.
74*a8da29e8SJustin Miller *
75*a8da29e8SJustin Miller * @param[in] DeviceObject
76*a8da29e8SJustin Miller * A pointer to a device object, this parameter is unused.
77*a8da29e8SJustin Miller *
78*a8da29e8SJustin Miller * @param[in] Irp
79*a8da29e8SJustin Miller * A pointer to an I/O request packet. It is used to gather the I/O stack
80*a8da29e8SJustin Miller * location which contains the data of the individual battery.
81*a8da29e8SJustin Miller *
82*a8da29e8SJustin Miller * @param[in] Context
83*a8da29e8SJustin Miller * An aribtrary pointer that points to context data, this paramater
84*a8da29e8SJustin Miller * is unused.
85*a8da29e8SJustin Miller *
86*a8da29e8SJustin Miller * @return
87*a8da29e8SJustin Miller * Returns STATUS_MORE_PROCESSING_REQUIRED to indicate the I/O request
88*a8da29e8SJustin Miller * is still in action, therefore the IRP is not freed.
89*a8da29e8SJustin Miller */
90*a8da29e8SJustin Miller NTSTATUS
91*a8da29e8SJustin Miller NTAPI
CompBattMonitorIrpComplete(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_In_ PVOID Context)92*a8da29e8SJustin Miller CompBattMonitorIrpComplete(
93*a8da29e8SJustin Miller _In_ PDEVICE_OBJECT DeviceObject,
94*a8da29e8SJustin Miller _In_ PIRP Irp,
95*a8da29e8SJustin Miller _In_ PVOID Context)
96*a8da29e8SJustin Miller {
97*a8da29e8SJustin Miller PIO_STACK_LOCATION IoStackLocation;
98*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
99*a8da29e8SJustin Miller
100*a8da29e8SJustin Miller /* We do not care about the device object */
101*a8da29e8SJustin Miller UNREFERENCED_PARAMETER(DeviceObject);
102*a8da29e8SJustin Miller
103*a8da29e8SJustin Miller /* Grab the composite battery data from the I/O stack packet */
104*a8da29e8SJustin Miller IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
105*a8da29e8SJustin Miller BatteryData = IoStackLocation->Parameters.Others.Argument2;
106*a8da29e8SJustin Miller
107*a8da29e8SJustin Miller /* Request the IRP complete worker to do the deed */
108*a8da29e8SJustin Miller ExQueueWorkItem(&BatteryData->WorkItem, DelayedWorkQueue);
109*a8da29e8SJustin Miller return STATUS_MORE_PROCESSING_REQUIRED;
110*a8da29e8SJustin Miller }
111*a8da29e8SJustin Miller
112*a8da29e8SJustin Miller /**
113*a8da29e8SJustin Miller * @brief
114*a8da29e8SJustin Miller * The brains of the battery IRP worker. It monitors the state of the
115*a8da29e8SJustin Miller * IRP as well as sends the IRP down the device stack to gather battery
116*a8da29e8SJustin Miller * related data, such tag and status. It also serves as the I/O
117*a8da29e8SJustin Miller * completion routine of which it elaborates the gathered data.
118*a8da29e8SJustin Miller *
119*a8da29e8SJustin Miller * @param[in] BatteryData
120*a8da29e8SJustin Miller * A pointer to battery data of an individual battery that contains
121*a8da29e8SJustin Miller * the IRP to be send down the device stack.
122*a8da29e8SJustin Miller */
123*a8da29e8SJustin Miller VOID
124*a8da29e8SJustin Miller NTAPI
CompBattMonitorIrpCompleteWorker(_In_ PCOMPBATT_BATTERY_DATA BatteryData)125*a8da29e8SJustin Miller CompBattMonitorIrpCompleteWorker(
126*a8da29e8SJustin Miller _In_ PCOMPBATT_BATTERY_DATA BatteryData)
127*a8da29e8SJustin Miller {
128*a8da29e8SJustin Miller NTSTATUS Status;
129*a8da29e8SJustin Miller PIRP Irp;
130*a8da29e8SJustin Miller UCHAR Mode;
131*a8da29e8SJustin Miller ULONG PrevPowerState;
132*a8da29e8SJustin Miller PDEVICE_OBJECT DeviceObject;
133*a8da29e8SJustin Miller BATTERY_STATUS BatteryStatus;
134*a8da29e8SJustin Miller PIO_STACK_LOCATION IoStackLocation;
135*a8da29e8SJustin Miller PCOMPBATT_DEVICE_EXTENSION DeviceExtension;
136*a8da29e8SJustin Miller
137*a8da29e8SJustin Miller /* Cache the necessary battery data */
138*a8da29e8SJustin Miller Irp = BatteryData->Irp;
139*a8da29e8SJustin Miller DeviceObject = BatteryData->DeviceObject;
140*a8da29e8SJustin Miller IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
141*a8da29e8SJustin Miller DeviceExtension = IoStackLocation->Parameters.Others.Argument1;
142*a8da29e8SJustin Miller
143*a8da29e8SJustin Miller /* Switch to the next stack as we have to setup the control function data there */
144*a8da29e8SJustin Miller IoStackLocation = IoGetNextIrpStackLocation(Irp);
145*a8da29e8SJustin Miller
146*a8da29e8SJustin Miller /* Has the I/O composite battery request succeeded? */
147*a8da29e8SJustin Miller Status = Irp->IoStatus.Status;
148*a8da29e8SJustin Miller if (!NT_SUCCESS(Status) && Status != STATUS_CANCELLED)
149*a8da29e8SJustin Miller {
150*a8da29e8SJustin Miller /*
151*a8da29e8SJustin Miller * This battery is being removed from the composite, perform
152*a8da29e8SJustin Miller * cleanups and do not inquire I/O requests again on this battery.
153*a8da29e8SJustin Miller */
154*a8da29e8SJustin Miller if (Status == STATUS_DEVICE_REMOVED)
155*a8da29e8SJustin Miller {
156*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
157*a8da29e8SJustin Miller DbgPrint("CompBatt: Battery (0x%p) is being removed from composite battery\n", BatteryData);
158*a8da29e8SJustin Miller
159*a8da29e8SJustin Miller IoFreeIrp(Irp);
160*a8da29e8SJustin Miller CompBattRemoveBattery(&BatteryData->BatteryName, DeviceExtension);
161*a8da29e8SJustin Miller return;
162*a8da29e8SJustin Miller }
163*a8da29e8SJustin Miller
164*a8da29e8SJustin Miller /*
165*a8da29e8SJustin Miller * This is the first time a battery is being added into the composite
166*a8da29e8SJustin Miller * (we understand that if Status was STATUS_DEVICE_NOT_CONNECTED).
167*a8da29e8SJustin Miller * We must invalidate the composite tag and request a recalculation
168*a8da29e8SJustin Miller * of the battery tag.
169*a8da29e8SJustin Miller */
170*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
171*a8da29e8SJustin Miller DbgPrint("CompBatt: Battery arrived for first time or disappeared (Status 0x%08lx)\n", Status);
172*a8da29e8SJustin Miller
173*a8da29e8SJustin Miller BatteryData->Tag = BATTERY_TAG_INVALID;
174*a8da29e8SJustin Miller
175*a8da29e8SJustin Miller /*
176*a8da29e8SJustin Miller * Invalidate the last read status interrupt time as well since the last
177*a8da29e8SJustin Miller * battery status data no longer applies. Same for the composite battery
178*a8da29e8SJustin Miller * as well.
179*a8da29e8SJustin Miller */
180*a8da29e8SJustin Miller BatteryData->InterruptTime = 0;
181*a8da29e8SJustin Miller DeviceExtension->InterruptTime = 0;
182*a8da29e8SJustin Miller
183*a8da29e8SJustin Miller /* Notify Battery Class the battery status incurs in a change */
184*a8da29e8SJustin Miller BatteryClassStatusNotify(DeviceExtension->ClassData);
185*a8da29e8SJustin Miller
186*a8da29e8SJustin Miller /* Setup the necessary I/O data to query the battery tag */
187*a8da29e8SJustin Miller IoStackLocation->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_TAG;
188*a8da29e8SJustin Miller IoStackLocation->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
189*a8da29e8SJustin Miller IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ULONG);
190*a8da29e8SJustin Miller BatteryData->Mode = COMPBATT_QUERY_TAG;
191*a8da29e8SJustin Miller BatteryData->WorkerBuffer.WorkerTag = 0xFFFFFFFF;
192*a8da29e8SJustin Miller
193*a8da29e8SJustin Miller /* Dispatch our request now to the battery's driver */
194*a8da29e8SJustin Miller goto DispatchRequest;
195*a8da29e8SJustin Miller }
196*a8da29e8SJustin Miller
197*a8da29e8SJustin Miller /* Our I/O request has been completed successfully, check what did we get */
198*a8da29e8SJustin Miller Mode = BatteryData->Mode;
199*a8da29e8SJustin Miller switch (Mode)
200*a8da29e8SJustin Miller {
201*a8da29e8SJustin Miller case COMPBATT_QUERY_TAG:
202*a8da29e8SJustin Miller {
203*a8da29e8SJustin Miller /*
204*a8da29e8SJustin Miller * This battery has just gotten a tag, acknowledge the composite battery
205*a8da29e8SJustin Miller * about that so it can recalculate its own composite tag.
206*a8da29e8SJustin Miller */
207*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
208*a8da29e8SJustin Miller DbgPrint("CompBatt: Battery (Device 0x%p) has a tag of %lu\n", DeviceObject, BatteryData->WorkerBuffer.WorkerTag);
209*a8da29e8SJustin Miller
210*a8da29e8SJustin Miller /* Ensure the battery tag is not bogus, getting a tag of 0 is illegal */
211*a8da29e8SJustin Miller ASSERT(BatteryData->WorkerBuffer.WorkerTag != 0);
212*a8da29e8SJustin Miller
213*a8da29e8SJustin Miller /* Assign the battery tag */
214*a8da29e8SJustin Miller BatteryData->Tag = BatteryData->WorkerBuffer.WorkerTag;
215*a8da29e8SJustin Miller BatteryData->Flags |= COMPBATT_TAG_ASSIGNED;
216*a8da29e8SJustin Miller
217*a8da29e8SJustin Miller /* Punt the composite battery flags, as the previous cached data no longer applies */
218*a8da29e8SJustin Miller DeviceExtension->Flags = 0;
219*a8da29e8SJustin Miller
220*a8da29e8SJustin Miller /* Notify the Battery Class driver this battery has got a tag */
221*a8da29e8SJustin Miller BatteryClassStatusNotify(DeviceExtension->ClassData);
222*a8da29e8SJustin Miller break;
223*a8da29e8SJustin Miller }
224*a8da29e8SJustin Miller
225*a8da29e8SJustin Miller case COMPBATT_READ_STATUS:
226*a8da29e8SJustin Miller {
227*a8da29e8SJustin Miller /*
228*a8da29e8SJustin Miller * Read the battery status only if the IRP has not been cancelled,
229*a8da29e8SJustin Miller * otherwise the request must be re-issued again. This typically
230*a8da29e8SJustin Miller * happens if the wait values are in conflict which it might
231*a8da29e8SJustin Miller * end up in inconsistent battery status results.
232*a8da29e8SJustin Miller */
233*a8da29e8SJustin Miller if (Status != STATUS_CANCELLED && !Irp->Cancel)
234*a8da29e8SJustin Miller {
235*a8da29e8SJustin Miller /*
236*a8da29e8SJustin Miller * If we reach here then the battery has entered into a change of
237*a8da29e8SJustin Miller * power state or its charge capacity has changed.
238*a8da29e8SJustin Miller */
239*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
240*a8da29e8SJustin Miller DbgPrint("CompBatt: Battery state (Device 0x%p) has changed\n", DeviceObject);
241*a8da29e8SJustin Miller
242*a8da29e8SJustin Miller /* Copy the battery status of this battery */
243*a8da29e8SJustin Miller RtlCopyMemory(&BatteryData->BatteryStatus,
244*a8da29e8SJustin Miller &BatteryData->WorkerBuffer.WorkerStatus,
245*a8da29e8SJustin Miller sizeof(BatteryData->BatteryStatus));
246*a8da29e8SJustin Miller
247*a8da29e8SJustin Miller /* Update the interrupt time as this is the most recent read of the battery status */
248*a8da29e8SJustin Miller BatteryData->InterruptTime = KeQueryInterruptTime();
249*a8da29e8SJustin Miller
250*a8da29e8SJustin Miller /*
251*a8da29e8SJustin Miller * Ensure we have not gotten unknown capacities while we waited for new
252*a8da29e8SJustin Miller * battery status. The battery might have malfunctioned or something.
253*a8da29e8SJustin Miller */
254*a8da29e8SJustin Miller if (BatteryData->WorkerBuffer.WorkerStatus.Capacity == BATTERY_UNKNOWN_CAPACITY)
255*a8da29e8SJustin Miller {
256*a8da29e8SJustin Miller /* We do not know the capacity of this battery, default the low and high capacities */
257*a8da29e8SJustin Miller BatteryData->WaitStatus.LowCapacity = BATTERY_UNKNOWN_CAPACITY;
258*a8da29e8SJustin Miller BatteryData->WaitStatus.HighCapacity = BATTERY_UNKNOWN_CAPACITY;
259*a8da29e8SJustin Miller }
260*a8da29e8SJustin Miller else
261*a8da29e8SJustin Miller {
262*a8da29e8SJustin Miller /* We know the capacity, adjust the low and high capacities accordingly */
263*a8da29e8SJustin Miller if (BatteryData->WaitStatus.LowCapacity >
264*a8da29e8SJustin Miller BatteryData->WorkerBuffer.WorkerStatus.Capacity)
265*a8da29e8SJustin Miller {
266*a8da29e8SJustin Miller BatteryData->WaitStatus.LowCapacity = BatteryData->WorkerBuffer.WorkerStatus.Capacity;
267*a8da29e8SJustin Miller }
268*a8da29e8SJustin Miller
269*a8da29e8SJustin Miller if (BatteryData->WaitStatus.HighCapacity <
270*a8da29e8SJustin Miller BatteryData->WorkerBuffer.WorkerStatus.Capacity)
271*a8da29e8SJustin Miller {
272*a8da29e8SJustin Miller BatteryData->WaitStatus.HighCapacity = BatteryData->WorkerBuffer.WorkerStatus.Capacity;
273*a8da29e8SJustin Miller }
274*a8da29e8SJustin Miller }
275*a8da29e8SJustin Miller
276*a8da29e8SJustin Miller /* Copy the current last read power state for the next wait */
277*a8da29e8SJustin Miller BatteryData->WaitStatus.PowerState = BatteryData->WorkerBuffer.WorkerStatus.PowerState;
278*a8da29e8SJustin Miller
279*a8da29e8SJustin Miller /*
280*a8da29e8SJustin Miller * Cache the previous power state of the composite battery and invalidate
281*a8da29e8SJustin Miller * the last computed battery status interrupt time. This is because,
282*a8da29e8SJustin Miller * logically, this specific battery incurred in a state change therefore
283*a8da29e8SJustin Miller * the previous composite status is no longer consistent.
284*a8da29e8SJustin Miller */
285*a8da29e8SJustin Miller PrevPowerState = DeviceExtension->BatteryStatus.PowerState;
286*a8da29e8SJustin Miller DeviceExtension->InterruptTime = 0;
287*a8da29e8SJustin Miller
288*a8da29e8SJustin Miller /* Compute a new battery status for the composite battery */
289*a8da29e8SJustin Miller Status = CompBattQueryStatus(DeviceExtension,
290*a8da29e8SJustin Miller DeviceExtension->Tag,
291*a8da29e8SJustin Miller &BatteryStatus);
292*a8da29e8SJustin Miller
293*a8da29e8SJustin Miller /* Print out the current battery status of the composite to the debugger */
294*a8da29e8SJustin Miller if ((CompBattDebug & COMPBATT_DEBUG_INFO) && NT_SUCCESS(Status))
295*a8da29e8SJustin Miller DbgPrint("CompBatt: Latest composite battery status\n"
296*a8da29e8SJustin Miller " PowerState -> 0x%lx\n"
297*a8da29e8SJustin Miller " Capacity -> %u\n"
298*a8da29e8SJustin Miller " Voltage -> %u\n"
299*a8da29e8SJustin Miller " Rate -> %d\n",
300*a8da29e8SJustin Miller BatteryStatus.PowerState,
301*a8da29e8SJustin Miller BatteryStatus.Capacity,
302*a8da29e8SJustin Miller BatteryStatus.Voltage,
303*a8da29e8SJustin Miller BatteryStatus.Rate);
304*a8da29e8SJustin Miller
305*a8da29e8SJustin Miller /*
306*a8da29e8SJustin Miller * Now determine whether should we notify the Battery Class driver due to
307*a8da29e8SJustin Miller * changes in power state settings in the composite battery. This could
308*a8da29e8SJustin Miller * happen in two following conditions:
309*a8da29e8SJustin Miller *
310*a8da29e8SJustin Miller * 1. The status notify flag was set for the respective power notification
311*a8da29e8SJustin Miller * settings, and the composite battery incurred in a change of such
312*a8da29e8SJustin Miller * settings. In this case we have to probe the current settings that
313*a8da29e8SJustin Miller * they have changed.
314*a8da29e8SJustin Miller *
315*a8da29e8SJustin Miller * 2. The status notify flag was not set, therefore we do not know the
316*a8da29e8SJustin Miller * exact configuration of the notification settings. We only care that
317*a8da29e8SJustin Miller * the power state has changed at this point.
318*a8da29e8SJustin Miller *
319*a8da29e8SJustin Miller * Why do we have to do this is because we have to warn the Battery Class
320*a8da29e8SJustin Miller * about the data that has changed.
321*a8da29e8SJustin Miller */
322*a8da29e8SJustin Miller if (!(DeviceExtension->Flags & COMPBATT_STATUS_NOTIFY_SET))
323*a8da29e8SJustin Miller {
324*a8da29e8SJustin Miller if (PrevPowerState != DeviceExtension->BatteryStatus.PowerState)
325*a8da29e8SJustin Miller {
326*a8da29e8SJustin Miller /* The previous power state is no longer valid, notify Battery Class */
327*a8da29e8SJustin Miller BatteryClassStatusNotify(DeviceExtension->ClassData);
328*a8da29e8SJustin Miller }
329*a8da29e8SJustin Miller }
330*a8da29e8SJustin Miller else
331*a8da29e8SJustin Miller {
332*a8da29e8SJustin Miller /*
333*a8da29e8SJustin Miller * Unlike the condition above, we check for power state change against
334*a8da29e8SJustin Miller * the current notify wait set since the notify set flag bit is assigned.
335*a8da29e8SJustin Miller */
336*a8da29e8SJustin Miller if (DeviceExtension->WaitNotifyStatus.PowerState != DeviceExtension->BatteryStatus.PowerState ||
337*a8da29e8SJustin Miller DeviceExtension->WaitNotifyStatus.LowCapacity > DeviceExtension->BatteryStatus.Capacity ||
338*a8da29e8SJustin Miller DeviceExtension->WaitNotifyStatus.HighCapacity < DeviceExtension->BatteryStatus.Capacity)
339*a8da29e8SJustin Miller {
340*a8da29e8SJustin Miller /* The following configuration settings have changed, notify Battery Class */
341*a8da29e8SJustin Miller BatteryClassStatusNotify(DeviceExtension->ClassData);
342*a8da29e8SJustin Miller }
343*a8da29e8SJustin Miller }
344*a8da29e8SJustin Miller }
345*a8da29e8SJustin Miller
346*a8da29e8SJustin Miller break;
347*a8da29e8SJustin Miller }
348*a8da29e8SJustin Miller
349*a8da29e8SJustin Miller default:
350*a8da29e8SJustin Miller {
351*a8da29e8SJustin Miller ASSERTMSG("CompBatt: BAD!!! WE SHOULD NOT BE HERE!\n", FALSE);
352*a8da29e8SJustin Miller UNREACHABLE;
353*a8da29e8SJustin Miller }
354*a8da29e8SJustin Miller }
355*a8da29e8SJustin Miller
356*a8da29e8SJustin Miller /* Setup the necessary data to read battery status */
357*a8da29e8SJustin Miller BatteryData->WaitStatus.BatteryTag = BatteryData->Tag;
358*a8da29e8SJustin Miller BatteryData->WaitStatus.Timeout = 3000; // FIXME: Hardcoded (wait for 3 seconds) because we do not have ACPI notifications implemented yet...
359*a8da29e8SJustin Miller
360*a8da29e8SJustin Miller RtlCopyMemory(&BatteryData->WorkerBuffer.WorkerWaitStatus,
361*a8da29e8SJustin Miller &BatteryData->WaitStatus,
362*a8da29e8SJustin Miller sizeof(BatteryData->WaitStatus));
363*a8da29e8SJustin Miller
364*a8da29e8SJustin Miller IoStackLocation->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_STATUS;
365*a8da29e8SJustin Miller IoStackLocation->Parameters.DeviceIoControl.InputBufferLength = sizeof(BatteryData->WorkerBuffer.WorkerWaitStatus);
366*a8da29e8SJustin Miller IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength = sizeof(BatteryData->WorkerBuffer.WorkerStatus);
367*a8da29e8SJustin Miller BatteryData->Mode = COMPBATT_READ_STATUS;
368*a8da29e8SJustin Miller
369*a8da29e8SJustin Miller DispatchRequest:
370*a8da29e8SJustin Miller /* Setup the system buffer to that of the battery data which it will hold the returned data */
371*a8da29e8SJustin Miller IoStackLocation->MajorFunction = IRP_MJ_DEVICE_CONTROL;
372*a8da29e8SJustin Miller Irp->AssociatedIrp.SystemBuffer = &BatteryData->WorkerBuffer;
373*a8da29e8SJustin Miller Irp->Cancel = FALSE;
374*a8da29e8SJustin Miller Irp->PendingReturned = FALSE;
375*a8da29e8SJustin Miller
376*a8da29e8SJustin Miller /* Setup the worker completion routine which it will invoke the worker later on */
377*a8da29e8SJustin Miller IoSetCompletionRoutine(Irp,
378*a8da29e8SJustin Miller CompBattMonitorIrpComplete,
379*a8da29e8SJustin Miller NULL,
380*a8da29e8SJustin Miller TRUE,
381*a8da29e8SJustin Miller TRUE,
382*a8da29e8SJustin Miller TRUE);
383*a8da29e8SJustin Miller
384*a8da29e8SJustin Miller /* Dispatch the I/O request now */
385*a8da29e8SJustin Miller IoCallDriver(DeviceObject, Irp);
386*a8da29e8SJustin Miller }
387*a8da29e8SJustin Miller
388*a8da29e8SJustin Miller VOID
389*a8da29e8SJustin Miller NTAPI
CompBattRecalculateTag(_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)390*a8da29e8SJustin Miller CompBattRecalculateTag(
391*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
392*a8da29e8SJustin Miller {
393*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
394*a8da29e8SJustin Miller ULONG Tag;
395*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
396*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING CompBattRecalculateTag\n");
397*a8da29e8SJustin Miller
398*a8da29e8SJustin Miller /* Loop the battery list */
399*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
400*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
401*a8da29e8SJustin Miller NextEntry = ListHead->Flink;
402*a8da29e8SJustin Miller while (NextEntry != ListHead)
403*a8da29e8SJustin Miller {
404*a8da29e8SJustin Miller /* Get the battery information and check if it has a tag */
405*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
406*a8da29e8SJustin Miller if (BatteryData->Flags & COMPBATT_TAG_ASSIGNED)
407*a8da29e8SJustin Miller {
408*a8da29e8SJustin Miller /* Generate the next tag and exit */
409*a8da29e8SJustin Miller Tag = DeviceExtension->NextTag;
410*a8da29e8SJustin Miller DeviceExtension->Flags |= COMPBATT_TAG_ASSIGNED;
411*a8da29e8SJustin Miller DeviceExtension->Tag = Tag;
412*a8da29e8SJustin Miller DeviceExtension->NextTag = Tag + 1;
413*a8da29e8SJustin Miller break;
414*a8da29e8SJustin Miller }
415*a8da29e8SJustin Miller
416*a8da29e8SJustin Miller /* No tag for this device extension, clear it */
417*a8da29e8SJustin Miller DeviceExtension->Tag = BATTERY_TAG_INVALID;
418*a8da29e8SJustin Miller NextEntry = NextEntry->Flink;
419*a8da29e8SJustin Miller }
420*a8da29e8SJustin Miller
421*a8da29e8SJustin Miller /* We're done */
422*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
423*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: EXITING CompBattRecalculateTag\n");
424*a8da29e8SJustin Miller }
425*a8da29e8SJustin Miller
426*a8da29e8SJustin Miller NTSTATUS
427*a8da29e8SJustin Miller NTAPI
CompBattIoctl(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)428*a8da29e8SJustin Miller CompBattIoctl(
429*a8da29e8SJustin Miller _In_ PDEVICE_OBJECT DeviceObject,
430*a8da29e8SJustin Miller _In_ PIRP Irp)
431*a8da29e8SJustin Miller {
432*a8da29e8SJustin Miller PCOMPBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
433*a8da29e8SJustin Miller NTSTATUS Status;
434*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING Ioctl\n");
435*a8da29e8SJustin Miller
436*a8da29e8SJustin Miller /* Let the class driver handle it */
437*a8da29e8SJustin Miller Status = BatteryClassIoctl(DeviceExtension->ClassData, Irp);
438*a8da29e8SJustin Miller if (Status == STATUS_NOT_SUPPORTED)
439*a8da29e8SJustin Miller {
440*a8da29e8SJustin Miller /* It failed, try the next driver up the stack */
441*a8da29e8SJustin Miller Irp->IoStatus.Status = Status;
442*a8da29e8SJustin Miller IoSkipCurrentIrpStackLocation(Irp);
443*a8da29e8SJustin Miller Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
444*a8da29e8SJustin Miller }
445*a8da29e8SJustin Miller
446*a8da29e8SJustin Miller /* Return status */
447*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: EXITING Ioctl\n");
448*a8da29e8SJustin Miller return Status;
449*a8da29e8SJustin Miller }
450*a8da29e8SJustin Miller
451*a8da29e8SJustin Miller NTSTATUS
452*a8da29e8SJustin Miller NTAPI
CompBattQueryTag(_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,_Out_ PULONG Tag)453*a8da29e8SJustin Miller CompBattQueryTag(
454*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,
455*a8da29e8SJustin Miller _Out_ PULONG Tag)
456*a8da29e8SJustin Miller {
457*a8da29e8SJustin Miller NTSTATUS Status;
458*a8da29e8SJustin Miller PAGED_CODE();
459*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING QueryTag\n");
460*a8da29e8SJustin Miller
461*a8da29e8SJustin Miller /* Was a tag assigned? */
462*a8da29e8SJustin Miller if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED))
463*a8da29e8SJustin Miller {
464*a8da29e8SJustin Miller /* Assign one */
465*a8da29e8SJustin Miller CompBattRecalculateTag(DeviceExtension);
466*a8da29e8SJustin Miller }
467*a8da29e8SJustin Miller
468*a8da29e8SJustin Miller /* Do we have a tag now? */
469*a8da29e8SJustin Miller if ((DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) && DeviceExtension->Tag != BATTERY_TAG_INVALID)
470*a8da29e8SJustin Miller {
471*a8da29e8SJustin Miller /* Return the tag */
472*a8da29e8SJustin Miller *Tag = DeviceExtension->Tag;
473*a8da29e8SJustin Miller Status = STATUS_SUCCESS;
474*a8da29e8SJustin Miller }
475*a8da29e8SJustin Miller else
476*a8da29e8SJustin Miller {
477*a8da29e8SJustin Miller /* No tag */
478*a8da29e8SJustin Miller *Tag = BATTERY_TAG_INVALID;
479*a8da29e8SJustin Miller Status = STATUS_NO_SUCH_DEVICE;
480*a8da29e8SJustin Miller }
481*a8da29e8SJustin Miller
482*a8da29e8SJustin Miller /* Return status */
483*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: EXITING QueryTag\n");
484*a8da29e8SJustin Miller return Status;
485*a8da29e8SJustin Miller }
486*a8da29e8SJustin Miller
487*a8da29e8SJustin Miller NTSTATUS
488*a8da29e8SJustin Miller NTAPI
CompBattDisableStatusNotify(_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)489*a8da29e8SJustin Miller CompBattDisableStatusNotify(
490*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
491*a8da29e8SJustin Miller {
492*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
493*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
494*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING DisableStatusNotify\n");
495*a8da29e8SJustin Miller
496*a8da29e8SJustin Miller /* Loop the battery list */
497*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
498*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
499*a8da29e8SJustin Miller NextEntry = ListHead->Flink;
500*a8da29e8SJustin Miller while (NextEntry != ListHead)
501*a8da29e8SJustin Miller {
502*a8da29e8SJustin Miller /* Get the battery information and clear capacity data */
503*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
504*a8da29e8SJustin Miller BatteryData->WaitStatus.LowCapacity = 0;
505*a8da29e8SJustin Miller BatteryData->WaitStatus.HighCapacity = 0x7FFFFFFF;
506*a8da29e8SJustin Miller NextEntry = NextEntry->Flink;
507*a8da29e8SJustin Miller }
508*a8da29e8SJustin Miller
509*a8da29e8SJustin Miller /* Done */
510*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
511*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: EXITING DisableStatusNotify\n");
512*a8da29e8SJustin Miller return STATUS_SUCCESS;
513*a8da29e8SJustin Miller }
514*a8da29e8SJustin Miller
515*a8da29e8SJustin Miller /**
516*a8da29e8SJustin Miller * @brief
517*a8da29e8SJustin Miller * Calculates the total discharging/charging rate flow of each individual
518*a8da29e8SJustin Miller * battery linked with the composite battery and determines whether at
519*a8da29e8SJustin Miller * least one battery is behaving improperly.
520*a8da29e8SJustin Miller *
521*a8da29e8SJustin Miller * @param[in] DeviceExtension
522*a8da29e8SJustin Miller * A pointer to a device extension which describes the composite battery
523*a8da29e8SJustin Miller * itself. It is used to gather each connected battery in the list with
524*a8da29e8SJustin Miller * the composite battery.
525*a8da29e8SJustin Miller *
526*a8da29e8SJustin Miller * @param[out] TotalRate
527*a8da29e8SJustin Miller * A pointer returned to caller that describes the total accumulated
528*a8da29e8SJustin Miller * rate flow of all batteries.
529*a8da29e8SJustin Miller *
530*a8da29e8SJustin Miller * @param[out] BatteriesCount
531*a8da29e8SJustin Miller * A pointer returned to caller that describes the batteries present.
532*a8da29e8SJustin Miller *
533*a8da29e8SJustin Miller * @return
534*a8da29e8SJustin Miller * Returns TRUE if at least one battery is behaving improperly, FALSE
535*a8da29e8SJustin Miller * otherwise. This is determined by the fact if a battery has a negative
536*a8da29e8SJustin Miller * rate but is charging, or if it has a positive rate but is discharging.
537*a8da29e8SJustin Miller */
538*a8da29e8SJustin Miller static
539*a8da29e8SJustin Miller BOOLEAN
CompBattCalculateTotalRateAndLinkedBatteries(_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,_Out_ PULONG TotalRate,_Out_ PULONG BatteriesCount)540*a8da29e8SJustin Miller CompBattCalculateTotalRateAndLinkedBatteries(
541*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,
542*a8da29e8SJustin Miller _Out_ PULONG TotalRate,
543*a8da29e8SJustin Miller _Out_ PULONG BatteriesCount)
544*a8da29e8SJustin Miller {
545*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
546*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
547*a8da29e8SJustin Miller BOOLEAN BadBattery = FALSE;
548*a8da29e8SJustin Miller ULONG LinkedBatteries = 0;
549*a8da29e8SJustin Miller ULONG BadBatteriesCount = 0;
550*a8da29e8SJustin Miller ULONG ComputedRate = 0;
551*a8da29e8SJustin Miller
552*a8da29e8SJustin Miller /* Loop over the linked batteries and sum up the total capacity rate */
553*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
554*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
555*a8da29e8SJustin Miller for (NextEntry = ListHead->Flink;
556*a8da29e8SJustin Miller NextEntry != ListHead;
557*a8da29e8SJustin Miller NextEntry = NextEntry->Flink)
558*a8da29e8SJustin Miller {
559*a8da29e8SJustin Miller /* Acquire the remove lock so this battery does not disappear under us */
560*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
561*a8da29e8SJustin Miller if (!NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp)))
562*a8da29e8SJustin Miller continue;
563*a8da29e8SJustin Miller
564*a8da29e8SJustin Miller /*
565*a8da29e8SJustin Miller * Ensure this battery has a valid tag and that its rate capacity
566*a8da29e8SJustin Miller * is not unknown. Reject unknown rates when calculating the total rate.
567*a8da29e8SJustin Miller */
568*a8da29e8SJustin Miller if ((BatteryData->Tag != BATTERY_TAG_INVALID) &&
569*a8da29e8SJustin Miller (BatteryData->BatteryStatus.Rate != BATTERY_UNKNOWN_RATE))
570*a8da29e8SJustin Miller {
571*a8da29e8SJustin Miller /*
572*a8da29e8SJustin Miller * Now the ultimate judgement for this battery is to determine
573*a8da29e8SJustin Miller * if the battery behaves optimally based on its current power
574*a8da29e8SJustin Miller * state it is and the rate flow of the battery.
575*a8da29e8SJustin Miller *
576*a8da29e8SJustin Miller * If the rate flow is positive the battery is receiving power
577*a8da29e8SJustin Miller * which increases the chemical potential energy as electrons
578*a8da29e8SJustin Miller * move around, THIS MEANS the battery is CHARGING. If the rate
579*a8da29e8SJustin Miller * flow is negative the battery cells are producing way less
580*a8da29e8SJustin Miller * electrical energy, thus the battery is DISCHARGING.
581*a8da29e8SJustin Miller *
582*a8da29e8SJustin Miller * A consistent battery is a battery of which power state matches
583*a8da29e8SJustin Miller * the rate flow. If that were the case, then we have found a bad
584*a8da29e8SJustin Miller * battery. The worst case is that a battery is physically damanged.
585*a8da29e8SJustin Miller */
586*a8da29e8SJustin Miller if ((BatteryData->BatteryStatus.PowerState & BATTERY_DISCHARGING) &&
587*a8da29e8SJustin Miller (BatteryData->BatteryStatus.Rate >= 0))
588*a8da29e8SJustin Miller {
589*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
590*a8da29e8SJustin Miller DbgPrint("CompBatt: The battery is discharging but in reality it is charging... (Rate %d)\n",
591*a8da29e8SJustin Miller BatteryData->BatteryStatus.Rate);
592*a8da29e8SJustin Miller
593*a8da29e8SJustin Miller BadBattery = TRUE;
594*a8da29e8SJustin Miller BadBatteriesCount++;
595*a8da29e8SJustin Miller }
596*a8da29e8SJustin Miller
597*a8da29e8SJustin Miller if ((BatteryData->BatteryStatus.PowerState & BATTERY_CHARGING) &&
598*a8da29e8SJustin Miller (BatteryData->BatteryStatus.Rate <= 0))
599*a8da29e8SJustin Miller {
600*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
601*a8da29e8SJustin Miller DbgPrint("CompBatt: The battery is charging but in reality it is discharging... (Rate %d)\n",
602*a8da29e8SJustin Miller BatteryData->BatteryStatus.Rate);
603*a8da29e8SJustin Miller
604*a8da29e8SJustin Miller BadBattery = TRUE;
605*a8da29e8SJustin Miller BadBatteriesCount++;
606*a8da29e8SJustin Miller }
607*a8da29e8SJustin Miller
608*a8da29e8SJustin Miller if (((BatteryData->BatteryStatus.PowerState & (BATTERY_CHARGING | BATTERY_DISCHARGING)) == 0) &&
609*a8da29e8SJustin Miller (BatteryData->BatteryStatus.Rate != 0))
610*a8da29e8SJustin Miller {
611*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
612*a8da29e8SJustin Miller DbgPrint("CompBatt: The battery is neither charging or discharging but has a contradicting rate... (Rate %d)\n",
613*a8da29e8SJustin Miller BatteryData->BatteryStatus.Rate);
614*a8da29e8SJustin Miller
615*a8da29e8SJustin Miller BadBattery = TRUE;
616*a8da29e8SJustin Miller BadBatteriesCount++;
617*a8da29e8SJustin Miller }
618*a8da29e8SJustin Miller
619*a8da29e8SJustin Miller /*
620*a8da29e8SJustin Miller * Sum up the rate of this battery to make up the total, even if that means
621*a8da29e8SJustin Miller * the battery may have incosistent rate. This is because it is still a linked
622*a8da29e8SJustin Miller * battery to the composite battery and it is used to power up the system nonetheless.
623*a8da29e8SJustin Miller */
624*a8da29e8SJustin Miller ComputedRate += BatteryData->BatteryStatus.Rate;
625*a8da29e8SJustin Miller }
626*a8da29e8SJustin Miller
627*a8da29e8SJustin Miller /* We are done with this individual battery */
628*a8da29e8SJustin Miller LinkedBatteries++;
629*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
630*a8da29e8SJustin Miller }
631*a8da29e8SJustin Miller
632*a8da29e8SJustin Miller /* Release the lock as we are no longer poking through the batteries list */
633*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
634*a8da29e8SJustin Miller
635*a8da29e8SJustin Miller /* Print out the total count of bad batteries we have found */
636*a8da29e8SJustin Miller if (BadBatteriesCount > 0)
637*a8da29e8SJustin Miller {
638*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
639*a8da29e8SJustin Miller DbgPrint("CompBatt: %lu bad batteries have been found!\n", BadBatteriesCount);
640*a8da29e8SJustin Miller }
641*a8da29e8SJustin Miller
642*a8da29e8SJustin Miller *TotalRate = ComputedRate;
643*a8da29e8SJustin Miller *BatteriesCount = LinkedBatteries;
644*a8da29e8SJustin Miller return BadBattery;
645*a8da29e8SJustin Miller }
646*a8da29e8SJustin Miller
647*a8da29e8SJustin Miller /**
648*a8da29e8SJustin Miller * @brief
649*a8da29e8SJustin Miller * Sets a new configuration battery wait status settings of each battery.
650*a8da29e8SJustin Miller * The purpose of this is so that the composite battery gets notified
651*a8da29e8SJustin Miller * of new battery status as if it was a single battery.
652*a8da29e8SJustin Miller *
653*a8da29e8SJustin Miller * @param[in] DeviceExtension
654*a8da29e8SJustin Miller * A pointer to a device extension which describes the composite battery
655*a8da29e8SJustin Miller * itself. It is used to gather each connected battery in the list with
656*a8da29e8SJustin Miller * the composite battery.
657*a8da29e8SJustin Miller *
658*a8da29e8SJustin Miller * @param[in] BatteryTag
659*a8da29e8SJustin Miller * A battery tag supplied by the caller. This is typically the tag of
660*a8da29e8SJustin Miller * the composite battery which is used to check against the cached tag
661*a8da29e8SJustin Miller * of the composite battery if it has changed or not.
662*a8da29e8SJustin Miller *
663*a8da29e8SJustin Miller * @param[in] BatteryNotify
664*a8da29e8SJustin Miller * A pointer to a structure filled with battery notification settings,
665*a8da29e8SJustin Miller * supplied by the caller. It is used as the new values for the
666*a8da29e8SJustin Miller * configuration wait settings.
667*a8da29e8SJustin Miller *
668*a8da29e8SJustin Miller * @return
669*a8da29e8SJustin Miller * Returns STATUS_NO_SUCH_DEVICE if the supplied battery tag does not match
670*a8da29e8SJustin Miller * with that of the cached composite battery's tag or if the composite
671*a8da29e8SJustin Miller * battery currently does not have a tag assigned. Otherwise STATUS_SUCCESS
672*a8da29e8SJustin Miller * is returned.
673*a8da29e8SJustin Miller */
674*a8da29e8SJustin Miller NTSTATUS
675*a8da29e8SJustin Miller NTAPI
CompBattSetStatusNotify(_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,_In_ ULONG BatteryTag,_In_ PBATTERY_NOTIFY BatteryNotify)676*a8da29e8SJustin Miller CompBattSetStatusNotify(
677*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,
678*a8da29e8SJustin Miller _In_ ULONG BatteryTag,
679*a8da29e8SJustin Miller _In_ PBATTERY_NOTIFY BatteryNotify)
680*a8da29e8SJustin Miller {
681*a8da29e8SJustin Miller NTSTATUS Status;
682*a8da29e8SJustin Miller BOOLEAN BadBattery;
683*a8da29e8SJustin Miller ULONG TotalRate;
684*a8da29e8SJustin Miller ULONG BatteriesCount;
685*a8da29e8SJustin Miller ULONG HighestCapacity;
686*a8da29e8SJustin Miller ULONG LowCapDifference, HighCapDifference, LowDelta, HighDelta;
687*a8da29e8SJustin Miller BATTERY_STATUS BatteryStatus;
688*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
689*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
690*a8da29e8SJustin Miller
691*a8da29e8SJustin Miller /*
692*a8da29e8SJustin Miller * The caller wants to set new status notification settings but the composite
693*a8da29e8SJustin Miller * battery does not have a valid tag assigned, or the tag does not actually match.
694*a8da29e8SJustin Miller */
695*a8da29e8SJustin Miller if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) ||
696*a8da29e8SJustin Miller (DeviceExtension->Tag != BatteryTag))
697*a8da29e8SJustin Miller {
698*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
699*a8da29e8SJustin Miller DbgPrint("CompBatt: Composite battery tag not assigned or not matching (Tag -> %lu, Composite Tag -> %lu)\n",
700*a8da29e8SJustin Miller BatteryTag, DeviceExtension->Tag);
701*a8da29e8SJustin Miller
702*a8da29e8SJustin Miller return STATUS_NO_SUCH_DEVICE;
703*a8da29e8SJustin Miller }
704*a8da29e8SJustin Miller
705*a8da29e8SJustin Miller /*
706*a8da29e8SJustin Miller * Before we are setting up new status wait notification points we need to
707*a8da29e8SJustin Miller * refresh the composite status so that we get to know what values should be
708*a8da29e8SJustin Miller * set for the current notification wait status.
709*a8da29e8SJustin Miller */
710*a8da29e8SJustin Miller Status = CompBattQueryStatus(DeviceExtension,
711*a8da29e8SJustin Miller BatteryTag,
712*a8da29e8SJustin Miller &BatteryStatus);
713*a8da29e8SJustin Miller if (!NT_SUCCESS(Status))
714*a8da29e8SJustin Miller {
715*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_ERR)
716*a8da29e8SJustin Miller DbgPrint("CompBatt: Failed to refresh composite battery's status (Status 0x%08lx)\n", Status);
717*a8da29e8SJustin Miller
718*a8da29e8SJustin Miller return Status;
719*a8da29e8SJustin Miller }
720*a8da29e8SJustin Miller
721*a8da29e8SJustin Miller /* Print out battery status data that has been polled */
722*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_INFO)
723*a8da29e8SJustin Miller DbgPrint("CompBatt: Latest composite battery status (when setting notify status)\n"
724*a8da29e8SJustin Miller " PowerState -> 0x%lx\n"
725*a8da29e8SJustin Miller " Capacity -> %u\n"
726*a8da29e8SJustin Miller " Voltage -> %u\n"
727*a8da29e8SJustin Miller " Rate -> %d\n",
728*a8da29e8SJustin Miller BatteryStatus.PowerState,
729*a8da29e8SJustin Miller BatteryStatus.Capacity,
730*a8da29e8SJustin Miller BatteryStatus.Voltage,
731*a8da29e8SJustin Miller BatteryStatus.Rate);
732*a8da29e8SJustin Miller
733*a8da29e8SJustin Miller /* Calculate the high and low capacity differences based on the real summed capacity of the composite */
734*a8da29e8SJustin Miller LowCapDifference = DeviceExtension->BatteryStatus.Capacity - BatteryNotify->LowCapacity;
735*a8da29e8SJustin Miller HighCapDifference = BatteryNotify->HighCapacity - DeviceExtension->BatteryStatus.Capacity;
736*a8da29e8SJustin Miller
737*a8da29e8SJustin Miller /* Cache the notification parameters provided for later usage when polling for battery status */
738*a8da29e8SJustin Miller DeviceExtension->WaitNotifyStatus.PowerState = BatteryNotify->PowerState;
739*a8da29e8SJustin Miller DeviceExtension->WaitNotifyStatus.LowCapacity = BatteryNotify->LowCapacity;
740*a8da29e8SJustin Miller DeviceExtension->WaitNotifyStatus.HighCapacity = BatteryNotify->HighCapacity;
741*a8da29e8SJustin Miller
742*a8da29e8SJustin Miller /* Toggle the valid notify flag as these are the newer notification settings */
743*a8da29e8SJustin Miller DeviceExtension->Flags |= COMPBATT_STATUS_NOTIFY_SET;
744*a8da29e8SJustin Miller
745*a8da29e8SJustin Miller /*
746*a8da29e8SJustin Miller * Get the number of currently linked batteries to composite and total rate,
747*a8da29e8SJustin Miller * we will use these counters later to determine the wait values for each
748*a8da29e8SJustin Miller * individual battery.
749*a8da29e8SJustin Miller */
750*a8da29e8SJustin Miller BadBattery = CompBattCalculateTotalRateAndLinkedBatteries(DeviceExtension,
751*a8da29e8SJustin Miller &TotalRate,
752*a8da29e8SJustin Miller &BatteriesCount);
753*a8da29e8SJustin Miller
754*a8da29e8SJustin Miller /*
755*a8da29e8SJustin Miller * Of course we have to be sure that we have at least one battery linked
756*a8da29e8SJustin Miller * with the composite battery at this time of getting invoked to set new
757*a8da29e8SJustin Miller * notification wait settings.
758*a8da29e8SJustin Miller */
759*a8da29e8SJustin Miller ASSERT(BatteriesCount != 0);
760*a8da29e8SJustin Miller
761*a8da29e8SJustin Miller /* Walk over the linked batteries list and set up new wait configuration settings */
762*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
763*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
764*a8da29e8SJustin Miller for (NextEntry = ListHead->Flink;
765*a8da29e8SJustin Miller NextEntry != ListHead;
766*a8da29e8SJustin Miller NextEntry = NextEntry->Flink)
767*a8da29e8SJustin Miller {
768*a8da29e8SJustin Miller /* Acquire the remove lock so this battery does not disappear under us */
769*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
770*a8da29e8SJustin Miller if (!NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp)))
771*a8da29e8SJustin Miller continue;
772*a8da29e8SJustin Miller
773*a8da29e8SJustin Miller /* Now release the device lock since the battery can't go away */
774*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
775*a8da29e8SJustin Miller
776*a8da29e8SJustin Miller /* Make sure this battery has a tag before setting new wait values */
777*a8da29e8SJustin Miller if (BatteryData->Tag != BATTERY_TAG_INVALID)
778*a8da29e8SJustin Miller {
779*a8da29e8SJustin Miller /*
780*a8da29e8SJustin Miller * And also make sure this battery does not have an unknown
781*a8da29e8SJustin Miller * capacity, we cannot set up new configuration wait settings
782*a8da29e8SJustin Miller * based on that. Default the low and high wait capacities.
783*a8da29e8SJustin Miller */
784*a8da29e8SJustin Miller if (BatteryData->BatteryStatus.Capacity != BATTERY_UNKNOWN_CAPACITY)
785*a8da29e8SJustin Miller {
786*a8da29e8SJustin Miller /*
787*a8da29e8SJustin Miller * Calculate the low capacity wait setting. If at least one
788*a8da29e8SJustin Miller * bad battery was found while we computed the total composite
789*a8da29e8SJustin Miller * rate, then divide the difference between the total batteries.
790*a8da29e8SJustin Miller * Otherwise compute the battery deltas of the composite based
791*a8da29e8SJustin Miller * on total summed capacity rate. Otherwise if the total rate
792*a8da29e8SJustin Miller * is 0, then the real wait low and high capacities will be on
793*a8da29e8SJustin Miller * par with the real capacity.
794*a8da29e8SJustin Miller */
795*a8da29e8SJustin Miller if (BadBattery)
796*a8da29e8SJustin Miller {
797*a8da29e8SJustin Miller LowDelta = LowCapDifference / BatteriesCount;
798*a8da29e8SJustin Miller HighDelta = HighCapDifference / BatteriesCount;
799*a8da29e8SJustin Miller }
800*a8da29e8SJustin Miller else
801*a8da29e8SJustin Miller {
802*a8da29e8SJustin Miller if (TotalRate)
803*a8da29e8SJustin Miller {
804*a8da29e8SJustin Miller LowDelta = COMPUTE_BATT_CAP_DELTA(LowCapDifference, BatteryData, TotalRate);
805*a8da29e8SJustin Miller HighDelta = COMPUTE_BATT_CAP_DELTA(HighCapDifference, BatteryData, TotalRate);
806*a8da29e8SJustin Miller }
807*a8da29e8SJustin Miller else
808*a8da29e8SJustin Miller {
809*a8da29e8SJustin Miller LowDelta = 0;
810*a8da29e8SJustin Miller HighDelta = 0;
811*a8da29e8SJustin Miller }
812*a8da29e8SJustin Miller }
813*a8da29e8SJustin Miller
814*a8da29e8SJustin Miller /*
815*a8da29e8SJustin Miller * Assign the wait low capacity setting ONLY if the battery delta
816*a8da29e8SJustin Miller * is not high. Otherwise it has overflowed and we cannot use that
817*a8da29e8SJustin Miller * for low capacity, of which we have to default it to 0.
818*a8da29e8SJustin Miller */
819*a8da29e8SJustin Miller if (BatteryData->BatteryStatus.Capacity > LowDelta)
820*a8da29e8SJustin Miller {
821*a8da29e8SJustin Miller BatteryData->WaitStatus.LowCapacity = BatteryData->BatteryStatus.Capacity - LowDelta;
822*a8da29e8SJustin Miller }
823*a8da29e8SJustin Miller else
824*a8da29e8SJustin Miller {
825*a8da29e8SJustin Miller BatteryData->WaitStatus.LowCapacity = COMPBATT_WAIT_MIN_LOW_CAPACITY;
826*a8da29e8SJustin Miller }
827*a8da29e8SJustin Miller
828*a8da29e8SJustin Miller /*
829*a8da29e8SJustin Miller * Assign the wait high capacity setting ONLY if the real capacity
830*a8da29e8SJustin Miller * is not above the maximum highest capacity constant.
831*a8da29e8SJustin Miller */
832*a8da29e8SJustin Miller HighestCapacity = COMPBATT_WAIT_MAX_HIGH_CAPACITY - HighDelta;
833*a8da29e8SJustin Miller if (HighestCapacity < BatteryData->BatteryStatus.Capacity)
834*a8da29e8SJustin Miller {
835*a8da29e8SJustin Miller BatteryData->WaitStatus.HighCapacity = HighestCapacity;
836*a8da29e8SJustin Miller }
837*a8da29e8SJustin Miller else
838*a8da29e8SJustin Miller {
839*a8da29e8SJustin Miller BatteryData->WaitStatus.HighCapacity = BatteryData->BatteryStatus.Capacity + HighDelta;
840*a8da29e8SJustin Miller }
841*a8da29e8SJustin Miller
842*a8da29e8SJustin Miller /*
843*a8da29e8SJustin Miller * We have set up the wait values but they are in conflict with the
844*a8da29e8SJustin Miller * ones set up by the IRP complete worker. We have to cancel the IRP
845*a8da29e8SJustin Miller * so the worker will copy our wait configuration values.
846*a8da29e8SJustin Miller */
847*a8da29e8SJustin Miller if ((BatteryData->Mode == COMPBATT_READ_STATUS) &&
848*a8da29e8SJustin Miller (BatteryData->WaitStatus.PowerState != BatteryData->WorkerBuffer.WorkerWaitStatus.PowerState ||
849*a8da29e8SJustin Miller BatteryData->WaitStatus.LowCapacity != BatteryData->WorkerBuffer.WorkerWaitStatus.LowCapacity ||
850*a8da29e8SJustin Miller BatteryData->WaitStatus.HighCapacity != BatteryData->WorkerBuffer.WorkerWaitStatus.HighCapacity))
851*a8da29e8SJustin Miller {
852*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_INFO)
853*a8da29e8SJustin Miller DbgPrint("CompBatt: Configuration wait values are in conflict\n"
854*a8da29e8SJustin Miller " BatteryData->WaitStatus.PowerState -> 0x%lx\n"
855*a8da29e8SJustin Miller " BatteryData->WorkerBuffer.WorkerWaitStatus.PowerState -> 0x%lx\n"
856*a8da29e8SJustin Miller " BatteryData->WaitStatus.LowCapacity -> %u\n"
857*a8da29e8SJustin Miller " BatteryData->WorkerBuffer.WorkerWaitStatus.LowCapacity -> %u\n"
858*a8da29e8SJustin Miller " BatteryData->WaitStatus.HighCapacity -> %u\n"
859*a8da29e8SJustin Miller " BatteryData->WorkerBuffer.WorkerWaitStatus.HighCapacity -> %u\n",
860*a8da29e8SJustin Miller BatteryData->WaitStatus.PowerState,
861*a8da29e8SJustin Miller BatteryData->WorkerBuffer.WorkerWaitStatus.PowerState,
862*a8da29e8SJustin Miller BatteryData->WaitStatus.LowCapacity,
863*a8da29e8SJustin Miller BatteryData->WorkerBuffer.WorkerWaitStatus.LowCapacity,
864*a8da29e8SJustin Miller BatteryData->WaitStatus.HighCapacity,
865*a8da29e8SJustin Miller BatteryData->WorkerBuffer.WorkerWaitStatus.HighCapacity);
866*a8da29e8SJustin Miller
867*a8da29e8SJustin Miller IoCancelIrp(BatteryData->Irp);
868*a8da29e8SJustin Miller }
869*a8da29e8SJustin Miller }
870*a8da29e8SJustin Miller else
871*a8da29e8SJustin Miller {
872*a8da29e8SJustin Miller BatteryData->WaitStatus.LowCapacity = BATTERY_UNKNOWN_CAPACITY;
873*a8da29e8SJustin Miller BatteryData->WaitStatus.HighCapacity = BATTERY_UNKNOWN_CAPACITY;
874*a8da29e8SJustin Miller }
875*a8da29e8SJustin Miller }
876*a8da29e8SJustin Miller
877*a8da29e8SJustin Miller /* We are done with this battery */
878*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
879*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
880*a8da29e8SJustin Miller }
881*a8da29e8SJustin Miller
882*a8da29e8SJustin Miller /* Release the lock as we are no longer poking through the batteries list */
883*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
884*a8da29e8SJustin Miller
885*a8da29e8SJustin Miller /* Ensure the composite battery did not incur in drastic changes of tag */
886*a8da29e8SJustin Miller if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) ||
887*a8da29e8SJustin Miller (DeviceExtension->Tag != BatteryTag))
888*a8da29e8SJustin Miller {
889*a8da29e8SJustin Miller /*
890*a8da29e8SJustin Miller * Either the last battery was removed (in this case the composite is no
891*a8da29e8SJustin Miller * longer existing) or a battery was removed of which the whole battery
892*a8da29e8SJustin Miller * information must be recomputed and such.
893*a8da29e8SJustin Miller */
894*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
895*a8da29e8SJustin Miller DbgPrint("CompBatt: Last battery or a battery was removed, the whole composite data must be recomputed\n");
896*a8da29e8SJustin Miller
897*a8da29e8SJustin Miller return STATUS_NO_SUCH_DEVICE;
898*a8da29e8SJustin Miller }
899*a8da29e8SJustin Miller
900*a8da29e8SJustin Miller return STATUS_SUCCESS;
901*a8da29e8SJustin Miller }
902*a8da29e8SJustin Miller
903*a8da29e8SJustin Miller /**
904*a8da29e8SJustin Miller * @brief
905*a8da29e8SJustin Miller * Queries the battery status of each individiual connected battery with
906*a8da29e8SJustin Miller * the composite battery and combines all the retrieved data as one
907*a8da29e8SJustin Miller * single battery status for the composite battery.
908*a8da29e8SJustin Miller *
909*a8da29e8SJustin Miller * @param[in] DeviceExtension
910*a8da29e8SJustin Miller * A pointer to a device extension which describes the composite battery
911*a8da29e8SJustin Miller * itself. It is used to gather each connected battery in the list with
912*a8da29e8SJustin Miller * the composite battery.
913*a8da29e8SJustin Miller *
914*a8da29e8SJustin Miller * @param[in] Tag
915*a8da29e8SJustin Miller * A battery tag supplied by the caller. This is typically the tag of
916*a8da29e8SJustin Miller * the composite battery which is used to check against the cached tag
917*a8da29e8SJustin Miller * of the composite battery if it has changed or not.
918*a8da29e8SJustin Miller *
919*a8da29e8SJustin Miller * @param[out] BatteryStatus
920*a8da29e8SJustin Miller * A pointer to a battery status that contains the combined data, returned
921*a8da29e8SJustin Miller * to the caller. It serves as the battery status for the composite battery.
922*a8da29e8SJustin Miller *
923*a8da29e8SJustin Miller * @return
924*a8da29e8SJustin Miller * Returns STATUS_NO_SUCH_DEVICE if the supplied battery tag does not match
925*a8da29e8SJustin Miller * with that of the cached composite battery's tag or if the composite
926*a8da29e8SJustin Miller * battery currently does not have a tag assigned. Otherwise STATUS_SUCCESS
927*a8da29e8SJustin Miller * is returned, which it will also return success if the composite battery's
928*a8da29e8SJustin Miller * cached battery status is fresh which indicates it has already been computed.
929*a8da29e8SJustin Miller */
930*a8da29e8SJustin Miller NTSTATUS
931*a8da29e8SJustin Miller NTAPI
CompBattQueryStatus(_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,_In_ ULONG Tag,_Out_ PBATTERY_STATUS BatteryStatus)932*a8da29e8SJustin Miller CompBattQueryStatus(
933*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,
934*a8da29e8SJustin Miller _In_ ULONG Tag,
935*a8da29e8SJustin Miller _Out_ PBATTERY_STATUS BatteryStatus)
936*a8da29e8SJustin Miller {
937*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
938*a8da29e8SJustin Miller BATTERY_WAIT_STATUS Wait;
939*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
940*a8da29e8SJustin Miller ULONGLONG LastReadTime, CurrentReadTime;
941*a8da29e8SJustin Miller NTSTATUS Status = STATUS_SUCCESS;
942*a8da29e8SJustin Miller
943*a8da29e8SJustin Miller /*
944*a8da29e8SJustin Miller * The caller wants to update the composite battery status but the composite
945*a8da29e8SJustin Miller * itself does not have a valid tag assigned, or the tag does not actually match.
946*a8da29e8SJustin Miller */
947*a8da29e8SJustin Miller if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) ||
948*a8da29e8SJustin Miller (DeviceExtension->Tag != Tag))
949*a8da29e8SJustin Miller {
950*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
951*a8da29e8SJustin Miller DbgPrint("CompBatt: Composite battery tag not assigned or not matching (Tag -> %lu, Composite Tag -> %lu)\n",
952*a8da29e8SJustin Miller Tag, DeviceExtension->Tag);
953*a8da29e8SJustin Miller
954*a8da29e8SJustin Miller return STATUS_NO_SUCH_DEVICE;
955*a8da29e8SJustin Miller }
956*a8da29e8SJustin Miller
957*a8da29e8SJustin Miller /* Initialize the status and wait fields with zeros */
958*a8da29e8SJustin Miller RtlZeroMemory(BatteryStatus, sizeof(*BatteryStatus));
959*a8da29e8SJustin Miller RtlZeroMemory(&Wait, sizeof(Wait));
960*a8da29e8SJustin Miller
961*a8da29e8SJustin Miller /*
962*a8da29e8SJustin Miller * The battery status was already updated when the caller queried for new
963*a8da29e8SJustin Miller * status. We do not need to update the status again for no reason.
964*a8da29e8SJustin Miller * Just give them the data outright.
965*a8da29e8SJustin Miller */
966*a8da29e8SJustin Miller CurrentReadTime = KeQueryInterruptTime();
967*a8da29e8SJustin Miller LastReadTime = CurrentReadTime - DeviceExtension->InterruptTime;
968*a8da29e8SJustin Miller if (LastReadTime < COMPBATT_FRESH_STATUS_TIME)
969*a8da29e8SJustin Miller {
970*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
971*a8da29e8SJustin Miller DbgPrint("CompBatt: Composite battery status data is fresh, no need to update it again\n");
972*a8da29e8SJustin Miller
973*a8da29e8SJustin Miller RtlCopyMemory(BatteryStatus, &DeviceExtension->BatteryStatus, sizeof(BATTERY_STATUS));
974*a8da29e8SJustin Miller return STATUS_SUCCESS;
975*a8da29e8SJustin Miller }
976*a8da29e8SJustin Miller
977*a8da29e8SJustin Miller /*
978*a8da29e8SJustin Miller * Initialize the battery status context with unknown defaults, until we get
979*a8da29e8SJustin Miller * to retrieve the real data from each battery and compute the exact status.
980*a8da29e8SJustin Miller * Assume the system is powered by AC source for now until we find out it is
981*a8da29e8SJustin Miller * not the case.
982*a8da29e8SJustin Miller */
983*a8da29e8SJustin Miller BatteryStatus->PowerState = BATTERY_POWER_ON_LINE;
984*a8da29e8SJustin Miller BatteryStatus->Capacity = BATTERY_UNKNOWN_CAPACITY;
985*a8da29e8SJustin Miller BatteryStatus->Voltage = BATTERY_UNKNOWN_VOLTAGE;
986*a8da29e8SJustin Miller BatteryStatus->Rate = BATTERY_UNKNOWN_RATE;
987*a8da29e8SJustin Miller
988*a8da29e8SJustin Miller /* Iterate over all the present linked batteries and retrieve their status */
989*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
990*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
991*a8da29e8SJustin Miller for (NextEntry = ListHead->Flink;
992*a8da29e8SJustin Miller NextEntry != ListHead;
993*a8da29e8SJustin Miller NextEntry = NextEntry->Flink)
994*a8da29e8SJustin Miller {
995*a8da29e8SJustin Miller /* Acquire the remove lock so this battery does not disappear under us */
996*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
997*a8da29e8SJustin Miller if (!NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp)))
998*a8da29e8SJustin Miller continue;
999*a8da29e8SJustin Miller
1000*a8da29e8SJustin Miller /* Now release the device lock since the battery can't go away */
1001*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1002*a8da29e8SJustin Miller
1003*a8da29e8SJustin Miller /* Setup the battery tag for the status wait which is needed to send off the IOCTL */
1004*a8da29e8SJustin Miller Wait.BatteryTag = BatteryData->Tag;
1005*a8da29e8SJustin Miller
1006*a8da29e8SJustin Miller /* Make sure this battery has a tag before we send off the IOCTL */
1007*a8da29e8SJustin Miller if (BatteryData->Tag != BATTERY_TAG_INVALID)
1008*a8da29e8SJustin Miller {
1009*a8da29e8SJustin Miller /* Only query new battery status data if it is no longer fresh */
1010*a8da29e8SJustin Miller LastReadTime = CurrentReadTime - BatteryData->InterruptTime;
1011*a8da29e8SJustin Miller if (LastReadTime > COMPBATT_FRESH_STATUS_TIME)
1012*a8da29e8SJustin Miller {
1013*a8da29e8SJustin Miller RtlZeroMemory(&BatteryData->BatteryStatus,
1014*a8da29e8SJustin Miller sizeof(BatteryData->BatteryStatus));
1015*a8da29e8SJustin Miller Status = BatteryIoctl(IOCTL_BATTERY_QUERY_STATUS,
1016*a8da29e8SJustin Miller BatteryData->DeviceObject,
1017*a8da29e8SJustin Miller &Wait,
1018*a8da29e8SJustin Miller sizeof(Wait),
1019*a8da29e8SJustin Miller &BatteryData->BatteryStatus,
1020*a8da29e8SJustin Miller sizeof(BatteryData->BatteryStatus),
1021*a8da29e8SJustin Miller FALSE);
1022*a8da29e8SJustin Miller if (!NT_SUCCESS(Status))
1023*a8da29e8SJustin Miller {
1024*a8da29e8SJustin Miller /*
1025*a8da29e8SJustin Miller * If the device is being suddenly removed then we must invalidate
1026*a8da29e8SJustin Miller * both this battery and composite tags.
1027*a8da29e8SJustin Miller */
1028*a8da29e8SJustin Miller if (Status == STATUS_DEVICE_REMOVED)
1029*a8da29e8SJustin Miller {
1030*a8da29e8SJustin Miller Status = STATUS_NO_SUCH_DEVICE;
1031*a8da29e8SJustin Miller }
1032*a8da29e8SJustin Miller
1033*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1034*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1035*a8da29e8SJustin Miller break;
1036*a8da29e8SJustin Miller }
1037*a8da29e8SJustin Miller
1038*a8da29e8SJustin Miller /* Update the timestamp of the current read of battery status */
1039*a8da29e8SJustin Miller BatteryData->InterruptTime = CurrentReadTime;
1040*a8da29e8SJustin Miller }
1041*a8da29e8SJustin Miller
1042*a8da29e8SJustin Miller /*
1043*a8da29e8SJustin Miller * Now it is time to combine the data into the composite status.
1044*a8da29e8SJustin Miller * The battery is either charging or discharging. AC is present
1045*a8da29e8SJustin Miller * only if the charger supplies current to all batteries. And
1046*a8da29e8SJustin Miller * the composite is deemed as critical if at least one battery
1047*a8da29e8SJustin Miller * is discharging and it is in crtitical state.
1048*a8da29e8SJustin Miller */
1049*a8da29e8SJustin Miller BatteryStatus->PowerState |= (BatteryData->BatteryStatus.PowerState & (BATTERY_CHARGING | BATTERY_DISCHARGING));
1050*a8da29e8SJustin Miller BatteryStatus->PowerState &= (BatteryData->BatteryStatus.PowerState | ~BATTERY_POWER_ON_LINE);
1051*a8da29e8SJustin Miller if ((BatteryData->BatteryStatus.PowerState & BATTERY_CRITICAL) &&
1052*a8da29e8SJustin Miller (BatteryData->BatteryStatus.PowerState & BATTERY_DISCHARGING))
1053*a8da29e8SJustin Miller {
1054*a8da29e8SJustin Miller BatteryStatus->PowerState |= BATTERY_CRITICAL;
1055*a8da29e8SJustin Miller }
1056*a8da29e8SJustin Miller
1057*a8da29e8SJustin Miller /* Add up the battery capacity if it is not unknown */
1058*a8da29e8SJustin Miller if (BatteryData->BatteryStatus.Capacity != BATTERY_UNKNOWN_CAPACITY)
1059*a8da29e8SJustin Miller {
1060*a8da29e8SJustin Miller if (BatteryStatus->Capacity != BATTERY_UNKNOWN_CAPACITY)
1061*a8da29e8SJustin Miller {
1062*a8da29e8SJustin Miller BatteryStatus->Capacity += BatteryData->BatteryStatus.Capacity;
1063*a8da29e8SJustin Miller }
1064*a8da29e8SJustin Miller else
1065*a8da29e8SJustin Miller {
1066*a8da29e8SJustin Miller BatteryStatus->Capacity = BatteryData->BatteryStatus.Capacity;
1067*a8da29e8SJustin Miller }
1068*a8da29e8SJustin Miller }
1069*a8da29e8SJustin Miller
1070*a8da29e8SJustin Miller /* Always pick up the greatest voltage for the composite battery */
1071*a8da29e8SJustin Miller if (BatteryData->BatteryStatus.Voltage != BATTERY_UNKNOWN_VOLTAGE)
1072*a8da29e8SJustin Miller {
1073*a8da29e8SJustin Miller if (BatteryStatus->Voltage != BATTERY_UNKNOWN_VOLTAGE)
1074*a8da29e8SJustin Miller {
1075*a8da29e8SJustin Miller BatteryStatus->Voltage = max(BatteryStatus->Voltage,
1076*a8da29e8SJustin Miller BatteryData->BatteryStatus.Voltage);
1077*a8da29e8SJustin Miller }
1078*a8da29e8SJustin Miller else
1079*a8da29e8SJustin Miller {
1080*a8da29e8SJustin Miller BatteryStatus->Voltage = BatteryData->BatteryStatus.Voltage;
1081*a8da29e8SJustin Miller }
1082*a8da29e8SJustin Miller }
1083*a8da29e8SJustin Miller
1084*a8da29e8SJustin Miller /* Add up the battery discharge rate if it is not unknown */
1085*a8da29e8SJustin Miller if (BatteryData->BatteryStatus.Rate != BATTERY_UNKNOWN_RATE)
1086*a8da29e8SJustin Miller {
1087*a8da29e8SJustin Miller if (BatteryStatus->Rate != BATTERY_UNKNOWN_RATE)
1088*a8da29e8SJustin Miller {
1089*a8da29e8SJustin Miller BatteryStatus->Rate += BatteryData->BatteryStatus.Rate;
1090*a8da29e8SJustin Miller }
1091*a8da29e8SJustin Miller else
1092*a8da29e8SJustin Miller {
1093*a8da29e8SJustin Miller BatteryStatus->Rate = BatteryData->BatteryStatus.Rate;
1094*a8da29e8SJustin Miller }
1095*a8da29e8SJustin Miller }
1096*a8da29e8SJustin Miller }
1097*a8da29e8SJustin Miller
1098*a8da29e8SJustin Miller /* We are done combining data from this battery */
1099*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1100*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1101*a8da29e8SJustin Miller }
1102*a8da29e8SJustin Miller
1103*a8da29e8SJustin Miller /* Release the lock as we are no longer poking through the batteries list */
1104*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1105*a8da29e8SJustin Miller
1106*a8da29e8SJustin Miller /* Ensure the composite battery did not incur in drastic changes of tag */
1107*a8da29e8SJustin Miller if (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED) ||
1108*a8da29e8SJustin Miller (DeviceExtension->Tag != Tag))
1109*a8da29e8SJustin Miller {
1110*a8da29e8SJustin Miller /*
1111*a8da29e8SJustin Miller * Either the last battery was removed (in this case the composite is no
1112*a8da29e8SJustin Miller * longer existing) or a battery was removed of which the whole battery
1113*a8da29e8SJustin Miller * information must be recomputed and such.
1114*a8da29e8SJustin Miller */
1115*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
1116*a8da29e8SJustin Miller DbgPrint("CompBatt: Last battery or a battery was removed, the whole composite data must be recomputed\n");
1117*a8da29e8SJustin Miller
1118*a8da29e8SJustin Miller return STATUS_NO_SUCH_DEVICE;
1119*a8da29e8SJustin Miller }
1120*a8da29e8SJustin Miller
1121*a8da29e8SJustin Miller /*
1122*a8da29e8SJustin Miller * If there is a battery that is charging while another one discharging,
1123*a8da29e8SJustin Miller * then tell the caller the composite battery is actually discharging.
1124*a8da29e8SJustin Miller * This is less likely to happen on a multi-battery system like laptops
1125*a8da29e8SJustin Miller * as the charger would provide electricity to all the batteries.
1126*a8da29e8SJustin Miller * Perhaps the most likely case scenario would be if the system were
1127*a8da29e8SJustin Miller * to be powered by a UPS.
1128*a8da29e8SJustin Miller */
1129*a8da29e8SJustin Miller if ((BatteryStatus->PowerState & BATTERY_CHARGING) &&
1130*a8da29e8SJustin Miller (BatteryStatus->PowerState & BATTERY_DISCHARGING))
1131*a8da29e8SJustin Miller {
1132*a8da29e8SJustin Miller BatteryStatus->PowerState &= ~BATTERY_CHARGING;
1133*a8da29e8SJustin Miller }
1134*a8da29e8SJustin Miller
1135*a8da29e8SJustin Miller /* Copy the combined status information to the composite battery */
1136*a8da29e8SJustin Miller if (NT_SUCCESS(Status))
1137*a8da29e8SJustin Miller {
1138*a8da29e8SJustin Miller RtlCopyMemory(&DeviceExtension->BatteryStatus,
1139*a8da29e8SJustin Miller BatteryStatus,
1140*a8da29e8SJustin Miller sizeof(DeviceExtension->BatteryStatus));
1141*a8da29e8SJustin Miller
1142*a8da29e8SJustin Miller /* Update the last read battery status timestamp as well */
1143*a8da29e8SJustin Miller DeviceExtension->InterruptTime = CurrentReadTime;
1144*a8da29e8SJustin Miller }
1145*a8da29e8SJustin Miller
1146*a8da29e8SJustin Miller return Status;
1147*a8da29e8SJustin Miller }
1148*a8da29e8SJustin Miller
1149*a8da29e8SJustin Miller NTSTATUS
1150*a8da29e8SJustin Miller NTAPI
CompBattGetBatteryInformation(_Out_ PBATTERY_INFORMATION BatteryInfo,_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)1151*a8da29e8SJustin Miller CompBattGetBatteryInformation(
1152*a8da29e8SJustin Miller _Out_ PBATTERY_INFORMATION BatteryInfo,
1153*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
1154*a8da29e8SJustin Miller {
1155*a8da29e8SJustin Miller NTSTATUS Status = STATUS_SUCCESS;
1156*a8da29e8SJustin Miller BATTERY_QUERY_INFORMATION InputBuffer;
1157*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
1158*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
1159*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING GetBatteryInformation\n");
1160*a8da29e8SJustin Miller
1161*a8da29e8SJustin Miller /* Set defaults */
1162*a8da29e8SJustin Miller BatteryInfo->DefaultAlert1 = 0;
1163*a8da29e8SJustin Miller BatteryInfo->DefaultAlert2 = 0;
1164*a8da29e8SJustin Miller BatteryInfo->CriticalBias = 0;
1165*a8da29e8SJustin Miller
1166*a8da29e8SJustin Miller /* Loop the battery list */
1167*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1168*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
1169*a8da29e8SJustin Miller NextEntry = ListHead->Flink;
1170*a8da29e8SJustin Miller while (NextEntry != ListHead)
1171*a8da29e8SJustin Miller {
1172*a8da29e8SJustin Miller /* Try to acquire the remove lock */
1173*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
1174*a8da29e8SJustin Miller if (NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp)))
1175*a8da29e8SJustin Miller {
1176*a8da29e8SJustin Miller /* Now release the device lock since the battery can't go away */
1177*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1178*a8da29e8SJustin Miller
1179*a8da29e8SJustin Miller /* Build the query */
1180*a8da29e8SJustin Miller InputBuffer.BatteryTag = BatteryData->Tag;
1181*a8da29e8SJustin Miller InputBuffer.InformationLevel = BatteryInformation;
1182*a8da29e8SJustin Miller InputBuffer.AtRate = 0;
1183*a8da29e8SJustin Miller
1184*a8da29e8SJustin Miller /* Make sure the battery has a tag */
1185*a8da29e8SJustin Miller if (BatteryData->Tag != BATTERY_TAG_INVALID)
1186*a8da29e8SJustin Miller {
1187*a8da29e8SJustin Miller /* Do we already have the data? */
1188*a8da29e8SJustin Miller if (!(BatteryData->Flags & COMPBATT_BATTERY_INFORMATION_PRESENT))
1189*a8da29e8SJustin Miller {
1190*a8da29e8SJustin Miller /* Send the IOCTL to query the information */
1191*a8da29e8SJustin Miller RtlZeroMemory(&BatteryData->BatteryInformation,
1192*a8da29e8SJustin Miller sizeof(BatteryData->BatteryInformation));
1193*a8da29e8SJustin Miller Status = BatteryIoctl(IOCTL_BATTERY_QUERY_INFORMATION,
1194*a8da29e8SJustin Miller BatteryData->DeviceObject,
1195*a8da29e8SJustin Miller &InputBuffer,
1196*a8da29e8SJustin Miller sizeof(InputBuffer),
1197*a8da29e8SJustin Miller &BatteryData->BatteryInformation,
1198*a8da29e8SJustin Miller sizeof(BatteryData->BatteryInformation),
1199*a8da29e8SJustin Miller FALSE);
1200*a8da29e8SJustin Miller if (!NT_SUCCESS(Status))
1201*a8da29e8SJustin Miller {
1202*a8da29e8SJustin Miller /* Fail if the query had a problem */
1203*a8da29e8SJustin Miller if (Status == STATUS_DEVICE_REMOVED) Status = STATUS_NO_SUCH_DEVICE;
1204*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1205*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1206*a8da29e8SJustin Miller break;
1207*a8da29e8SJustin Miller }
1208*a8da29e8SJustin Miller
1209*a8da29e8SJustin Miller /* Next time we can use the static copy */
1210*a8da29e8SJustin Miller BatteryData->Flags |= COMPBATT_BATTERY_INFORMATION_PRESENT;
1211*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_INFO)
1212*a8da29e8SJustin Miller DbgPrint("CompBattGetBatteryInformation: Read individual BATTERY_INFORMATION\n"
1213*a8da29e8SJustin Miller "-------- Capabilities = %x\n-------- Technology = %x\n"
1214*a8da29e8SJustin Miller "-------- Chemistry[4] = %x\n-------- DesignedCapacity = %x\n"
1215*a8da29e8SJustin Miller "-------- FullChargedCapacity = %x\n-------- DefaultAlert1 = %x\n"
1216*a8da29e8SJustin Miller "-------- DefaultAlert2 = %x\n-------- CriticalBias = %x\n"
1217*a8da29e8SJustin Miller "-------- CycleCount = %x\n",
1218*a8da29e8SJustin Miller BatteryData->BatteryInformation.Capabilities,
1219*a8da29e8SJustin Miller BatteryData->BatteryInformation.Technology,
1220*a8da29e8SJustin Miller BatteryData->BatteryInformation.Chemistry,
1221*a8da29e8SJustin Miller BatteryData->BatteryInformation.DesignedCapacity,
1222*a8da29e8SJustin Miller BatteryData->BatteryInformation.FullChargedCapacity,
1223*a8da29e8SJustin Miller BatteryData->BatteryInformation.DefaultAlert1,
1224*a8da29e8SJustin Miller BatteryData->BatteryInformation.DefaultAlert2,
1225*a8da29e8SJustin Miller BatteryData->BatteryInformation.CriticalBias,
1226*a8da29e8SJustin Miller BatteryData->BatteryInformation.CycleCount);
1227*a8da29e8SJustin Miller }
1228*a8da29e8SJustin Miller
1229*a8da29e8SJustin Miller /* Combine capabilities */
1230*a8da29e8SJustin Miller BatteryInfo->Capabilities |= BatteryData->BatteryInformation.Capabilities;
1231*a8da29e8SJustin Miller
1232*a8da29e8SJustin Miller /* Add-on capacity */
1233*a8da29e8SJustin Miller if (BatteryData->BatteryInformation.DesignedCapacity != BATTERY_UNKNOWN_CAPACITY)
1234*a8da29e8SJustin Miller {
1235*a8da29e8SJustin Miller BatteryInfo->DesignedCapacity += BatteryData->BatteryInformation.DesignedCapacity;
1236*a8da29e8SJustin Miller }
1237*a8da29e8SJustin Miller
1238*a8da29e8SJustin Miller /* Add on fully charged capacity */
1239*a8da29e8SJustin Miller if (BatteryData->BatteryInformation.FullChargedCapacity != BATTERY_UNKNOWN_CAPACITY)
1240*a8da29e8SJustin Miller {
1241*a8da29e8SJustin Miller BatteryInfo->FullChargedCapacity += BatteryData->BatteryInformation.FullChargedCapacity;
1242*a8da29e8SJustin Miller }
1243*a8da29e8SJustin Miller
1244*a8da29e8SJustin Miller /* Choose the highest alert */
1245*a8da29e8SJustin Miller BatteryInfo->DefaultAlert1 = max(BatteryInfo->DefaultAlert1,
1246*a8da29e8SJustin Miller BatteryData->BatteryInformation.DefaultAlert1);
1247*a8da29e8SJustin Miller
1248*a8da29e8SJustin Miller /* Choose the highest alert */
1249*a8da29e8SJustin Miller BatteryInfo->DefaultAlert2 = max(BatteryInfo->DefaultAlert2,
1250*a8da29e8SJustin Miller BatteryData->BatteryInformation.DefaultAlert2);
1251*a8da29e8SJustin Miller
1252*a8da29e8SJustin Miller /* Choose the highest critical bias */
1253*a8da29e8SJustin Miller BatteryInfo->CriticalBias = max(BatteryInfo->CriticalBias,
1254*a8da29e8SJustin Miller BatteryData->BatteryInformation.CriticalBias);
1255*a8da29e8SJustin Miller }
1256*a8da29e8SJustin Miller
1257*a8da29e8SJustin Miller /* Re-acquire the device extension lock and release the remove lock */
1258*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1259*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1260*a8da29e8SJustin Miller }
1261*a8da29e8SJustin Miller
1262*a8da29e8SJustin Miller /* Next entry */
1263*a8da29e8SJustin Miller NextEntry = NextEntry->Flink;
1264*a8da29e8SJustin Miller }
1265*a8da29e8SJustin Miller
1266*a8da29e8SJustin Miller /* We are done with the list, check if the information was queried okay */
1267*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1268*a8da29e8SJustin Miller if (NT_SUCCESS(Status))
1269*a8da29e8SJustin Miller {
1270*a8da29e8SJustin Miller /* If there's no fully charged capacity, use the design capacity */
1271*a8da29e8SJustin Miller if (!BatteryInfo->FullChargedCapacity)
1272*a8da29e8SJustin Miller {
1273*a8da29e8SJustin Miller BatteryInfo->FullChargedCapacity = BatteryInfo->DesignedCapacity;
1274*a8da29e8SJustin Miller }
1275*a8da29e8SJustin Miller
1276*a8da29e8SJustin Miller /* Print out final combined data */
1277*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_INFO)
1278*a8da29e8SJustin Miller DbgPrint("CompBattGetBatteryInformation: Returning BATTERY_INFORMATION\n"
1279*a8da29e8SJustin Miller "-------- Capabilities = %x\n-------- Technology = %x\n"
1280*a8da29e8SJustin Miller "-------- Chemistry[4] = %x\n-------- DesignedCapacity = %x\n"
1281*a8da29e8SJustin Miller "-------- FullChargedCapacity = %x\n-------- DefaultAlert1 = %x\n"
1282*a8da29e8SJustin Miller "-------- DefaultAlert2 = %x\n-------- CriticalBias = %x\n"
1283*a8da29e8SJustin Miller "-------- CycleCount = %x\n",
1284*a8da29e8SJustin Miller BatteryInfo->Capabilities,
1285*a8da29e8SJustin Miller BatteryInfo->Technology,
1286*a8da29e8SJustin Miller BatteryInfo->Chemistry,
1287*a8da29e8SJustin Miller BatteryInfo->DesignedCapacity,
1288*a8da29e8SJustin Miller BatteryInfo->FullChargedCapacity,
1289*a8da29e8SJustin Miller BatteryInfo->DefaultAlert1,
1290*a8da29e8SJustin Miller BatteryInfo->DefaultAlert2,
1291*a8da29e8SJustin Miller BatteryInfo->CriticalBias,
1292*a8da29e8SJustin Miller BatteryInfo->CycleCount);
1293*a8da29e8SJustin Miller
1294*a8da29e8SJustin Miller /* Copy the data into the device extension */
1295*a8da29e8SJustin Miller RtlCopyMemory(&DeviceExtension->BatteryInformation,
1296*a8da29e8SJustin Miller BatteryInfo,
1297*a8da29e8SJustin Miller sizeof(DeviceExtension->BatteryInformation));
1298*a8da29e8SJustin Miller DeviceExtension->Flags |= COMPBATT_BATTERY_INFORMATION_PRESENT;
1299*a8da29e8SJustin Miller }
1300*a8da29e8SJustin Miller
1301*a8da29e8SJustin Miller /* We are done */
1302*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: EXITING GetBatteryInformation\n");
1303*a8da29e8SJustin Miller return Status;
1304*a8da29e8SJustin Miller }
1305*a8da29e8SJustin Miller
1306*a8da29e8SJustin Miller NTSTATUS
1307*a8da29e8SJustin Miller NTAPI
CompBattGetBatteryGranularity(_Out_ PBATTERY_REPORTING_SCALE ReportingScale,_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)1308*a8da29e8SJustin Miller CompBattGetBatteryGranularity(
1309*a8da29e8SJustin Miller _Out_ PBATTERY_REPORTING_SCALE ReportingScale,
1310*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
1311*a8da29e8SJustin Miller {
1312*a8da29e8SJustin Miller NTSTATUS Status = STATUS_SUCCESS;
1313*a8da29e8SJustin Miller BATTERY_QUERY_INFORMATION InputBuffer;
1314*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
1315*a8da29e8SJustin Miller BATTERY_REPORTING_SCALE BatteryScale[4];
1316*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
1317*a8da29e8SJustin Miller ULONG i;
1318*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING GetBatteryGranularity\n");
1319*a8da29e8SJustin Miller
1320*a8da29e8SJustin Miller /* Set defaults */
1321*a8da29e8SJustin Miller ReportingScale[0].Granularity = -1;
1322*a8da29e8SJustin Miller ReportingScale[1].Granularity = -1;
1323*a8da29e8SJustin Miller ReportingScale[2].Granularity = -1;
1324*a8da29e8SJustin Miller ReportingScale[3].Granularity = -1;
1325*a8da29e8SJustin Miller
1326*a8da29e8SJustin Miller /* Loop the battery list */
1327*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1328*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
1329*a8da29e8SJustin Miller NextEntry = ListHead->Flink;
1330*a8da29e8SJustin Miller while (NextEntry != ListHead)
1331*a8da29e8SJustin Miller {
1332*a8da29e8SJustin Miller /* Try to acquire the remove lock */
1333*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
1334*a8da29e8SJustin Miller if (NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp)))
1335*a8da29e8SJustin Miller {
1336*a8da29e8SJustin Miller /* Now release the device lock since the battery can't go away */
1337*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1338*a8da29e8SJustin Miller
1339*a8da29e8SJustin Miller /* Build the query */
1340*a8da29e8SJustin Miller InputBuffer.BatteryTag = BatteryData->Tag;
1341*a8da29e8SJustin Miller InputBuffer.InformationLevel = BatteryGranularityInformation;
1342*a8da29e8SJustin Miller
1343*a8da29e8SJustin Miller /* Make sure the battery has a tag */
1344*a8da29e8SJustin Miller if (BatteryData->Tag != BATTERY_TAG_INVALID)
1345*a8da29e8SJustin Miller {
1346*a8da29e8SJustin Miller /* Send the IOCTL to query the information */
1347*a8da29e8SJustin Miller RtlZeroMemory(&BatteryData->BatteryInformation,
1348*a8da29e8SJustin Miller sizeof(BatteryData->BatteryInformation));
1349*a8da29e8SJustin Miller Status = BatteryIoctl(IOCTL_BATTERY_QUERY_INFORMATION,
1350*a8da29e8SJustin Miller BatteryData->DeviceObject,
1351*a8da29e8SJustin Miller &InputBuffer,
1352*a8da29e8SJustin Miller sizeof(InputBuffer),
1353*a8da29e8SJustin Miller &BatteryScale,
1354*a8da29e8SJustin Miller sizeof(BatteryScale),
1355*a8da29e8SJustin Miller FALSE);
1356*a8da29e8SJustin Miller if (!NT_SUCCESS(Status))
1357*a8da29e8SJustin Miller {
1358*a8da29e8SJustin Miller /* Fail if the query had a problem */
1359*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1360*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1361*a8da29e8SJustin Miller break;
1362*a8da29e8SJustin Miller }
1363*a8da29e8SJustin Miller
1364*a8da29e8SJustin Miller /* Loop all 4 scales */
1365*a8da29e8SJustin Miller for (i = 0; i < 4; i++)
1366*a8da29e8SJustin Miller {
1367*a8da29e8SJustin Miller /* Check for valid granularity */
1368*a8da29e8SJustin Miller if (BatteryScale[i].Granularity)
1369*a8da29e8SJustin Miller {
1370*a8da29e8SJustin Miller /* If it's smaller, use it instead */
1371*a8da29e8SJustin Miller ReportingScale[i].Granularity = min(BatteryScale[i].Granularity,
1372*a8da29e8SJustin Miller ReportingScale[i].Granularity);
1373*a8da29e8SJustin Miller }
1374*a8da29e8SJustin Miller
1375*a8da29e8SJustin Miller }
1376*a8da29e8SJustin Miller }
1377*a8da29e8SJustin Miller
1378*a8da29e8SJustin Miller /* Re-acquire the device extension lock and release the remove lock */
1379*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1380*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1381*a8da29e8SJustin Miller }
1382*a8da29e8SJustin Miller
1383*a8da29e8SJustin Miller /* Next entry */
1384*a8da29e8SJustin Miller NextEntry = NextEntry->Flink;
1385*a8da29e8SJustin Miller }
1386*a8da29e8SJustin Miller
1387*a8da29e8SJustin Miller /* All done */
1388*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1389*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: EXITING GetBatteryGranularity\n");
1390*a8da29e8SJustin Miller return STATUS_SUCCESS;
1391*a8da29e8SJustin Miller }
1392*a8da29e8SJustin Miller
1393*a8da29e8SJustin Miller /**
1394*a8da29e8SJustin Miller * @brief
1395*a8da29e8SJustin Miller * Calculates the "At Rate" flow of the composite battery based on the
1396*a8da29e8SJustin Miller * sum of all connected batteries, in order to retrieve the precise
1397*a8da29e8SJustin Miller * battery time estimation.
1398*a8da29e8SJustin Miller *
1399*a8da29e8SJustin Miller * @param[in] DeviceExtension
1400*a8da29e8SJustin Miller * A pointer to a device extension which describes the composite battery
1401*a8da29e8SJustin Miller * itself. It is used to gather each connected battery in the list with
1402*a8da29e8SJustin Miller * the composite battery.
1403*a8da29e8SJustin Miller *
1404*a8da29e8SJustin Miller * @return
1405*a8da29e8SJustin Miller * Returns the computed "At Rate" flow to the caller.
1406*a8da29e8SJustin Miller */
1407*a8da29e8SJustin Miller static
1408*a8da29e8SJustin Miller LONG
CompBattCalculateAtRateTime(_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)1409*a8da29e8SJustin Miller CompBattCalculateAtRateTime(
1410*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
1411*a8da29e8SJustin Miller {
1412*a8da29e8SJustin Miller NTSTATUS Status;
1413*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
1414*a8da29e8SJustin Miller BATTERY_QUERY_INFORMATION QueryInformation;
1415*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
1416*a8da29e8SJustin Miller ULONG Time;
1417*a8da29e8SJustin Miller LONG ComputedAtRate = 0;
1418*a8da29e8SJustin Miller
1419*a8da29e8SJustin Miller /* Walk over the linked batteries list to poll for "At Rate" value of each battery */
1420*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1421*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
1422*a8da29e8SJustin Miller for (NextEntry = ListHead->Flink;
1423*a8da29e8SJustin Miller NextEntry != ListHead;
1424*a8da29e8SJustin Miller NextEntry = NextEntry->Flink)
1425*a8da29e8SJustin Miller {
1426*a8da29e8SJustin Miller /* Acquire the remove lock so this battery does not disappear under us */
1427*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
1428*a8da29e8SJustin Miller if (!NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp)))
1429*a8da29e8SJustin Miller continue;
1430*a8da29e8SJustin Miller
1431*a8da29e8SJustin Miller /* Now release the device lock since the battery can't go away */
1432*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1433*a8da29e8SJustin Miller
1434*a8da29e8SJustin Miller /* Build the necessary information in order to query the battery estimated time */
1435*a8da29e8SJustin Miller QueryInformation.BatteryTag = BatteryData->Tag;
1436*a8da29e8SJustin Miller QueryInformation.InformationLevel = BatteryEstimatedTime;
1437*a8da29e8SJustin Miller QueryInformation.AtRate = 0;
1438*a8da29e8SJustin Miller
1439*a8da29e8SJustin Miller /* Make sure this battery has a valid tag before issuing the IOCTL */
1440*a8da29e8SJustin Miller if (BatteryData->Tag != BATTERY_TAG_INVALID)
1441*a8da29e8SJustin Miller {
1442*a8da29e8SJustin Miller /*
1443*a8da29e8SJustin Miller * Now it is time to issue the IOCTL to the battery device.
1444*a8da29e8SJustin Miller * We are calculating the "At Rate" counter based on each linked
1445*a8da29e8SJustin Miller * battery that is discharging, one at a time. This ensures
1446*a8da29e8SJustin Miller * that when we will actually retrieve the estimation time of each
1447*a8da29e8SJustin Miller * individual battery and sum it all up as one time for the composite
1448*a8da29e8SJustin Miller * battery, that the estimated time is accurate enough.
1449*a8da29e8SJustin Miller */
1450*a8da29e8SJustin Miller Status = BatteryIoctl(IOCTL_BATTERY_QUERY_INFORMATION,
1451*a8da29e8SJustin Miller BatteryData->DeviceObject,
1452*a8da29e8SJustin Miller &QueryInformation,
1453*a8da29e8SJustin Miller sizeof(QueryInformation),
1454*a8da29e8SJustin Miller &Time,
1455*a8da29e8SJustin Miller sizeof(Time),
1456*a8da29e8SJustin Miller FALSE);
1457*a8da29e8SJustin Miller if (NT_SUCCESS(Status))
1458*a8da29e8SJustin Miller {
1459*a8da29e8SJustin Miller if ((Time != 0) && (Time != BATTERY_UNKNOWN_TIME))
1460*a8da29e8SJustin Miller {
1461*a8da29e8SJustin Miller ComputedAtRate -= COMPUTE_ATRATE_DRAIN(BatteryData, Time);
1462*a8da29e8SJustin Miller }
1463*a8da29e8SJustin Miller }
1464*a8da29e8SJustin Miller }
1465*a8da29e8SJustin Miller
1466*a8da29e8SJustin Miller /* We are done with this battery */
1467*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1468*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1469*a8da29e8SJustin Miller }
1470*a8da29e8SJustin Miller
1471*a8da29e8SJustin Miller /* Release the lock as we are no longer poking through the batteries list */
1472*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1473*a8da29e8SJustin Miller return ComputedAtRate;
1474*a8da29e8SJustin Miller }
1475*a8da29e8SJustin Miller
1476*a8da29e8SJustin Miller /**
1477*a8da29e8SJustin Miller * @brief
1478*a8da29e8SJustin Miller * Retrieves the estimated time of the composite battery based on the
1479*a8da29e8SJustin Miller * power drain rate of all the batteries present in the system.
1480*a8da29e8SJustin Miller *
1481*a8da29e8SJustin Miller * @param[out] Time
1482*a8da29e8SJustin Miller * A pointer to the computed estimated time of the composite battery,
1483*a8da29e8SJustin Miller * returned to caller. Note that if there are not any batteries that
1484*a8da29e8SJustin Miller * are draining power, or if the system is powered by external AC source,
1485*a8da29e8SJustin Miller * the estimated time is unknown
1486*a8da29e8SJustin Miller *
1487*a8da29e8SJustin Miller * @param[in] DeviceExtension
1488*a8da29e8SJustin Miller * A pointer to a device extension which describes the composite battery
1489*a8da29e8SJustin Miller * itself. It is used to gather each connected battery in the list with
1490*a8da29e8SJustin Miller * the composite battery.
1491*a8da29e8SJustin Miller *
1492*a8da29e8SJustin Miller * @return
1493*a8da29e8SJustin Miller * Returns STATUS_NO_SUCH_DEVICE if the supplied battery tag does not match
1494*a8da29e8SJustin Miller * with that of the cached composite battery's tag or if the composite
1495*a8da29e8SJustin Miller * battery currently does not have a tag assigned. Otherwise STATUS_SUCCESS
1496*a8da29e8SJustin Miller * is returned.
1497*a8da29e8SJustin Miller */
1498*a8da29e8SJustin Miller NTSTATUS
1499*a8da29e8SJustin Miller NTAPI
CompBattGetEstimatedTime(_Out_ PULONG Time,_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)1500*a8da29e8SJustin Miller CompBattGetEstimatedTime(
1501*a8da29e8SJustin Miller _Out_ PULONG Time,
1502*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
1503*a8da29e8SJustin Miller {
1504*a8da29e8SJustin Miller NTSTATUS Status;
1505*a8da29e8SJustin Miller PCOMPBATT_BATTERY_DATA BatteryData;
1506*a8da29e8SJustin Miller BATTERY_STATUS BatteryStatus;
1507*a8da29e8SJustin Miller BATTERY_QUERY_INFORMATION QueryInformation;
1508*a8da29e8SJustin Miller PLIST_ENTRY ListHead, NextEntry;
1509*a8da29e8SJustin Miller ULONG ReturnedTime;
1510*a8da29e8SJustin Miller LONG ComputedAtRate;
1511*a8da29e8SJustin Miller
1512*a8da29e8SJustin Miller /* Assume the battery time is not estimated yet */
1513*a8da29e8SJustin Miller *Time = BATTERY_UNKNOWN_TIME;
1514*a8da29e8SJustin Miller
1515*a8da29e8SJustin Miller /*
1516*a8da29e8SJustin Miller * Before we are querying the composite estimated battery time we must
1517*a8da29e8SJustin Miller * refresh the battery status cache if we have not done it so.
1518*a8da29e8SJustin Miller */
1519*a8da29e8SJustin Miller Status = CompBattQueryStatus(DeviceExtension,
1520*a8da29e8SJustin Miller DeviceExtension->Tag,
1521*a8da29e8SJustin Miller &BatteryStatus);
1522*a8da29e8SJustin Miller if (!NT_SUCCESS(Status))
1523*a8da29e8SJustin Miller {
1524*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_ERR)
1525*a8da29e8SJustin Miller DbgPrint("CompBatt: Failed to refresh composite battery's status (Status 0x%08lx)\n", Status);
1526*a8da29e8SJustin Miller
1527*a8da29e8SJustin Miller return Status;
1528*a8da29e8SJustin Miller }
1529*a8da29e8SJustin Miller
1530*a8da29e8SJustin Miller /* Print out battery status data that has been polled */
1531*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_INFO)
1532*a8da29e8SJustin Miller DbgPrint("CompBatt: Latest composite battery status (when querying for estimated time)\n"
1533*a8da29e8SJustin Miller " PowerState -> 0x%lx\n"
1534*a8da29e8SJustin Miller " Capacity -> %u\n"
1535*a8da29e8SJustin Miller " Voltage -> %u\n"
1536*a8da29e8SJustin Miller " Rate -> %d\n",
1537*a8da29e8SJustin Miller BatteryStatus.PowerState,
1538*a8da29e8SJustin Miller BatteryStatus.Capacity,
1539*a8da29e8SJustin Miller BatteryStatus.Voltage,
1540*a8da29e8SJustin Miller BatteryStatus.Rate);
1541*a8da29e8SJustin Miller
1542*a8da29e8SJustin Miller /*
1543*a8da29e8SJustin Miller * If the batteries are not being discharged and the system is directly
1544*a8da29e8SJustin Miller * being powered by external AC source then it makes no sense to
1545*a8da29e8SJustin Miller * compute the battery estimated time because that construct is for
1546*a8da29e8SJustin Miller * WHEN the system is powered directly from batteries and it drains power.
1547*a8da29e8SJustin Miller */
1548*a8da29e8SJustin Miller if (DeviceExtension->BatteryStatus.PowerState & BATTERY_POWER_ON_LINE)
1549*a8da29e8SJustin Miller {
1550*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
1551*a8da29e8SJustin Miller {
1552*a8da29e8SJustin Miller DbgPrint("CompBatt: The system is powered by AC source, estimated time is not available\n");
1553*a8da29e8SJustin Miller }
1554*a8da29e8SJustin Miller
1555*a8da29e8SJustin Miller return STATUS_SUCCESS;
1556*a8da29e8SJustin Miller }
1557*a8da29e8SJustin Miller
1558*a8da29e8SJustin Miller /* Determine the draining "At Rate" counter for all batteries */
1559*a8da29e8SJustin Miller ComputedAtRate = CompBattCalculateAtRateTime(DeviceExtension);
1560*a8da29e8SJustin Miller
1561*a8da29e8SJustin Miller /*
1562*a8da29e8SJustin Miller * A rate of 0 indicates none of the batteries that are linked with
1563*a8da29e8SJustin Miller * the composite are being drained therefore we cannot estimate the
1564*a8da29e8SJustin Miller * run time of the composite as it is not discharging.
1565*a8da29e8SJustin Miller */
1566*a8da29e8SJustin Miller if (ComputedAtRate == 0)
1567*a8da29e8SJustin Miller {
1568*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_WARN)
1569*a8da29e8SJustin Miller DbgPrint("CompBatt: No battery is discharging and no power is being drained, cannot estimate the run time\n");
1570*a8da29e8SJustin Miller
1571*a8da29e8SJustin Miller return STATUS_SUCCESS;
1572*a8da29e8SJustin Miller }
1573*a8da29e8SJustin Miller
1574*a8da29e8SJustin Miller /* Walk over the linked batteries list and determine the exact estimated time */
1575*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1576*a8da29e8SJustin Miller ListHead = &DeviceExtension->BatteryList;
1577*a8da29e8SJustin Miller for (NextEntry = ListHead->Flink;
1578*a8da29e8SJustin Miller NextEntry != ListHead;
1579*a8da29e8SJustin Miller NextEntry = NextEntry->Flink)
1580*a8da29e8SJustin Miller {
1581*a8da29e8SJustin Miller /* Acquire the remove lock so this battery does not disappear under us */
1582*a8da29e8SJustin Miller BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
1583*a8da29e8SJustin Miller if (!NT_SUCCESS(IoAcquireRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp)))
1584*a8da29e8SJustin Miller continue;
1585*a8da29e8SJustin Miller
1586*a8da29e8SJustin Miller /* Now release the device lock since the battery can't go away */
1587*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1588*a8da29e8SJustin Miller
1589*a8da29e8SJustin Miller /* Build the necessary information in order to query the battery estimated time */
1590*a8da29e8SJustin Miller QueryInformation.BatteryTag = BatteryData->Tag;
1591*a8da29e8SJustin Miller QueryInformation.InformationLevel = BatteryEstimatedTime;
1592*a8da29e8SJustin Miller QueryInformation.AtRate = ComputedAtRate;
1593*a8da29e8SJustin Miller
1594*a8da29e8SJustin Miller /* Make sure this battery has a valid tag before issuing the IOCTL */
1595*a8da29e8SJustin Miller if (BatteryData->Tag != BATTERY_TAG_INVALID)
1596*a8da29e8SJustin Miller {
1597*a8da29e8SJustin Miller Status = BatteryIoctl(IOCTL_BATTERY_QUERY_INFORMATION,
1598*a8da29e8SJustin Miller BatteryData->DeviceObject,
1599*a8da29e8SJustin Miller &QueryInformation,
1600*a8da29e8SJustin Miller sizeof(QueryInformation),
1601*a8da29e8SJustin Miller &ReturnedTime,
1602*a8da29e8SJustin Miller sizeof(ReturnedTime),
1603*a8da29e8SJustin Miller FALSE);
1604*a8da29e8SJustin Miller if (!NT_SUCCESS(Status))
1605*a8da29e8SJustin Miller {
1606*a8da29e8SJustin Miller /*
1607*a8da29e8SJustin Miller * If the device is being suddenly removed then we must invalidate
1608*a8da29e8SJustin Miller * both this battery and composite tags.
1609*a8da29e8SJustin Miller */
1610*a8da29e8SJustin Miller if (Status == STATUS_DEVICE_REMOVED)
1611*a8da29e8SJustin Miller {
1612*a8da29e8SJustin Miller Status = STATUS_NO_SUCH_DEVICE;
1613*a8da29e8SJustin Miller }
1614*a8da29e8SJustin Miller
1615*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1616*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1617*a8da29e8SJustin Miller
1618*a8da29e8SJustin Miller /*
1619*a8da29e8SJustin Miller * In other places we are ceasing the execution of the loop but
1620*a8da29e8SJustin Miller * here we want to continue looking for other linked batteries.
1621*a8da29e8SJustin Miller * This is because we are querying for the estimated battery time
1622*a8da29e8SJustin Miller * at the time the last battery status was valid. Also bear in
1623*a8da29e8SJustin Miller * mind IOCTL_BATTERY_QUERY_INFORMATION with InformationLevel as
1624*a8da29e8SJustin Miller * BatteryEstimatedTime might not be a valid request supported
1625*a8da29e8SJustin Miller * by this battery.
1626*a8da29e8SJustin Miller */
1627*a8da29e8SJustin Miller continue;
1628*a8da29e8SJustin Miller }
1629*a8da29e8SJustin Miller
1630*a8da29e8SJustin Miller /* Now sum up the estimated battery time */
1631*a8da29e8SJustin Miller if (ReturnedTime != BATTERY_UNKNOWN_TIME)
1632*a8da29e8SJustin Miller {
1633*a8da29e8SJustin Miller if (*Time != BATTERY_UNKNOWN_TIME)
1634*a8da29e8SJustin Miller {
1635*a8da29e8SJustin Miller *Time += ReturnedTime;
1636*a8da29e8SJustin Miller }
1637*a8da29e8SJustin Miller else
1638*a8da29e8SJustin Miller {
1639*a8da29e8SJustin Miller *Time = ReturnedTime;
1640*a8da29e8SJustin Miller }
1641*a8da29e8SJustin Miller }
1642*a8da29e8SJustin Miller }
1643*a8da29e8SJustin Miller
1644*a8da29e8SJustin Miller /* We are done with this battery */
1645*a8da29e8SJustin Miller ExAcquireFastMutex(&DeviceExtension->Lock);
1646*a8da29e8SJustin Miller IoReleaseRemoveLock(&BatteryData->RemoveLock, BatteryData->Irp);
1647*a8da29e8SJustin Miller }
1648*a8da29e8SJustin Miller
1649*a8da29e8SJustin Miller /* Release the lock as we are no longer poking through the batteries list */
1650*a8da29e8SJustin Miller ExReleaseFastMutex(&DeviceExtension->Lock);
1651*a8da29e8SJustin Miller return Status;
1652*a8da29e8SJustin Miller }
1653*a8da29e8SJustin Miller
1654*a8da29e8SJustin Miller NTSTATUS
1655*a8da29e8SJustin Miller NTAPI
CompBattQueryInformation(_In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,_In_ ULONG Tag,_In_ BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,_In_opt_ LONG AtRate,_In_ PVOID Buffer,_In_ ULONG BufferLength,_Out_ PULONG ReturnedLength)1656*a8da29e8SJustin Miller CompBattQueryInformation(
1657*a8da29e8SJustin Miller _In_ PCOMPBATT_DEVICE_EXTENSION DeviceExtension,
1658*a8da29e8SJustin Miller _In_ ULONG Tag,
1659*a8da29e8SJustin Miller _In_ BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
1660*a8da29e8SJustin Miller _In_opt_ LONG AtRate,
1661*a8da29e8SJustin Miller _In_ PVOID Buffer,
1662*a8da29e8SJustin Miller _In_ ULONG BufferLength,
1663*a8da29e8SJustin Miller _Out_ PULONG ReturnedLength)
1664*a8da29e8SJustin Miller {
1665*a8da29e8SJustin Miller BATTERY_INFORMATION BatteryInfo;
1666*a8da29e8SJustin Miller BATTERY_REPORTING_SCALE BatteryGranularity[4];
1667*a8da29e8SJustin Miller PWCHAR BatteryName = L"Composite Battery";
1668*a8da29e8SJustin Miller //BATTERY_MANUFACTURE_DATE Date;
1669*a8da29e8SJustin Miller ULONG Dummy, Time;
1670*a8da29e8SJustin Miller PVOID QueryData = NULL;
1671*a8da29e8SJustin Miller ULONG QueryLength = 0;
1672*a8da29e8SJustin Miller NTSTATUS Status = STATUS_SUCCESS;
1673*a8da29e8SJustin Miller PAGED_CODE();
1674*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: ENTERING QueryInformation\n");
1675*a8da29e8SJustin Miller
1676*a8da29e8SJustin Miller /* Check for valid/correct tag */
1677*a8da29e8SJustin Miller if ((Tag != DeviceExtension->Tag) ||
1678*a8da29e8SJustin Miller (!(DeviceExtension->Flags & COMPBATT_TAG_ASSIGNED)))
1679*a8da29e8SJustin Miller {
1680*a8da29e8SJustin Miller /* Not right, so fail */
1681*a8da29e8SJustin Miller return STATUS_NO_SUCH_DEVICE;
1682*a8da29e8SJustin Miller }
1683*a8da29e8SJustin Miller
1684*a8da29e8SJustin Miller /* Check what caller wants */
1685*a8da29e8SJustin Miller switch (InfoLevel)
1686*a8da29e8SJustin Miller {
1687*a8da29e8SJustin Miller case BatteryInformation:
1688*a8da29e8SJustin Miller
1689*a8da29e8SJustin Miller /* Query combined battery information */
1690*a8da29e8SJustin Miller RtlZeroMemory(&BatteryInfo, sizeof(BatteryInfo));
1691*a8da29e8SJustin Miller Status = CompBattGetBatteryInformation(&BatteryInfo, DeviceExtension);
1692*a8da29e8SJustin Miller if (NT_SUCCESS(Status))
1693*a8da29e8SJustin Miller {
1694*a8da29e8SJustin Miller /* Return the data if successful */
1695*a8da29e8SJustin Miller QueryData = &BatteryInfo;
1696*a8da29e8SJustin Miller QueryLength = sizeof(BatteryInfo);
1697*a8da29e8SJustin Miller }
1698*a8da29e8SJustin Miller break;
1699*a8da29e8SJustin Miller
1700*a8da29e8SJustin Miller case BatteryGranularityInformation:
1701*a8da29e8SJustin Miller
1702*a8da29e8SJustin Miller /* Query combined granularity information */
1703*a8da29e8SJustin Miller RtlZeroMemory(&BatteryGranularity, sizeof(BatteryGranularity));
1704*a8da29e8SJustin Miller Status = CompBattGetBatteryGranularity(BatteryGranularity, DeviceExtension);
1705*a8da29e8SJustin Miller if (NT_SUCCESS(Status))
1706*a8da29e8SJustin Miller {
1707*a8da29e8SJustin Miller /* Return the data if successful */
1708*a8da29e8SJustin Miller QueryLength = sizeof(BatteryGranularity);
1709*a8da29e8SJustin Miller QueryData = &BatteryGranularity;
1710*a8da29e8SJustin Miller }
1711*a8da29e8SJustin Miller break;
1712*a8da29e8SJustin Miller
1713*a8da29e8SJustin Miller case BatteryEstimatedTime:
1714*a8da29e8SJustin Miller
1715*a8da29e8SJustin Miller /* Query combined time estimate information */
1716*a8da29e8SJustin Miller RtlZeroMemory(&Time, sizeof(Time));
1717*a8da29e8SJustin Miller Status = CompBattGetEstimatedTime(&Time, DeviceExtension);
1718*a8da29e8SJustin Miller if (NT_SUCCESS(Status))
1719*a8da29e8SJustin Miller {
1720*a8da29e8SJustin Miller /* Return the data if successful */
1721*a8da29e8SJustin Miller QueryLength = sizeof(Time);
1722*a8da29e8SJustin Miller QueryData = &Time;
1723*a8da29e8SJustin Miller }
1724*a8da29e8SJustin Miller break;
1725*a8da29e8SJustin Miller
1726*a8da29e8SJustin Miller case BatteryManufactureName:
1727*a8da29e8SJustin Miller case BatteryDeviceName:
1728*a8da29e8SJustin Miller
1729*a8da29e8SJustin Miller /* Return the static buffer */
1730*a8da29e8SJustin Miller QueryData = BatteryName;
1731*a8da29e8SJustin Miller QueryLength = sizeof(L"Composite Battery");
1732*a8da29e8SJustin Miller break;
1733*a8da29e8SJustin Miller
1734*a8da29e8SJustin Miller case BatteryManufactureDate:
1735*a8da29e8SJustin Miller
1736*a8da29e8SJustin Miller /* Static data */
1737*a8da29e8SJustin Miller //Date.Day = 26;
1738*a8da29e8SJustin Miller //Date.Month = 06;
1739*a8da29e8SJustin Miller //Date.Year = 1997;
1740*a8da29e8SJustin Miller break;
1741*a8da29e8SJustin Miller
1742*a8da29e8SJustin Miller case BatteryTemperature:
1743*a8da29e8SJustin Miller case BatteryUniqueID:
1744*a8da29e8SJustin Miller
1745*a8da29e8SJustin Miller /* Return zero */
1746*a8da29e8SJustin Miller Dummy = 0;
1747*a8da29e8SJustin Miller QueryData = &Dummy;
1748*a8da29e8SJustin Miller QueryLength = sizeof(Dummy);
1749*a8da29e8SJustin Miller break;
1750*a8da29e8SJustin Miller
1751*a8da29e8SJustin Miller default:
1752*a8da29e8SJustin Miller /* Everything else is unknown */
1753*a8da29e8SJustin Miller Status = STATUS_INVALID_PARAMETER;
1754*a8da29e8SJustin Miller break;
1755*a8da29e8SJustin Miller }
1756*a8da29e8SJustin Miller
1757*a8da29e8SJustin Miller /* Return the required length and check if the caller supplied enough */
1758*a8da29e8SJustin Miller *ReturnedLength = QueryLength;
1759*a8da29e8SJustin Miller if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
1760*a8da29e8SJustin Miller
1761*a8da29e8SJustin Miller /* Copy the data if there's enough space and it exists */
1762*a8da29e8SJustin Miller if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
1763*a8da29e8SJustin Miller
1764*a8da29e8SJustin Miller /* Return function result */
1765*a8da29e8SJustin Miller if (CompBattDebug & COMPBATT_DEBUG_TRACE) DbgPrint("CompBatt: EXITING QueryInformation\n");
1766*a8da29e8SJustin Miller return Status;
1767*a8da29e8SJustin Miller }
1768*a8da29e8SJustin Miller
1769*a8da29e8SJustin Miller NTSTATUS
1770*a8da29e8SJustin Miller NTAPI
DriverEntry(_In_ PDRIVER_OBJECT DriverObject,_In_ PUNICODE_STRING RegistryPath)1771*a8da29e8SJustin Miller DriverEntry(
1772*a8da29e8SJustin Miller _In_ PDRIVER_OBJECT DriverObject,
1773*a8da29e8SJustin Miller _In_ PUNICODE_STRING RegistryPath)
1774*a8da29e8SJustin Miller {
1775*a8da29e8SJustin Miller /* Register add device routine */
1776*a8da29e8SJustin Miller DriverObject->DriverExtension->AddDevice = CompBattAddDevice;
1777*a8da29e8SJustin Miller
1778*a8da29e8SJustin Miller /* Register other handlers */
1779*a8da29e8SJustin Miller DriverObject->MajorFunction[IRP_MJ_CREATE] = CompBattOpenClose;
1780*a8da29e8SJustin Miller DriverObject->MajorFunction[IRP_MJ_CLOSE] = CompBattOpenClose;
1781*a8da29e8SJustin Miller DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CompBattIoctl;
1782*a8da29e8SJustin Miller DriverObject->MajorFunction[IRP_MJ_POWER] = CompBattPowerDispatch;
1783*a8da29e8SJustin Miller DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CompBattSystemControl;
1784*a8da29e8SJustin Miller DriverObject->MajorFunction[IRP_MJ_PNP] = CompBattPnpDispatch;
1785*a8da29e8SJustin Miller return STATUS_SUCCESS;
1786*a8da29e8SJustin Miller }
1787*a8da29e8SJustin Miller
1788*a8da29e8SJustin Miller /* EOF */
1789