xref: /reactos/drivers/bus/acpi/cmbatt/cmbatt.c (revision a97fcf19)
1 /*
2  * PROJECT:         ReactOS ACPI-Compliant Control Method Battery
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            boot/drivers/bus/acpi/cmbatt/cmbatt.c
5  * PURPOSE:         Main Initialization Code and IRP Handling
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "cmbatt.h"
12 
13 #include <debug.h>
14 
15 /* GLOBALS ********************************************************************/
16 
17 ULONG CmBattDebug;
18 PCALLBACK_OBJECT CmBattPowerCallBackObject;
19 PVOID CmBattPowerCallBackRegistration;
20 UNICODE_STRING GlobalRegistryPath;
21 KTIMER CmBattWakeDpcTimerObject;
22 KDPC CmBattWakeDpcObject;
23 PDEVICE_OBJECT AcAdapterPdo;
24 LARGE_INTEGER CmBattWakeDpcDelay;
25 
26 /* FUNCTIONS ******************************************************************/
27 
28 VOID
29 NTAPI
CmBattPowerCallBack(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG Action,IN ULONG Value)30 CmBattPowerCallBack(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
31                     IN ULONG Action,
32                     IN ULONG Value)
33 {
34     BOOLEAN Cancelled;
35     PDEVICE_OBJECT DeviceObject;
36     if (CmBattDebug & 0x10)
37         DbgPrint("CmBattPowerCallBack: action: %d, value: %d \n", Action, Value);
38 
39     /* Check if a transition is going to happen */
40     if (Action == PO_CB_SYSTEM_STATE_LOCK)
41     {
42         /* We have just re-entered S0: call the wake DPC in 10 seconds */
43         if (Value == 1)
44         {
45             if (CmBattDebug & 0x10)
46                 DbgPrint("CmBattPowerCallBack: Calling CmBattWakeDpc after 10 seconds.\n");
47             Cancelled = KeSetTimer(&CmBattWakeDpcTimerObject, CmBattWakeDpcDelay, &CmBattWakeDpcObject);
48             if (CmBattDebug & 0x10)
49                 DbgPrint("CmBattPowerCallBack: timerCanceled = %d.\n", Cancelled);
50         }
51         else if (Value == 0)
52         {
53             /* We are exiting the S0 state: loop all devices to set the delay flag */
54             if (CmBattDebug & 0x10)
55                 DbgPrint("CmBattPowerCallBack: Delaying Notifications\n");
56             for (DeviceObject = DeviceExtension->DeviceObject;
57                  DeviceObject;
58                  DeviceObject = DeviceObject->NextDevice)
59             {
60                 /* Set the delay flag */
61                 DeviceExtension = DeviceObject->DeviceExtension;
62                 DeviceExtension->DelayNotification = TRUE;
63             }
64         }
65         else if (CmBattDebug & 0x10)
66         {
67             /* Unknown value */
68             DbgPrint("CmBattPowerCallBack: unknown argument2 = %08x\n", Value);
69         }
70     }
71 }
72 
73 VOID
74 NTAPI
CmBattWakeDpc(IN PKDPC Dpc,IN PCMBATT_DEVICE_EXTENSION FdoExtension,IN PVOID SystemArgument1,IN PVOID SystemArgument2)75 CmBattWakeDpc(IN PKDPC Dpc,
76               IN PCMBATT_DEVICE_EXTENSION FdoExtension,
77               IN PVOID SystemArgument1,
78               IN PVOID SystemArgument2)
79 {
80     PDEVICE_OBJECT CurrentObject;
81     BOOLEAN AcNotify = FALSE;
82     PCMBATT_DEVICE_EXTENSION DeviceExtension;
83     ULONG ArFlag;
84     if (CmBattDebug & 2) DbgPrint("CmBattWakeDpc: Entered.\n");
85 
86     /* Loop all device objects */
87     for (CurrentObject = FdoExtension->DeviceObject;
88          CurrentObject;
89          CurrentObject = CurrentObject->NextDevice)
90     {
91         /* Turn delay flag off, we're back in S0 */
92         DeviceExtension = CurrentObject->DeviceExtension;
93         DeviceExtension->DelayNotification = 0;
94 
95         /* Check if this is an AC adapter */
96         if (DeviceExtension->FdoType == CmBattAcAdapter)
97         {
98             /* Was there a pending notify? */
99             if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
100             {
101                 /* We'll send a notify on the next pass */
102                 AcNotify = TRUE;
103                 DeviceExtension->ArFlag = 0;
104                 if (CmBattDebug & 0x20)
105                     DbgPrint("CmBattWakeDpc: AC adapter notified\n");
106             }
107         }
108     }
109 
110     /* Loop the device objects again */
111     for (CurrentObject = FdoExtension->DeviceObject;
112          CurrentObject;
113          CurrentObject = CurrentObject->NextDevice)
114     {
115         /* Check if this is a battery */
116         DeviceExtension = CurrentObject->DeviceExtension;
117         if (DeviceExtension->FdoType == CmBattBattery)
118         {
119             /* Check what ARs are pending */
120             ArFlag = DeviceExtension->ArFlag;
121             if (CmBattDebug & 0x20)
122                 DbgPrint("CmBattWakeDpc: Performing delayed ARs: %01x\n", ArFlag);
123 
124             /* Insert notification, clear the lock value */
125             if (ArFlag & CMBATT_AR_INSERT) InterlockedExchange(&DeviceExtension->ArLockValue, 0);
126 
127             /* Removal, clear the battery tag */
128             if (ArFlag & CMBATT_AR_REMOVE) DeviceExtension->Tag = 0;
129 
130             /* Notification (or AC/DC adapter change from first pass above) */
131             if ((ArFlag & CMBATT_AR_NOTIFY) || (AcNotify))
132             {
133                 /* Notify the class driver */
134                 BatteryClassStatusNotify(DeviceExtension->ClassData);
135             }
136         }
137     }
138 }
139 
140 VOID
141 NTAPI
CmBattNotifyHandler(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG NotifyValue)142 CmBattNotifyHandler(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
143                     IN ULONG NotifyValue)
144 {
145     ULONG ArFlag;
146     PCMBATT_DEVICE_EXTENSION FdoExtension;
147     PDEVICE_OBJECT DeviceObject;
148 
149     if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_PNP_INFO))
150         DbgPrint("CmBattNotifyHandler: CmBatt 0x%08x Type %d Number %d Notify Value: %x\n",
151                  DeviceExtension,
152                  DeviceExtension->FdoType,
153                  DeviceExtension->DeviceId,
154                  NotifyValue);
155 
156     /* Check what kind of notification was received */
157     switch (NotifyValue)
158     {
159         /* ACPI Specification says is sends a "Bus Check" when power source changes */
160         case ACPI_BUS_CHECK:
161 
162             /* We treat it as possible physical change */
163             DeviceExtension->ArFlag |= (CMBATT_AR_NOTIFY | CMBATT_AR_INSERT);
164             if ((DeviceExtension->Tag) &&
165                 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
166                 DbgPrint("CmBattNotifyHandler: Received battery #%x insertion, but tag was not invalid.\n",
167                          DeviceExtension->DeviceId);
168             break;
169 
170         /* Status of the battery has changed */
171         case ACPI_BATT_NOTIFY_STATUS:
172 
173             /* All we'll do is notify the class driver */
174             DeviceExtension->ArFlag |= CMBATT_AR_NOTIFY;
175             break;
176 
177         /* Information on the battery has changed, such as physical presence */
178         case ACPI_DEVICE_CHECK:
179         case ACPI_BATT_NOTIFY_INFO:
180 
181             /* Reset all state and let the class driver re-evaluate it all */
182             DeviceExtension->ArFlag |= (CMBATT_AR_NOTIFY |
183                                         CMBATT_AR_INSERT |
184                                         CMBATT_AR_REMOVE);
185             break;
186 
187         default:
188 
189             if (CmBattDebug & CMBATT_PNP_INFO)
190                 DbgPrint("CmBattNotifyHandler: Unknown Notify Value: %x\n", NotifyValue);
191     }
192 
193     /* Check if we're supposed to delay the notification till later */
194     if (DeviceExtension->DelayNotification)
195     {
196         /* We'll handle this when we get a status query later on */
197         if (CmBattDebug & CMBATT_PNP_INFO)
198             DbgPrint("CmBattNotifyHandler: Notification delayed: ARs = %01x\n",
199                       DeviceExtension->ArFlag);
200         return;
201     }
202 
203     /* We're going to handle this now */
204     if (CmBattDebug & CMBATT_PNP_INFO)
205         DbgPrint("CmBattNotifyHandler: Performing ARs: %01x\n", DeviceExtension->ArFlag);
206 
207     /* Check if this is a battery or AC adapter notification */
208     if (DeviceExtension->FdoType == CmBattBattery)
209     {
210         /* Reset the current trip point */
211         DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
212 
213         /* Check what ARs have to be done */
214         ArFlag = DeviceExtension->ArFlag;
215 
216         /* New battery inserted, reset lock value */
217         if (ArFlag & CMBATT_AR_INSERT) InterlockedExchange(&DeviceExtension->ArLockValue, 0);
218 
219         /* Check if the battery may have been removed */
220         if (ArFlag & CMBATT_AR_REMOVE) DeviceExtension->Tag = 0;
221 
222         /* Check if there's been any sort of change to the battery */
223         if (ArFlag & CMBATT_AR_NOTIFY)
224         {
225             /* We'll probably end up re-evaluating _BIF and _BST */
226             DeviceExtension->NotifySent = TRUE;
227             BatteryClassStatusNotify(DeviceExtension->ClassData);
228         }
229     }
230     else if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
231     {
232         /* The only known notification is AC/DC change. Loop device objects. */
233         for (DeviceObject = DeviceExtension->FdoDeviceObject->DriverObject->DeviceObject;
234              DeviceObject;
235              DeviceObject = DeviceObject->NextDevice)
236         {
237             /* Is this a battery? */
238             FdoExtension = DeviceObject->DeviceExtension;
239             if (FdoExtension->FdoType == CmBattBattery)
240             {
241                 /* Send a notification to the class driver */
242                 FdoExtension->NotifySent = TRUE;
243                 BatteryClassStatusNotify(FdoExtension->ClassData);
244             }
245         }
246     }
247 
248     /* ARs have been processed */
249     DeviceExtension->ArFlag = 0;
250 }
251 
252 VOID
253 NTAPI
CmBattUnload(IN PDRIVER_OBJECT DriverObject)254 CmBattUnload(IN PDRIVER_OBJECT DriverObject)
255 {
256     if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattUnload: \n");
257 
258     /* Check if we have a registered power callback */
259     if (CmBattPowerCallBackObject)
260     {
261         /* Get rid of it */
262         ExUnregisterCallback(CmBattPowerCallBackRegistration);
263         ObDereferenceObject(CmBattPowerCallBackObject);
264     }
265 
266     /* Free the registry buffer if it exists */
267     if (GlobalRegistryPath.Buffer) ExFreePool(GlobalRegistryPath.Buffer);
268 
269     /* Make sure we don't still have references to the DO */
270     if ((DriverObject->DeviceObject) && (CmBattDebug & CMBATT_GENERIC_WARNING))
271     {
272         DbgPrint("Unload called before all devices removed.\n");
273     }
274 }
275 
276 /**
277  * @brief
278  * Retrieves the static information of the battery.
279  *
280  * @param[in] DeviceExtension
281  * A pointer to a Control Method (CM) battery device extension.
282  * It is used for debugging purposes.
283  *
284  * @param[out] UseBix
285  * A pointer to a boolean value, returned to caller. This can return
286  * TRUE if this machine supports the _BIX method, FALSE otherwise.
287  *
288  * @param[out] BattInfo
289  * A pointer to a structure that contains the static info of the
290  * battery. ONLY ONE type of information is filled. See Remarks.
291  *
292  * @return
293  * Returns STATUS_INSUFFICIENT_RESOURCES if there is no enough
294  * memory to allocate for the static info buffer. Returns
295  * STATUS_SUCCESS if the operation has succeeded. Otherwise an
296  * error NTSTATUS code is returned.
297  *
298  * @remarks
299  * It is important to note that a machine can only support one method,
300  * _BIX or _BIF. Starting with ACPI 4.0, _BIF has become deprecated.
301  * The caller MUST INSPECT the boolean value, ExtendedData, in order
302  * to determine if the machine has returned the extended information
303  * data or not.
304  */
305 static
306 NTSTATUS
CmBattGetBattStaticInfo(_In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,_Out_ PBOOLEAN UseBix,_Outptr_ PACPI_BATT_STATIC_INFO * BattInfo)307 CmBattGetBattStaticInfo(
308     _In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
309     _Out_ PBOOLEAN UseBix,
310     _Outptr_ PACPI_BATT_STATIC_INFO *BattInfo)
311 {
312     NTSTATUS Status;
313     ACPI_BIF_DATA BifData;
314     ACPI_BIX_DATA BixData;
315     PACPI_BATT_STATIC_INFO Info;
316 
317     /* Allocate pool space for the static information */
318     Info = ExAllocatePoolZero(PagedPool,
319                               sizeof(*Info),
320                               CMBATT_BATT_STATIC_INFO_TAG);
321     if (Info == NULL)
322     {
323         return STATUS_INSUFFICIENT_RESOURCES;
324     }
325 
326     /* Assume this machine supports the _BIX method */
327     *UseBix = TRUE;
328 
329     /* Retrieve extended battery static info from _BIX method */
330     Status = CmBattGetBixData(DeviceExtension, &BixData);
331     if (!NT_SUCCESS(Status))
332     {
333         /*
334          * It failed. This can be expected because not every machine supports
335          * _BIX, especially the older machines which do not support ACPI 4.0.
336          * Fallback to _BIF at this point.
337          */
338         Status = CmBattGetBifData(DeviceExtension, &BifData);
339         if (!NT_SUCCESS(Status))
340         {
341             /* That failed too, time to punt */
342             ExFreePoolWithTag(Info, CMBATT_BATT_STATIC_INFO_TAG);
343             return Status;
344         }
345 
346         /* Acknowledge the caller it will be going to use _BIF */
347         *UseBix = FALSE;
348         Info->BifData = BifData;
349     }
350     else
351     {
352         Info->BixData = BixData;
353     }
354 
355     /* Return the battery static info to caller */
356     Info->ExtendedData = *UseBix;
357     *BattInfo = Info;
358     return Status;
359 }
360 
361 /**
362  * @brief
363  * Verifies the extended battery information (_BIX) and translates
364  * such data to the BATTERY_INFORMATION structure.
365  *
366  * @param[in] DeviceExtension
367  * A pointer to a Control Method (CM) battery device extension.
368  * It is used to gather _BIX data.
369  *
370  * @param[in,out] Info
371  * A pointer to a structure of which this function fills in
372  * battery information that can be by other battery miniport
373  * drivers, such as the Composite Battery driver.
374  */
375 static
376 VOID
CmBattVerifyBixData(_In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,_Inout_ PBATTERY_INFORMATION Info)377 CmBattVerifyBixData(
378     _In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
379     _Inout_ PBATTERY_INFORMATION Info)
380 {
381     ULONG DesignVoltage;
382     ACPI_BIX_DATA BixData = DeviceExtension->BattInfo.BixData;
383 
384     /* Copy the battery info data */
385     Info->Technology = BixData.BatteryTechnology;
386     Info->CycleCount = BixData.CycleCount;
387     RtlCopyMemory(Info->Chemistry, BixData.BatteryType, 4);
388 
389     /* Check if the power stats are reported in ampere or watts */
390     if (BixData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
391     {
392         /*
393          * We have got power stats in milli-ampere but ReactOS expects the values
394          * to be reported in milli-watts, so we have to convert them.
395          * In order to do so we must expect the design voltage of the battery
396          * is not unknown.
397          */
398         DesignVoltage = BixData.DesignVoltage;
399         if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage != 0))
400         {
401             /* Convert the design capacity */
402             Info->DesignedCapacity = CONVERT_BATT_INFO(BixData.DesignCapacity, DesignVoltage);
403 
404             /* Convert the full charged capacity */
405             Info->FullChargedCapacity = CONVERT_BATT_INFO(BixData.LastFullCapacity, DesignVoltage);
406 
407             /* Convert the low capacity alarm (DefaultAlert1) */
408             Info->DefaultAlert1 = CONVERT_BATT_INFO(BixData.DesignCapacityLow, DesignVoltage);
409 
410             /* Convert the designed capacity warning alarm (DefaultAlert2) */
411             Info->DefaultAlert2 = CONVERT_BATT_INFO(BixData.DesignCapacityWarning, DesignVoltage);
412         }
413         else
414         {
415             /*
416              * Without knowing the nominal designed voltage of the battery
417              * we cannot determine the power consumption of this battery.
418              */
419             Info->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
420             Info->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
421             Info->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
422             Info->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
423         }
424     }
425     else
426     {
427         /* The stats are in milli-watts, use them directly */
428         Info->DesignedCapacity = BixData.DesignCapacity;
429         Info->FullChargedCapacity = BixData.LastFullCapacity;
430         Info->DefaultAlert1 = BixData.DesignCapacityLow;
431         Info->DefaultAlert2 = BixData.DesignCapacityWarning;
432     }
433 }
434 
435 /**
436  * @brief
437  * Verifies the battery information (_BIF) and translates
438  * such data to the BATTERY_INFORMATION structure.
439  *
440  * @param[in] DeviceExtension
441  * A pointer to a Control Method (CM) battery device extension.
442  * It is used to gather _BIF data.
443  *
444  * @param[in,out] Info
445  * A pointer to a structure of which this function fills in
446  * battery information that can be by other battery miniport
447  * drivers, such as the Composite Battery driver.
448  */
449 static
450 VOID
CmBattVerifyBifData(_In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,_Inout_ PBATTERY_INFORMATION Info)451 CmBattVerifyBifData(
452     _In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
453     _Inout_ PBATTERY_INFORMATION Info)
454 {
455     ULONG DesignVoltage;
456     ACPI_BIF_DATA BifData = DeviceExtension->BattInfo.BifData;
457 
458     /* Copy the battery info data, CycleCount is not supported in _BIF */
459     Info->Technology = BifData.BatteryTechnology;
460     Info->CycleCount = 0;
461     RtlCopyMemory(Info->Chemistry, BifData.BatteryType, 4);
462 
463     /* Check if the power stats are reported in ampere or watts */
464     if (BifData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
465     {
466         /*
467          * We have got power stats in milli-ampere but ReactOS expects the values
468          * to be reported in milli-watts, so we have to convert them.
469          * In order to do so we must expect the design voltage of the battery
470          * is not unknown.
471          */
472         DesignVoltage = BifData.DesignVoltage;
473         if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage != 0))
474         {
475             /* Convert the design capacity */
476             Info->DesignedCapacity = CONVERT_BATT_INFO(BifData.DesignCapacity, DesignVoltage);
477 
478             /* Convert the full charged capacity */
479             Info->FullChargedCapacity = CONVERT_BATT_INFO(BifData.LastFullCapacity, DesignVoltage);
480 
481             /* Convert the low capacity alarm (DefaultAlert1) */
482             Info->DefaultAlert1 = CONVERT_BATT_INFO(BifData.DesignCapacityLow, DesignVoltage);
483 
484             /* Convert the designed capacity warning alarm (DefaultAlert2) */
485             Info->DefaultAlert2 = CONVERT_BATT_INFO(BifData.DesignCapacityWarning, DesignVoltage);
486         }
487         else
488         {
489             /*
490              * Without knowing the nominal designed voltage of the battery
491              * we cannot determine the power consumption of this battery.
492              */
493             Info->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
494             Info->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
495             Info->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
496             Info->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
497         }
498     }
499     else
500     {
501         /* The stats are in milli-watts, use them directly */
502         Info->DesignedCapacity = BifData.DesignCapacity;
503         Info->FullChargedCapacity = BifData.LastFullCapacity;
504         Info->DefaultAlert1 = BifData.DesignCapacityLow;
505         Info->DefaultAlert2 = BifData.DesignCapacityWarning;
506     }
507 }
508 
509 NTSTATUS
510 NTAPI
CmBattVerifyStaticInfo(_Inout_ PCMBATT_DEVICE_EXTENSION DeviceExtension,_In_ ULONG BatteryTag)511 CmBattVerifyStaticInfo(
512     _Inout_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
513     _In_ ULONG BatteryTag)
514 {
515     NTSTATUS Status;
516     BOOLEAN UseBix;
517     PACPI_BATT_STATIC_INFO BattInfo;
518     PBATTERY_INFORMATION Info = &DeviceExtension->BatteryInformation;
519 
520     /* FIXME: This function is not fully implemented, more checks need to be implemented */
521     UNREFERENCED_PARAMETER(BatteryTag);
522 
523     /* Retrieve the battery static info */
524     Status = CmBattGetBattStaticInfo(DeviceExtension, &UseBix, &BattInfo);
525     if (NT_SUCCESS(Status))
526     {
527         /* Initialize the battery information data */
528         RtlZeroMemory(Info, sizeof(*Info));
529         Info->Capabilities = BATTERY_SYSTEM_BATTERY;
530 
531         /* Copy the static information to the device extension of the battery */
532         RtlCopyMemory(&DeviceExtension->BattInfo, BattInfo, sizeof(*BattInfo));
533 
534         /* Check if the data from _BIX has to be used or not */
535         if (UseBix)
536         {
537             CmBattVerifyBixData(DeviceExtension, Info);
538         }
539         else
540         {
541             CmBattVerifyBifData(DeviceExtension, Info);
542         }
543 
544         /* Free the static information buffer as we already copied it */
545         ExFreePoolWithTag(BattInfo, CMBATT_BATT_STATIC_INFO_TAG);
546     }
547 
548     return Status;
549 }
550 
551 NTSTATUS
552 NTAPI
CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)553 CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject,
554                 IN PIRP Irp)
555 {
556     NTSTATUS Status = STATUS_SUCCESS;
557     PIO_STACK_LOCATION IoStackLocation;
558     UCHAR Major;
559     ULONG Count;
560     PCMBATT_DEVICE_EXTENSION DeviceExtension;
561     PAGED_CODE();
562     if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattOpenClose\n");
563 
564     /* Grab the device extension and lock it */
565     DeviceExtension = DeviceObject->DeviceExtension;
566     ExAcquireFastMutex(&DeviceExtension->FastMutex);
567 
568     /* Check if someone is trying to open a device that doesn't exist yet */
569     Count = DeviceExtension->HandleCount;
570     if (Count == 0xFFFFFFFF)
571     {
572         /* Fail the request */
573         Status = STATUS_NO_SUCH_DEVICE;
574         if (CmBattDebug & CMBATT_PNP_INFO)
575         {
576             DbgPrint("CmBattOpenClose: Failed (UID = %x)(device being removed).\n",
577                      DeviceExtension->Tag);
578         }
579         goto Complete;
580     }
581 
582     /* Check if this is an open or close */
583     IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
584     Major = IoStackLocation->MajorFunction;
585     if (Major == IRP_MJ_CREATE)
586     {
587         /* Increment the open count */
588         DeviceExtension->HandleCount = Count + 1;
589         if (CmBattDebug & CMBATT_PNP_INFO)
590         {
591             DbgPrint("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
592                      DeviceExtension->DeviceId, Count + 1);
593         }
594     }
595     else if (Major == IRP_MJ_CLOSE)
596     {
597         /* Decrement the open count */
598         DeviceExtension->HandleCount = Count - 1;
599         if (CmBattDebug & CMBATT_PNP_INFO)
600         {
601             DbgPrint("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
602                      DeviceExtension->DeviceId, Count + 1);
603         }
604     }
605 
606 Complete:
607     /* Release lock and complete request */
608     ExReleaseFastMutex(&DeviceExtension->FastMutex);
609     Irp->IoStatus.Status = Status;
610     IoCompleteRequest(Irp, IO_NO_INCREMENT);
611     return Status;
612 }
613 
614 NTSTATUS
615 NTAPI
CmBattIoctl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)616 CmBattIoctl(IN PDEVICE_OBJECT DeviceObject,
617             IN PIRP Irp)
618 {
619     PCMBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
620     NTSTATUS Status;
621     PIO_STACK_LOCATION IoStackLocation;
622     ULONG IoControlCode, OutputBufferLength, InputBufferLength;
623     PAGED_CODE();
624     if (CmBattDebug & 2) DbgPrint("CmBattIoctl\n");
625 
626     /* Acquire the remove lock */
627     Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, Irp);
628     if (!NT_SUCCESS(Status))
629     {
630         /* It's too late, fail */
631         Irp->IoStatus.Status = STATUS_DEVICE_REMOVED;
632         IoCompleteRequest(Irp, IO_NO_INCREMENT);
633         return STATUS_DEVICE_REMOVED;
634     }
635 
636     /* There's nothing to do for an AC adapter */
637     if (DeviceExtension->FdoType == CmBattAcAdapter)
638     {
639         /* Pass it down, and release the remove lock */
640         IoSkipCurrentIrpStackLocation(Irp);
641         Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
642         IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
643         return Status;
644     }
645 
646     /* Send to class driver */
647     Status = BatteryClassIoctl(DeviceExtension->ClassData, Irp);
648     if (Status == STATUS_NOT_SUPPORTED)
649     {
650         /* Read IOCTL information from IRP stack */
651         IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
652         IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
653         OutputBufferLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
654         InputBufferLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
655         if (CmBattDebug & 4)
656             DbgPrint("CmBattIoctl: Received  Direct Access IOCTL %x\n", IoControlCode);
657 
658         /* Handle internal IOCTLs */
659         switch (IoControlCode)
660         {
661             case IOCTL_BATTERY_QUERY_UNIQUE_ID:
662 
663                 /* Data is 4 bytes long */
664                 if (OutputBufferLength == sizeof(ULONG))
665                 {
666                     /* Query it */
667                     Status = CmBattGetUniqueId(DeviceExtension->PdoDeviceObject,
668                                                Irp->AssociatedIrp.SystemBuffer);
669                     if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
670                 }
671                 else
672                 {
673                     /* Buffer size invalid */
674                     Status = STATUS_INVALID_BUFFER_SIZE;
675                 }
676                 break;
677 
678             case IOCTL_BATTERY_QUERY_STA:
679 
680                 /* Data is 4 bytes long */
681                 if (OutputBufferLength == sizeof(ULONG))
682                 {
683                     /* Query it */
684                     Status = CmBattGetStaData(DeviceExtension->PdoDeviceObject,
685                                               Irp->AssociatedIrp.SystemBuffer);
686                     if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
687                 }
688                 else
689                 {
690                     /* Buffer size invalid */
691                     Status = STATUS_INVALID_BUFFER_SIZE;
692                 }
693                 break;
694 
695             case IOCTL_BATTERY_QUERY_PSR:
696 
697                 /* Data is 4 bytes long */
698                 if (OutputBufferLength == sizeof(ULONG))
699                 {
700                     /* Do we have an AC adapter? */
701                     if (AcAdapterPdo)
702                     {
703                         /* Query it */
704                         Status = CmBattGetPsrData(AcAdapterPdo,
705                                                   Irp->AssociatedIrp.SystemBuffer);
706                         if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
707                     }
708                     else
709                     {
710                         /* No adapter, just a battery, so fail */
711                         Status = STATUS_NO_SUCH_DEVICE;
712                     }
713                 }
714                 else
715                 {
716                     /* Buffer size invalid */
717                     Status = STATUS_INVALID_BUFFER_SIZE;
718                 }
719                 break;
720 
721             case IOCTL_BATTERY_SET_TRIP_POINT:
722 
723                 /* Data is 4 bytes long */
724                 if (InputBufferLength == sizeof(ULONG))
725                 {
726                     /* Query it */
727                     Status = CmBattSetTripPpoint(DeviceExtension,
728                                                  *(PULONG)Irp->AssociatedIrp.SystemBuffer);
729                     Irp->IoStatus.Information = 0;
730                 }
731                 else
732                 {
733                     /* Buffer size invalid */
734                     Status = STATUS_INVALID_BUFFER_SIZE;
735                 }
736                 break;
737 
738             case IOCTL_BATTERY_QUERY_BIF_BIX:
739 
740                 /* Return the battery static information to the caller depending on the supported ACPI method */
741                 if (DeviceExtension->BattInfo.ExtendedData)
742                 {
743                     if (OutputBufferLength == sizeof(ACPI_BIX_DATA))
744                     {
745                         /* Query it */
746                         Status = CmBattGetBixData(DeviceExtension,
747                                                   Irp->AssociatedIrp.SystemBuffer);
748                         if (NT_SUCCESS(Status))
749                         {
750                             Irp->IoStatus.Information = sizeof(ACPI_BIX_DATA);
751                         }
752                     }
753                     else
754                     {
755                         /* Buffer size invalid */
756                         Status = STATUS_INVALID_BUFFER_SIZE;
757                     }
758                 }
759                 else
760                 {
761                     if (OutputBufferLength == sizeof(ACPI_BIF_DATA))
762                     {
763                         /* Query it */
764                         Status = CmBattGetBifData(DeviceExtension,
765                                                   Irp->AssociatedIrp.SystemBuffer);
766                         if (NT_SUCCESS(Status))
767                         {
768                             Irp->IoStatus.Information = sizeof(ACPI_BIF_DATA);
769                         }
770                     }
771                     else
772                     {
773                         /* Buffer size invalid */
774                         Status = STATUS_INVALID_BUFFER_SIZE;
775                     }
776                 }
777                 break;
778 
779             case IOCTL_BATTERY_QUERY_BST:
780 
781                 /* Data is 16 bytes long */
782                 if (OutputBufferLength == sizeof(ACPI_BST_DATA))
783                 {
784                     /* Query it */
785                     Status = CmBattGetBstData(DeviceExtension,
786                                               Irp->AssociatedIrp.SystemBuffer);
787                     if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ACPI_BST_DATA);
788                 }
789                 else
790                 {
791                     /* Buffer size invalid */
792                     Status = STATUS_INVALID_BUFFER_SIZE;
793                 }
794                 break;
795 
796             default:
797 
798                 /* Unknown, let us pass it on to ACPI */
799                 if (CmBattDebug & 0xC)
800                     DbgPrint("CmBattIoctl: Unknown IOCTL %x\n", IoControlCode);
801                 break;
802         }
803 
804         /* Did someone pick it up? */
805         if (Status != STATUS_NOT_SUPPORTED)
806         {
807             /* Complete the request */
808             Irp->IoStatus.Status = Status;
809             IoCompleteRequest(Irp, IO_NO_INCREMENT);
810         }
811         else
812         {
813             /* Still unsupported, try ACPI */
814             IoSkipCurrentIrpStackLocation(Irp);
815             Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
816         }
817     }
818 
819     /* Release the remove lock and return status */
820     IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
821     return Status;
822 }
823 
824 NTSTATUS
825 NTAPI
CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,OUT PULONG Tag)826 CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
827                OUT PULONG Tag)
828 {
829     PDEVICE_OBJECT PdoDevice;
830     ULONG StaData;
831     NTSTATUS Status;
832     PAGED_CODE();
833     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
834       DbgPrint("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
835                 *Tag, DeviceExtension, DeviceExtension->DeviceId);
836 
837     /* Get PDO and clear notification flag */
838     PdoDevice = DeviceExtension->PdoDeviceObject;
839     DeviceExtension->NotifySent = 0;
840 
841     /* Get _STA from PDO (we need the machine status, not the battery status) */
842     Status = CmBattGetStaData(PdoDevice, &StaData);
843     if (NT_SUCCESS(Status))
844     {
845         /* Is a battery present? */
846         if (StaData & ACPI_STA_BATTERY_PRESENT)
847         {
848             /* Do we not have a tag yet? */
849             if (DeviceExtension->Tag == BATTERY_TAG_INVALID)
850             {
851                 /* Set the new tag value, reset tags if we reached the maximum */
852                 if (++DeviceExtension->TagData == BATTERY_TAG_INVALID)
853                     DeviceExtension->TagData = 1;
854                 DeviceExtension->Tag = DeviceExtension->TagData;
855                 if (CmBattDebug & CMBATT_GENERIC_INFO)
856                     DbgPrint("CmBattQueryTag - New Tag: (%d)\n", DeviceExtension->Tag);
857 
858                 /* Reset trip point data */
859                 DeviceExtension->TripPointOld = 0;
860                 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
861 
862                 /* Clear AR lock and set new interrupt time */
863                 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
864                 DeviceExtension->InterruptTime = KeQueryInterruptTime();
865             }
866         }
867         else
868         {
869             /* No battery, so no tag */
870             DeviceExtension->Tag = BATTERY_TAG_INVALID;
871             Status = STATUS_NO_SUCH_DEVICE;
872         }
873     }
874 
875     /* Return the tag and status result */
876     *Tag = DeviceExtension->Tag;
877     if (CmBattDebug & CMBATT_ACPI_WARNING)
878       DbgPrint("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n", *Tag, Status);
879     return Status;
880 }
881 
882 NTSTATUS
883 NTAPI
CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)884 CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)
885 {
886     NTSTATUS Status;
887     PAGED_CODE();
888     if (CmBattDebug & 0xA) DbgPrint("CmBattDisableStatusNotify\n");
889 
890     /* Do we have a trip point */
891     if (DeviceExtension->TripPointSet)
892     {
893         /* Is there a current value set? */
894         if (DeviceExtension->TripPointValue)
895         {
896             /* Reset it back to 0 */
897             DeviceExtension->TripPointValue = 0;
898             Status = CmBattSetTripPpoint(DeviceExtension, 0);
899             if (!NT_SUCCESS(Status))
900             {
901                 /* If it failed, set unknown/invalid value */
902                 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
903                 if (CmBattDebug & 8)
904                     DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status);
905             }
906         }
907         else
908         {
909             /* No trip point set, so this is a successful no-op */
910             Status = STATUS_SUCCESS;
911         }
912     }
913     else
914     {
915         /* Nothing we can do */
916         Status = STATUS_OBJECT_NAME_NOT_FOUND;
917     }
918 
919     /* Return status */
920     return Status;
921 }
922 
923 NTSTATUS
924 NTAPI
CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG BatteryTag,IN PBATTERY_NOTIFY BatteryNotify)925 CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
926                       IN ULONG BatteryTag,
927                       IN PBATTERY_NOTIFY BatteryNotify)
928 {
929     NTSTATUS Status;
930     ACPI_BST_DATA BstData;
931     ULONG PowerUnit, Capacity, NewTripPoint, TripPoint, DesignVoltage;
932     BOOLEAN Charging;
933     PAGED_CODE();
934     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
935         DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
936                  BatteryTag, BatteryNotify->LowCapacity);
937 
938     /* Update any ACPI evaluations */
939     Status = CmBattVerifyStaticInfo(DeviceExtension, BatteryTag);
940     if (!NT_SUCCESS(Status)) return Status;
941 
942     /* Trip point not supported, fail */
943     if (!DeviceExtension->TripPointSet) return STATUS_OBJECT_NAME_NOT_FOUND;
944 
945     /* Are both capacities known? */
946     if ((BatteryNotify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
947         (BatteryNotify->LowCapacity == BATTERY_UNKNOWN_CAPACITY))
948     {
949         /* We can't set trip points without these */
950         if (CmBattDebug & CMBATT_GENERIC_WARNING)
951             DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
952         return STATUS_NOT_SUPPORTED;
953     }
954 
955     /* Is the battery charging? */
956     Charging = DeviceExtension->BstData.State & ACPI_BATT_STAT_CHARGING;
957     if (Charging)
958     {
959         /* Then the trip point is when we hit the cap */
960         Capacity = BatteryNotify->HighCapacity;
961         NewTripPoint = BatteryNotify->HighCapacity;
962     }
963     else
964     {
965         /* Otherwise it's when we discharge to the bottom */
966         Capacity = BatteryNotify->LowCapacity;
967         NewTripPoint = BatteryNotify->LowCapacity;
968     }
969 
970     /* Is this machine supporting _BIX or _BIF? */
971     if (DeviceExtension->BattInfo.ExtendedData)
972     {
973         PowerUnit = DeviceExtension->BattInfo.BixData.PowerUnit;
974         DesignVoltage = DeviceExtension->BattInfo.BixData.DesignVoltage;
975     }
976     else
977     {
978         PowerUnit = DeviceExtension->BattInfo.BifData.PowerUnit;
979         DesignVoltage = DeviceExtension->BattInfo.BifData.DesignVoltage;
980     }
981 
982     /* Do we have data in Amps or Watts? */
983     if (PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
984     {
985         /* We need the voltage to do the conversion */
986         if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage))
987         {
988             /* Convert from mAh into Ah */
989             TripPoint = 1000 * NewTripPoint;
990             if (Charging)
991             {
992                 /* Scale the high trip point */
993                 NewTripPoint = (TripPoint + 500) / DesignVoltage + ((TripPoint + 500) % DesignVoltage != 0);
994             }
995             else
996             {
997                 /* Scale the low trip point */
998                 NewTripPoint = (TripPoint - 500) / DesignVoltage - ((TripPoint - 500) % DesignVoltage == 0);
999             }
1000         }
1001         else
1002         {
1003             /* Without knowing the voltage, Amps are not enough data on consumption */
1004             Status = STATUS_NOT_SUPPORTED;
1005             if (CmBattDebug & CMBATT_ACPI_WARNING)
1006                 DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
1007                         DesignVoltage);
1008         }
1009     }
1010     else if (Charging)
1011     {
1012         /* Make it trip just one past the charge cap */
1013         ++NewTripPoint;
1014     }
1015     else if (NewTripPoint > 0)
1016     {
1017         /* Make it trip just one below the drain cap */
1018         --NewTripPoint;
1019     }
1020 
1021     /* Do we actually have a new trip point? */
1022     if (NewTripPoint == DeviceExtension->TripPointValue)
1023     {
1024         /* No, so there is no work to be done */
1025         if (CmBattDebug & CMBATT_GENERIC_STATUS)
1026             DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension->TripPointValue);
1027         return STATUS_SUCCESS;
1028     }
1029 
1030     /* Set the trip point with ACPI and check for success */
1031     DeviceExtension->TripPointValue = NewTripPoint;
1032     Status = CmBattSetTripPpoint(DeviceExtension, NewTripPoint);
1033     if (!(NewTripPoint) && (Capacity)) Status = STATUS_NOT_SUPPORTED;
1034     if (!NT_SUCCESS(Status))
1035     {
1036         /* We failed to set the trip point, or there wasn't one settable */
1037         DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
1038         if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
1039             DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status);
1040         return Status;
1041     }
1042 
1043     /* Read the new BST data to see the latest state */
1044     Status = CmBattGetBstData(DeviceExtension, &BstData);
1045     if (!NT_SUCCESS(Status))
1046     {
1047         /* We'll return failure to the caller */
1048         if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
1049             DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status);
1050     }
1051     else if ((Charging) && (BstData.RemainingCapacity >= NewTripPoint))
1052     {
1053         /* We are charging and our capacity is past the trip point, so trip now */
1054         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1055             DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
1056                      NewTripPoint, BstData.RemainingCapacity);
1057         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
1058     }
1059     else if ((BstData.RemainingCapacity) && (Capacity))
1060     {
1061         /* We are discharging, and our capacity is below the trip point, trip now */
1062         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1063             DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
1064                      NewTripPoint, BstData.RemainingCapacity);
1065         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
1066     }
1067 
1068     /* All should've went well if we got here, unless BST failed... return! */
1069     if (CmBattDebug & CMBATT_GENERIC_STATUS)
1070           DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
1071                     Capacity, DeviceExtension->RemainingCapacity);
1072     if (CmBattDebug & CMBATT_ACPI_WARNING)
1073           DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %lx\n",
1074                     BatteryNotify->PowerState,
1075                     BatteryNotify->LowCapacity,
1076                     BatteryNotify->HighCapacity,
1077                     Status);
1078     return Status;
1079 }
1080 
1081 NTSTATUS
1082 NTAPI
CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG Tag)1083 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
1084                        IN ULONG Tag)
1085 {
1086     ULONG PsrData = 0;
1087     NTSTATUS Status;
1088     BOOLEAN WasDischarging;
1089     ULONG BstState;
1090     ULONG PowerUnit;
1091     ULONG DesignVoltage, PresentRate, RemainingCapacity;
1092     PAGED_CODE();
1093     if (CmBattDebug & CMBATT_GENERIC_INFO)
1094         DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension, Tag);
1095 
1096     /* Validate ACPI data */
1097     Status = CmBattVerifyStaticInfo(DeviceExtension, Tag);
1098     if (!NT_SUCCESS(Status)) return Status;
1099 
1100     /* Check for delayed status notifications */
1101     if (DeviceExtension->DelayNotification)
1102     {
1103         /* Process them now and don't do any other work */
1104         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
1105         return Status;
1106     }
1107 
1108     /* Get _BST from ACPI */
1109     Status = CmBattGetBstData(DeviceExtension, &DeviceExtension->BstData);
1110     if (!NT_SUCCESS(Status))
1111     {
1112         /* Fail */
1113         InterlockedExchange(&DeviceExtension->ArLockValue, 0);
1114         return Status;
1115     }
1116 
1117     /* Remember if the battery was discharging at the time of querying new status */
1118     WasDischarging = !!(DeviceExtension->State & BATTERY_DISCHARGING);
1119 
1120     /* Clear current BST information */
1121     DeviceExtension->State = 0;
1122     DeviceExtension->RemainingCapacity = 0;
1123     DeviceExtension->PresentVoltage = 0;
1124     DeviceExtension->Rate = 0;
1125 
1126     /* Get battery state */
1127     BstState = DeviceExtension->BstData.State;
1128 
1129     /* Is the battery both charging and discharging? */
1130     if ((BstState & ACPI_BATT_STAT_DISCHARG) && (BstState & ACPI_BATT_STAT_CHARGING) &&
1131         (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
1132             DbgPrint("************************ ACPI BIOS BUG ********************\n* "
1133                      "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
1134                      "* One battery cannot be charging and discharging at the same time.\n",
1135                      BstState);
1136 
1137     /* Is the battery discharging? */
1138     if (BstState & ACPI_BATT_STAT_DISCHARG)
1139     {
1140         /* Set power state and check if it just started discharging now */
1141         DeviceExtension->State |= BATTERY_DISCHARGING;
1142         if (!WasDischarging)
1143         {
1144             /* The battery is discharging now and not before, remember the time when the state changed */
1145             DeviceExtension->InterruptTime = KeQueryInterruptTime();
1146         }
1147     }
1148     else if (BstState & ACPI_BATT_STAT_CHARGING)
1149     {
1150         /* Battery is charging, update power state */
1151         DeviceExtension->State |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
1152     }
1153 
1154     /* Is the battery in a critical state? */
1155     if (BstState & ACPI_BATT_STAT_CRITICAL) DeviceExtension->State |= BATTERY_CRITICAL;
1156 
1157     /* Read the voltage data */
1158     DeviceExtension->PresentVoltage = DeviceExtension->BstData.PresentVoltage;
1159 
1160     /* Check if we have an A/C adapter */
1161     if (AcAdapterPdo)
1162     {
1163         /* Query information on it */
1164         CmBattGetPsrData(AcAdapterPdo, &PsrData);
1165     }
1166     else
1167     {
1168         /* Otherwise, check if the battery is charging */
1169         if (BstState & ACPI_BATT_STAT_CHARGING)
1170         {
1171             /* Then we'll assume there's a charger */
1172             PsrData = 1;
1173         }
1174         else
1175         {
1176             /* Assume no charger */
1177             PsrData = 0;
1178         }
1179     }
1180 
1181     /* Is there a charger? */
1182     if (PsrData)
1183     {
1184         /* Set the power state flag to reflect this */
1185         DeviceExtension->State |= BATTERY_POWER_ON_LINE;
1186         if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
1187             DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
1188     }
1189     else if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
1190     {
1191         DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
1192     }
1193 
1194     /* Is this machine supporting _BIX or _BIF? */
1195     if (DeviceExtension->BattInfo.ExtendedData)
1196     {
1197         PowerUnit = DeviceExtension->BattInfo.BixData.PowerUnit;
1198         DesignVoltage = DeviceExtension->BattInfo.BixData.DesignVoltage;
1199     }
1200     else
1201     {
1202         PowerUnit = DeviceExtension->BattInfo.BifData.PowerUnit;
1203         DesignVoltage = DeviceExtension->BattInfo.BifData.DesignVoltage;
1204     }
1205 
1206     /* Get some data we'll need */
1207     PresentRate = DeviceExtension->BstData.PresentRate;
1208     RemainingCapacity = DeviceExtension->BstData.RemainingCapacity;
1209 
1210     /* Check if we have battery data in Watts instead of Amps */
1211     if (PowerUnit == ACPI_BATT_POWER_UNIT_WATTS)
1212     {
1213         /* Get the data from the BST */
1214         DeviceExtension->RemainingCapacity = RemainingCapacity;
1215         DeviceExtension->Rate = PresentRate;
1216 
1217         /* Check if the rate is invalid */
1218         if (PresentRate > CM_MAX_VALUE)
1219         {
1220             /* Set an unknown rate and don't touch the old value */
1221             DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
1222             if ((PresentRate != CM_UNKNOWN_VALUE) && (CmBattDebug & CMBATT_ACPI_WARNING))
1223             {
1224                 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
1225                 DbgPrint("----------------------   PresentRate = 0x%08x\n", PresentRate);
1226             }
1227         }
1228     }
1229     else if ((DesignVoltage != CM_UNKNOWN_VALUE) && (DesignVoltage != 0)) // Same as doing PowerUnit == ACPI_BATT_POWER_UNIT_AMPS
1230     {
1231         /* We have voltage data, what about capacity? */
1232         if (RemainingCapacity == CM_UNKNOWN_VALUE)
1233         {
1234             /* Unable to calculate it */
1235             DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
1236             if (CmBattDebug & CMBATT_ACPI_WARNING)
1237             {
1238                 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
1239                 DbgPrint("----------------------   RemainingCapacity = CM_UNKNOWN_VALUE\n");
1240             }
1241         }
1242         else
1243         {
1244             /* Compute the capacity with the information we have */
1245             DeviceExtension->RemainingCapacity = (DesignVoltage * RemainingCapacity + 500) / 1000;
1246         }
1247 
1248         /* Check if we have a rate */
1249         if (PresentRate != CM_UNKNOWN_VALUE)
1250         {
1251             /* Make sure the rate isn't too large */
1252             if (PresentRate > (-500 / DesignVoltage))
1253             {
1254                 /* It is, so set unknown state */
1255                 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
1256                 if (CmBattDebug & CMBATT_ACPI_WARNING)
1257                 {
1258                     DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
1259                     DbgPrint("----------------------   Overflow: PresentRate = 0x%08x\n", PresentRate);
1260                 }
1261             }
1262 
1263             /* Compute the rate */
1264             DeviceExtension->Rate = (PresentRate * DesignVoltage + 500) / 1000;
1265         }
1266         else
1267         {
1268             /* We don't have a rate, so set unknown value */
1269             DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
1270             if (CmBattDebug & CMBATT_ACPI_WARNING)
1271             {
1272                 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
1273                 DbgPrint("----------------------   Present Rate = CM_UNKNOWN_VALUE\n");
1274             }
1275         }
1276     }
1277     else
1278     {
1279         /* We have no rate, and no capacity, set unknown values */
1280         DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
1281         DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
1282         if (CmBattDebug & CMBATT_ACPI_WARNING)
1283         {
1284             DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
1285             DbgPrint("----------------------   DesignVoltage = 0x%08x\n", DesignVoltage);
1286         }
1287     }
1288 
1289     /* Check if we have an unknown rate */
1290     if (DeviceExtension->Rate == BATTERY_UNKNOWN_RATE)
1291     {
1292         /* The battery is discharging but we don't know by how much... this is bad! */
1293         if ((BstState & ACPI_BATT_STAT_DISCHARG) &&
1294             (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
1295             DbgPrint("CmBattGetBatteryStatus: battery rate is unknown when battery is not charging!\n");
1296     }
1297     else if (DeviceExtension->State & BATTERY_DISCHARGING)
1298     {
1299         /* The battery is discharging, so treat the rate as a negative rate */
1300         DeviceExtension->Rate = -(LONG)DeviceExtension->Rate;
1301     }
1302     else if (!(DeviceExtension->State & BATTERY_CHARGING) && (DeviceExtension->Rate))
1303     {
1304         /* We are not charging, not discharging, but have a rate? Ignore it! */
1305         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1306             DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
1307                      DeviceExtension->Rate);
1308         DeviceExtension->Rate = 0;
1309     }
1310 
1311     /* Done */
1312     return STATUS_SUCCESS;
1313 }
1314 
1315 NTSTATUS
1316 NTAPI
CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,IN ULONG Tag,IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,IN OPTIONAL LONG AtRate,IN PVOID Buffer,IN ULONG BufferLength,OUT PULONG ReturnedLength)1317 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,
1318                        IN ULONG Tag,
1319                        IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
1320                        IN OPTIONAL LONG AtRate,
1321                        IN PVOID Buffer,
1322                        IN ULONG BufferLength,
1323                        OUT PULONG ReturnedLength)
1324 {
1325     NTSTATUS Status;
1326     PVOID QueryData = NULL;
1327     ULONG QueryLength = 0;
1328     ULONG RemainingTime = 0;
1329     ANSI_STRING TempString;
1330     UNICODE_STRING TempString2;
1331     WCHAR InfoBuffer[256];
1332     WCHAR TempBuffer[256];
1333     UNICODE_STRING InfoString;
1334     ULONG RemainingCapacity;
1335     BATTERY_REPORTING_SCALE BatteryReportingScale[2];
1336     LONG Rate;
1337     PAGED_CODE();
1338     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1339         DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
1340                  Tag,
1341                  FdoExtension->DeviceId,
1342                  InfoLevel);
1343 
1344     /* Check ACPI Data */
1345     Status = CmBattVerifyStaticInfo(FdoExtension, Tag);
1346     if (!NT_SUCCESS(Status)) return Status;
1347 
1348     /* Check what caller wants */
1349     switch (InfoLevel)
1350     {
1351         case BatteryInformation:
1352             /* Just return our static information */
1353             QueryData = &FdoExtension->BatteryInformation;
1354             QueryLength = sizeof(BATTERY_INFORMATION);
1355             break;
1356 
1357         case BatteryGranularityInformation:
1358 
1359             /* Return our static information, we have two scales */
1360             BatteryReportingScale[0].Granularity = FdoExtension->BatteryCapacityGranularity1;
1361             BatteryReportingScale[0].Capacity = FdoExtension->BatteryInformation.DefaultAlert1;
1362             BatteryReportingScale[1].Granularity = FdoExtension->BatteryCapacityGranularity2;
1363             BatteryReportingScale[1].Capacity = FdoExtension->BatteryInformation.DesignedCapacity;
1364             QueryData = BatteryReportingScale;
1365             QueryLength = sizeof(BATTERY_REPORTING_SCALE) * 2;
1366             break;
1367 
1368         case BatteryEstimatedTime:
1369 
1370             /* Check if it's been more than 15 seconds since the last change */
1371             if (KeQueryInterruptTime() > (FdoExtension->InterruptTime + CMBATT_DISCHARGE_TIME))
1372             {
1373                 /* Get new battery status */
1374                 CmBattGetBatteryStatus(FdoExtension, FdoExtension->Tag);
1375 
1376                 /* If the caller didn't specify a rate, use our static one */
1377                 Rate = AtRate;
1378                 if (!Rate) Rate = FdoExtension->Rate;
1379 
1380                 /* If we don't have a valid negative rate, use unknown value */
1381                 if (Rate >= 0) Rate = BATTERY_UNKNOWN_RATE;
1382 
1383                 /* Grab the remaining capacity */
1384                 RemainingCapacity = FdoExtension->RemainingCapacity;
1385 
1386                 /* Default time to unknown if we fail the request later */
1387                 RemainingTime = BATTERY_UNKNOWN_TIME;
1388 
1389                 /* See if we don't know one or the other */
1390                 if ((Rate == BATTERY_UNKNOWN_RATE) ||
1391                     (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY))
1392                 {
1393                     /* If the battery is discharging, we can't give out a time */
1394                     if ((FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG) &&
1395                         (CmBattDebug & CMBATT_GENERIC_WARNING))
1396                             DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
1397 
1398                     /* Check if we don't have a rate and capacity is going down */
1399                     if ((FdoExtension->Rate == BATTERY_UNKNOWN_RATE) &&
1400                         (FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG))
1401                     {
1402                         /* We have to fail, since we lack data */
1403                         Status = STATUS_INVALID_DEVICE_REQUEST;
1404                         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1405                             DbgPrint("----------------------   PresentRate = BATTERY_UNKNOWN_RATE\n");
1406                     }
1407 
1408                     /* If we don't have capacity, the rate is useless */
1409                     if (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY)
1410                     {
1411                         /* We have to fail the request */
1412                         Status = STATUS_INVALID_DEVICE_REQUEST;
1413                         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1414                             DbgPrint("----------------------   RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
1415                     }
1416                 }
1417                 else
1418                 {
1419                     /* We have data, but is it valid? */
1420                     if (RemainingCapacity > CMBATT_CAPACITY_BOGUS)
1421                     {
1422                         /* The capacity seems bogus, so don't use it */
1423                         if (CmBattDebug & CMBATT_ACPI_WARNING)
1424                             DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
1425                     }
1426                     else
1427                     {
1428                         /* Compute the remaining time in seconds, based on rate */
1429                         RemainingTime = (RemainingCapacity * 3600) / -Rate;
1430                     }
1431                 }
1432             }
1433             else
1434             {
1435                 RemainingTime = BATTERY_UNKNOWN_TIME;
1436             }
1437 
1438             /* Return the remaining time */
1439             QueryData = &RemainingTime;
1440             QueryLength = sizeof(ULONG);
1441             break;
1442 
1443         case BatteryDeviceName:
1444 
1445             /* Build the model number string */
1446             if (FdoExtension->BattInfo.ExtendedData)
1447             {
1448                 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.ModelNumber);
1449             }
1450             else
1451             {
1452                 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.ModelNumber);
1453             }
1454 
1455             /* Convert it to Unicode */
1456             InfoString.Buffer = InfoBuffer;
1457             InfoString.MaximumLength = sizeof(InfoBuffer);
1458             Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1459 
1460             /* Return the unicode buffer */
1461             QueryData = InfoString.Buffer;
1462             QueryLength = InfoString.Length;
1463             break;
1464 
1465         case BatteryTemperature:
1466         case BatteryManufactureDate:
1467 
1468             /* We don't support these */
1469             Status = STATUS_INVALID_DEVICE_REQUEST;
1470             break;
1471 
1472         case BatteryManufactureName:
1473 
1474             /* Build the OEM info string */
1475             if (FdoExtension->BattInfo.ExtendedData)
1476             {
1477                 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.OemInfo);
1478             }
1479             else
1480             {
1481                 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.OemInfo);
1482             }
1483 
1484             /* Convert it to Unicode */
1485             InfoString.Buffer = InfoBuffer;
1486             InfoString.MaximumLength = sizeof(InfoBuffer);
1487             Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1488 
1489             /* Return the unicode buffer */
1490             QueryData = InfoString.Buffer;
1491             QueryLength = InfoString.Length;
1492             break;
1493 
1494         case BatteryUniqueID:
1495 
1496             /* Build the serial number string */
1497             if (FdoExtension->BattInfo.ExtendedData)
1498             {
1499                 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.SerialNumber);
1500             }
1501             else
1502             {
1503                 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.SerialNumber);
1504             }
1505 
1506             /* Convert it to Unicode */
1507             InfoString.Buffer = InfoBuffer;
1508             InfoString.MaximumLength = sizeof(InfoBuffer);
1509             RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1510 
1511             /* Setup a temporary string for concatenation */
1512             TempString2.Buffer = TempBuffer;
1513             TempString2.MaximumLength = sizeof(TempBuffer);
1514 
1515             /* Check if there's an OEM string */
1516             if ((FdoExtension->BattInfo.ExtendedData && FdoExtension->BattInfo.BixData.OemInfo[0]) ||
1517                 FdoExtension->BattInfo.BifData.OemInfo[0])
1518             {
1519                 /* Build the OEM info string */
1520                 if (FdoExtension->BattInfo.ExtendedData)
1521                 {
1522                     RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.OemInfo);
1523                 }
1524                 else
1525                 {
1526                     RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.OemInfo);
1527                 }
1528 
1529                 /* Convert it to Unicode and append it */
1530                 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1531                 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1532             }
1533 
1534             /* Build the model number string */
1535             if (FdoExtension->BattInfo.ExtendedData)
1536             {
1537                 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.ModelNumber);
1538             }
1539             else
1540             {
1541                 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.ModelNumber);
1542             }
1543 
1544             /* Convert it to Unicode and append it */
1545             RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1546             RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1547 
1548             /* Return the final appended string */
1549             QueryData = InfoString.Buffer;
1550             QueryLength = InfoString.Length;
1551             break;
1552 
1553         default:
1554 
1555             /* Everything else is unknown */
1556             Status = STATUS_INVALID_PARAMETER;
1557             break;
1558     }
1559 
1560     /* Return the required length and check if the caller supplied enough */
1561     *ReturnedLength = QueryLength;
1562     if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
1563 
1564     /* Copy the data if there's enough space and it exists */
1565     if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
1566 
1567     /* Return function result */
1568     return Status;
1569 }
1570 
1571 NTSTATUS
1572 NTAPI
CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG Tag,IN PBATTERY_STATUS BatteryStatus)1573 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
1574                   IN ULONG Tag,
1575                   IN PBATTERY_STATUS BatteryStatus)
1576 {
1577     NTSTATUS Status;
1578     PAGED_CODE();
1579     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1580         DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag, DeviceExtension->DeviceId);
1581 
1582     /* Query ACPI information */
1583     Status = CmBattGetBatteryStatus(DeviceExtension, Tag);
1584     if (NT_SUCCESS(Status))
1585     {
1586         BatteryStatus->PowerState = DeviceExtension->State;
1587         BatteryStatus->Capacity = DeviceExtension->RemainingCapacity;
1588         BatteryStatus->Voltage = DeviceExtension->PresentVoltage;
1589         BatteryStatus->Rate = DeviceExtension->Rate;
1590     }
1591 
1592     /* Return status */
1593     if (CmBattDebug & (CMBATT_GENERIC_INFO))
1594         DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
1595                  BatteryStatus->PowerState,
1596                  BatteryStatus->Capacity,
1597                  BatteryStatus->Voltage,
1598                  BatteryStatus->Rate);
1599     return Status;
1600 }
1601 
1602 NTSTATUS
1603 NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)1604 DriverEntry(IN PDRIVER_OBJECT DriverObject,
1605             IN PUNICODE_STRING RegistryPath)
1606 {
1607     NTSTATUS Status;
1608     PDRIVER_EXTENSION DriverExtension;
1609     OBJECT_ATTRIBUTES ObjectAttributes;
1610     UNICODE_STRING CallbackName;
1611 
1612     /* Allocate registry path */
1613     GlobalRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
1614     GlobalRegistryPath.Length = RegistryPath->Length;
1615     GlobalRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
1616                                                       GlobalRegistryPath.MaximumLength,
1617                                                       'MtaB');
1618     if (!GlobalRegistryPath.Buffer)
1619     {
1620         /* Fail if we're out of memory this early */
1621         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1622             DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
1623         return STATUS_INSUFFICIENT_RESOURCES;
1624     }
1625 
1626     /* Buffer allocated, copy the string */
1627     RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
1628     if (CmBattDebug & CMBATT_GENERIC_INFO)
1629         DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%wZ\"\n",
1630                  DriverObject,
1631                  RegistryPath);
1632 
1633     /* Setup the major dispatchers */
1634     DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
1635     DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
1636     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
1637     DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
1638     DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
1639     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
1640 
1641     /* And the unload routine */
1642     DriverObject->DriverUnload = CmBattUnload;
1643 
1644     /* And the add device routine */
1645     DriverExtension = DriverObject->DriverExtension;
1646     DriverExtension->AddDevice = CmBattAddDevice;
1647 
1648     /* Create a power callback */
1649     RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
1650     InitializeObjectAttributes(&ObjectAttributes,
1651                                &CallbackName,
1652                                OBJ_KERNEL_HANDLE,
1653                                NULL,
1654                                NULL);
1655     Status = ExCreateCallback(&CmBattPowerCallBackObject, &ObjectAttributes, 0, TRUE);
1656     if (!NT_SUCCESS(Status))
1657     {
1658         /* No callback, fail */
1659         CmBattPowerCallBackObject = 0;
1660         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1661             DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status);
1662     }
1663     else
1664     {
1665         /* Register the power callback now */
1666         CmBattPowerCallBackRegistration = ExRegisterCallback(CmBattPowerCallBackObject,
1667                                                              (PVOID)CmBattPowerCallBack,
1668                                                              DriverObject);
1669         if (CmBattPowerCallBackRegistration)
1670         {
1671             /* Last thing: setup our DPC and timer for battery wake */
1672             KeInitializeDpc(&CmBattWakeDpcObject, (PVOID)CmBattWakeDpc, DriverObject);
1673             KeInitializeTimer(&CmBattWakeDpcTimerObject);
1674         }
1675         else
1676         {
1677             ObDereferenceObject(CmBattPowerCallBackObject);
1678             if (CmBattDebug & CMBATT_GENERIC_WARNING)
1679                 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1680         }
1681 
1682         /* All good */
1683         Status = STATUS_SUCCESS;
1684     }
1685 
1686     /* Return failure or success */
1687     return Status;
1688 }
1689 
1690 /* EOF */
1691