xref: /reactos/drivers/bus/acpi/cmbatt/cmbatt.c (revision e8c7597b)
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
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
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
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
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 NTSTATUS
277 NTAPI
278 CmBattVerifyStaticInfo(PCMBATT_DEVICE_EXTENSION DeviceExtension,
279                        ULONG BatteryTag)
280 {
281     ACPI_BIF_DATA BifData;
282     PBATTERY_INFORMATION Info = &DeviceExtension->BatteryInformation;
283     NTSTATUS Status;
284 
285     Status = CmBattGetBifData(DeviceExtension, &BifData);
286     if (NT_SUCCESS(Status))
287     {
288         RtlZeroMemory(Info, sizeof(*Info));
289         Info->Capabilities = BATTERY_SYSTEM_BATTERY;
290         Info->Technology = BifData.BatteryTechnology;
291         RtlCopyMemory(Info->Chemistry, BifData.BatteryType, 4);
292         // FIXME: take from _BIX method: Info->CycleCount
293         DeviceExtension->BifData = BifData;
294 
295         if (BifData.PowerUnit == 1)
296         {
297             DPRINT1("FIXME: need to convert mAh into mWh\n");
298             Info->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
299             Info->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
300             Info->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
301             Info->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
302         }
303         else
304         {
305             Info->DesignedCapacity = BifData.DesignCapacity;
306             Info->FullChargedCapacity = BifData.LastFullCapacity;
307             Info->DefaultAlert1 = BifData.DesignCapacityLow;
308             Info->DefaultAlert2 = BifData.DesignCapacityWarning;
309         }
310     }
311 
312     return Status;
313 }
314 
315 NTSTATUS
316 NTAPI
317 CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject,
318                 IN PIRP Irp)
319 {
320     NTSTATUS Status = STATUS_SUCCESS;
321     PIO_STACK_LOCATION IoStackLocation;
322     UCHAR Major;
323     ULONG Count;
324     PCMBATT_DEVICE_EXTENSION DeviceExtension;
325     PAGED_CODE();
326     if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattOpenClose\n");
327 
328     /* Grab the device extension and lock it */
329     DeviceExtension = DeviceObject->DeviceExtension;
330     ExAcquireFastMutex(&DeviceExtension->FastMutex);
331 
332     /* Check if someone is trying to open a device that doesn't exist yet */
333     Count = DeviceExtension->HandleCount;
334     if (Count == 0xFFFFFFFF)
335     {
336         /* Fail the request */
337         Status = STATUS_NO_SUCH_DEVICE;
338         if (CmBattDebug & CMBATT_PNP_INFO)
339         {
340             DbgPrint("CmBattOpenClose: Failed (UID = %x)(device being removed).\n",
341                      DeviceExtension->Tag);
342         }
343         goto Complete;
344     }
345 
346     /* Check if this is an open or close */
347     IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
348     Major = IoStackLocation->MajorFunction;
349     if (Major == IRP_MJ_CREATE)
350     {
351         /* Increment the open count */
352         DeviceExtension->HandleCount = Count + 1;
353         if (CmBattDebug & CMBATT_PNP_INFO)
354         {
355             DbgPrint("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
356                      DeviceExtension->DeviceId, Count + 1);
357         }
358     }
359     else if (Major == IRP_MJ_CLOSE)
360     {
361         /* Decrement the open count */
362         DeviceExtension->HandleCount = Count - 1;
363         if (CmBattDebug & CMBATT_PNP_INFO)
364         {
365             DbgPrint("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
366                      DeviceExtension->DeviceId, Count + 1);
367         }
368     }
369 
370 Complete:
371     /* Release lock and complete request */
372     ExReleaseFastMutex(&DeviceExtension->FastMutex);
373     Irp->IoStatus.Status = Status;
374     IoCompleteRequest(Irp, IO_NO_INCREMENT);
375     return Status;
376 }
377 
378 NTSTATUS
379 NTAPI
380 CmBattIoctl(IN PDEVICE_OBJECT DeviceObject,
381             IN PIRP Irp)
382 {
383     PCMBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
384     NTSTATUS Status;
385     PIO_STACK_LOCATION IoStackLocation;
386     ULONG IoControlCode, OutputBufferLength, InputBufferLength;
387     PAGED_CODE();
388     if (CmBattDebug & 2) DbgPrint("CmBattIoctl\n");
389 
390     /* Acquire the remove lock */
391     Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, Irp);
392     if (!NT_SUCCESS(Status))
393     {
394         /* It's too late, fail */
395         Irp->IoStatus.Status = STATUS_DEVICE_REMOVED;
396         IoCompleteRequest(Irp, IO_NO_INCREMENT);
397         return STATUS_DEVICE_REMOVED;
398     }
399 
400     /* There's nothing to do for an AC adapter */
401     if (DeviceExtension->FdoType == CmBattAcAdapter)
402     {
403         /* Pass it down, and release the remove lock */
404         IoSkipCurrentIrpStackLocation(Irp);
405         Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
406         IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
407         return Status;
408     }
409 
410     /* Send to class driver */
411     Status = BatteryClassIoctl(DeviceExtension->ClassData, Irp);
412     if (Status == STATUS_NOT_SUPPORTED)
413     {
414         /* Read IOCTL information from IRP stack */
415         IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
416         IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
417         OutputBufferLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
418         InputBufferLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
419         if (CmBattDebug & 4)
420             DbgPrint("CmBattIoctl: Received  Direct Access IOCTL %x\n", IoControlCode);
421 
422         /* Handle internal IOCTLs */
423         switch (IoControlCode)
424         {
425             case IOCTL_BATTERY_QUERY_UNIQUE_ID:
426 
427                 /* Data is 4 bytes long */
428                 if (OutputBufferLength == sizeof(ULONG))
429                 {
430                     /* Query it */
431                     Status = CmBattGetUniqueId(DeviceExtension->PdoDeviceObject,
432                                                Irp->AssociatedIrp.SystemBuffer);
433                     if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
434                 }
435                 else
436                 {
437                     /* Buffer size invalid */
438                     Status = STATUS_INVALID_BUFFER_SIZE;
439                 }
440                 break;
441 
442             case IOCTL_BATTERY_QUERY_STA:
443 
444                 /* Data is 4 bytes long */
445                 if (OutputBufferLength == sizeof(ULONG))
446                 {
447                     /* Query it */
448                     Status = CmBattGetStaData(DeviceExtension->PdoDeviceObject,
449                                               Irp->AssociatedIrp.SystemBuffer);
450                     if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
451                 }
452                 else
453                 {
454                     /* Buffer size invalid */
455                     Status = STATUS_INVALID_BUFFER_SIZE;
456                 }
457                 break;
458 
459             case IOCTL_BATTERY_QUERY_PSR:
460 
461                 /* Data is 4 bytes long */
462                 if (OutputBufferLength == sizeof(ULONG))
463                 {
464                     /* Do we have an AC adapter? */
465                     if (AcAdapterPdo)
466                     {
467                         /* Query it */
468                         Status = CmBattGetPsrData(AcAdapterPdo,
469                                                   Irp->AssociatedIrp.SystemBuffer);
470                         if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
471                     }
472                     else
473                     {
474                         /* No adapter, just a battery, so fail */
475                         Status = STATUS_NO_SUCH_DEVICE;
476                     }
477                 }
478                 else
479                 {
480                     /* Buffer size invalid */
481                     Status = STATUS_INVALID_BUFFER_SIZE;
482                 }
483                 break;
484 
485             case IOCTL_BATTERY_SET_TRIP_POINT:
486 
487                 /* Data is 4 bytes long */
488                 if (InputBufferLength == sizeof(ULONG))
489                 {
490                     /* Query it */
491                     Status = CmBattSetTripPpoint(DeviceExtension,
492                                                  *(PULONG)Irp->AssociatedIrp.SystemBuffer);
493                     Irp->IoStatus.Information = 0;
494                 }
495                 else
496                 {
497                     /* Buffer size invalid */
498                     Status = STATUS_INVALID_BUFFER_SIZE;
499                 }
500                 break;
501 
502             case IOCTL_BATTERY_QUERY_BIF:
503 
504                 /* Data is 1060 bytes long */
505                 if (OutputBufferLength == sizeof(ACPI_BIF_DATA))
506                 {
507                     /* Query it */
508                     Status = CmBattGetBifData(DeviceExtension,
509                                               Irp->AssociatedIrp.SystemBuffer);
510                     if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ACPI_BIF_DATA);
511                 }
512                 else
513                 {
514                     /* Buffer size invalid */
515                     Status = STATUS_INVALID_BUFFER_SIZE;
516                 }
517                 break;
518 
519             case IOCTL_BATTERY_QUERY_BST:
520 
521                 /* Data is 16 bytes long */
522                 if (OutputBufferLength == sizeof(ACPI_BST_DATA))
523                 {
524                     /* Query it */
525                     Status = CmBattGetBstData(DeviceExtension,
526                                               Irp->AssociatedIrp.SystemBuffer);
527                     if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ACPI_BST_DATA);
528                 }
529                 else
530                 {
531                     /* Buffer size invalid */
532                     Status = STATUS_INVALID_BUFFER_SIZE;
533                 }
534                 break;
535 
536             default:
537 
538                 /* Unknown, let us pass it on to ACPI */
539                 if (CmBattDebug & 0xC)
540                     DbgPrint("CmBattIoctl: Unknown IOCTL %x\n", IoControlCode);
541                 break;
542         }
543 
544         /* Did someone pick it up? */
545         if (Status != STATUS_NOT_SUPPORTED)
546         {
547             /* Complete the request */
548             Irp->IoStatus.Status = Status;
549             IoCompleteRequest(Irp, IO_NO_INCREMENT);
550         }
551         else
552         {
553             /* Still unsupported, try ACPI */
554             IoSkipCurrentIrpStackLocation(Irp);
555             Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
556         }
557     }
558 
559     /* Release the remove lock and return status */
560     IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
561     return Status;
562 }
563 
564 NTSTATUS
565 NTAPI
566 CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
567                OUT PULONG Tag)
568 {
569     PDEVICE_OBJECT PdoDevice;
570     ULONG StaData;
571     ULONG NewTag;
572     NTSTATUS Status;
573     PAGED_CODE();
574     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
575       DbgPrint("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
576                 *Tag, DeviceExtension, DeviceExtension->DeviceId);
577 
578     /* Get PDO and clear notification flag */
579     PdoDevice = DeviceExtension->PdoDeviceObject;
580     DeviceExtension->NotifySent = 0;
581 
582     /* Get _STA from PDO (we need the machine status, not the battery status) */
583     Status = CmBattGetStaData(PdoDevice, &StaData);
584     if (NT_SUCCESS(Status))
585     {
586         /* Is a battery present? */
587         if (StaData & ACPI_STA_BATTERY_PRESENT)
588         {
589             /* Do we not have a tag yet? */
590             if (!DeviceExtension->Tag)
591             {
592                 /* Set the new tag value, reset tags if we reached the maximum */
593                 NewTag = DeviceExtension->TagData;
594                 if (DeviceExtension->TagData++ == 0xFFFFFFFF) NewTag = 1;
595                 DeviceExtension->Tag = NewTag;
596                 if (CmBattDebug & CMBATT_GENERIC_INFO)
597                     DbgPrint("CmBattQueryTag - New Tag: (%d)\n", DeviceExtension->Tag);
598 
599                 /* Reset trip point data */
600                 DeviceExtension->TripPointOld = 0;
601                 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
602 
603                 /* Clear AR lock and set new interrupt time */
604                 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
605                 DeviceExtension->InterruptTime = KeQueryInterruptTime();
606             }
607         }
608         else
609         {
610             /* No battery, so no tag */
611             DeviceExtension->Tag = 0;
612             Status = STATUS_NO_SUCH_DEVICE;
613         }
614     }
615 
616     /* Return the tag and status result */
617     *Tag = DeviceExtension->Tag;
618     if (CmBattDebug & CMBATT_ACPI_WARNING)
619       DbgPrint("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n", *Tag, Status);
620     return Status;
621 }
622 
623 NTSTATUS
624 NTAPI
625 CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)
626 {
627     NTSTATUS Status;
628     PAGED_CODE();
629     if (CmBattDebug & 0xA) DbgPrint("CmBattDisableStatusNotify\n");
630 
631     /* Do we have a trip point */
632     if (DeviceExtension->TripPointSet)
633     {
634         /* Is there a current value set? */
635         if (DeviceExtension->TripPointValue)
636         {
637             /* Reset it back to 0 */
638             DeviceExtension->TripPointValue = 0;
639             Status = CmBattSetTripPpoint(DeviceExtension, 0);
640             if (!NT_SUCCESS(Status))
641             {
642                 /* If it failed, set unknown/invalid value */
643                 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
644                 if (CmBattDebug & 8)
645                     DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status);
646             }
647         }
648         else
649         {
650             /* No trip point set, so this is a successful no-op */
651             Status = STATUS_SUCCESS;
652         }
653     }
654     else
655     {
656         /* Nothing we can do */
657         Status = STATUS_OBJECT_NAME_NOT_FOUND;
658     }
659 
660     /* Return status */
661     return Status;
662 }
663 
664 NTSTATUS
665 NTAPI
666 CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
667                       IN ULONG BatteryTag,
668                       IN PBATTERY_NOTIFY BatteryNotify)
669 {
670     NTSTATUS Status;
671     ACPI_BST_DATA BstData;
672     ULONG Capacity, NewTripPoint, TripPoint, DesignVoltage;
673     BOOLEAN Charging;
674     PAGED_CODE();
675     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
676         DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
677                  BatteryTag, BatteryNotify->LowCapacity);
678 
679     /* Update any ACPI evaluations */
680     Status = CmBattVerifyStaticInfo(DeviceExtension, BatteryTag);
681     if (!NT_SUCCESS(Status)) return Status;
682 
683     /* Trip point not supported, fail */
684     if (!DeviceExtension->TripPointSet) return STATUS_OBJECT_NAME_NOT_FOUND;
685 
686     /* Are both capacities known? */
687     if ((BatteryNotify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
688         (BatteryNotify->LowCapacity == BATTERY_UNKNOWN_CAPACITY))
689     {
690         /* We can't set trip points without these */
691         if (CmBattDebug & CMBATT_GENERIC_WARNING)
692             DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
693         return STATUS_NOT_SUPPORTED;
694     }
695 
696     /* Is the battery charging? */
697     Charging = DeviceExtension->BstData.State & ACPI_BATT_STAT_CHARGING;
698     if (Charging)
699     {
700         /* Then the trip point is when we hit the cap */
701         Capacity = BatteryNotify->HighCapacity;
702         NewTripPoint = BatteryNotify->HighCapacity;
703     }
704     else
705     {
706         /* Otherwise it's when we discharge to the bottom */
707         Capacity = BatteryNotify->LowCapacity;
708         NewTripPoint = BatteryNotify->LowCapacity;
709     }
710 
711     /* Do we have data in Amps or Watts? */
712     if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
713     {
714         /* We need the voltage to do the conversion */
715         DesignVoltage = DeviceExtension->BifData.DesignVoltage;
716         if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage))
717         {
718             /* Convert from mAh into Ah */
719             TripPoint = 1000 * NewTripPoint;
720             if (Charging)
721             {
722                 /* Scale the high trip point */
723                 NewTripPoint = (TripPoint + 500) / DesignVoltage + ((TripPoint + 500) % DesignVoltage != 0);
724             }
725             else
726             {
727                 /* Scale the low trip point */
728                 NewTripPoint = (TripPoint - 500) / DesignVoltage - ((TripPoint - 500) % DesignVoltage == 0);
729             }
730         }
731         else
732         {
733             /* Without knowing the voltage, Amps are not enough data on consumption */
734             Status = STATUS_NOT_SUPPORTED;
735             if (CmBattDebug & CMBATT_ACPI_WARNING)
736                 DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
737                         DesignVoltage);
738         }
739     }
740     else if (Charging)
741     {
742         /* Make it trip just one past the charge cap */
743         ++NewTripPoint;
744     }
745     else if (NewTripPoint > 0)
746     {
747         /* Make it trip just one below the drain cap */
748         --NewTripPoint;
749     }
750 
751     /* Do we actually have a new trip point? */
752     if (NewTripPoint == DeviceExtension->TripPointValue)
753     {
754         /* No, so there is no work to be done */
755         if (CmBattDebug & CMBATT_GENERIC_STATUS)
756             DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension->TripPointValue);
757         return STATUS_SUCCESS;
758     }
759 
760     /* Set the trip point with ACPI and check for success */
761     DeviceExtension->TripPointValue = NewTripPoint;
762     Status = CmBattSetTripPpoint(DeviceExtension, NewTripPoint);
763     if (!(NewTripPoint) && (Capacity)) Status = STATUS_NOT_SUPPORTED;
764     if (!NT_SUCCESS(Status))
765     {
766         /* We failed to set the trip point, or there wasn't one settable */
767         DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
768         if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
769             DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status);
770         return Status;
771     }
772 
773     /* Read the new BST data to see the latest state */
774     Status = CmBattGetBstData(DeviceExtension, &BstData);
775     if (!NT_SUCCESS(Status))
776     {
777         /* We'll return failure to the caller */
778         if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
779             DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status);
780     }
781     else if ((Charging) && (BstData.RemainingCapacity >= NewTripPoint))
782     {
783         /* We are charging and our capacity is past the trip point, so trip now */
784         if (CmBattDebug & CMBATT_GENERIC_WARNING)
785             DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
786                      NewTripPoint, BstData.RemainingCapacity);
787         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
788     }
789     else if ((BstData.RemainingCapacity) && (Capacity))
790     {
791         /* We are discharging, and our capacity is below the trip point, trip now */
792         if (CmBattDebug & CMBATT_GENERIC_WARNING)
793             DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
794                      NewTripPoint, BstData.RemainingCapacity);
795         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
796     }
797 
798     /* All should've went well if we got here, unless BST failed... return! */
799     if (CmBattDebug & CMBATT_GENERIC_STATUS)
800           DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
801                     Capacity, DeviceExtension->RemainingCapacity);
802     if (CmBattDebug & CMBATT_ACPI_WARNING)
803           DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %lx\n",
804                     BatteryNotify->PowerState,
805                     BatteryNotify->LowCapacity,
806                     BatteryNotify->HighCapacity,
807                     Status);
808     return Status;
809 }
810 
811 NTSTATUS
812 NTAPI
813 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
814                        IN ULONG Tag)
815 {
816     ULONG PsrData = 0;
817     NTSTATUS Status;
818     ULONG BstState;
819     ULONG DesignVoltage, PresentRate, RemainingCapacity;
820     PAGED_CODE();
821     if (CmBattDebug & CMBATT_GENERIC_INFO)
822         DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension, Tag);
823 
824     /* Validate ACPI data */
825     Status = CmBattVerifyStaticInfo(DeviceExtension, Tag);
826     if (!NT_SUCCESS(Status)) return Status;
827 
828     /* Check for delayed status notifications */
829     if (DeviceExtension->DelayNotification)
830     {
831         /* Process them now and don't do any other work */
832         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
833         return Status;
834     }
835 
836     /* Get _BST from ACPI */
837     Status = CmBattGetBstData(DeviceExtension, &DeviceExtension->BstData);
838     if (!NT_SUCCESS(Status))
839     {
840         /* Fail */
841         InterlockedExchange(&DeviceExtension->ArLockValue, 0);
842         return Status;
843     }
844 
845     /* Clear current BST information */
846     DeviceExtension->State = 0;
847     DeviceExtension->RemainingCapacity = 0;
848     DeviceExtension->PresentVoltage = 0;
849     DeviceExtension->Rate = 0;
850 
851     /* Get battery state */
852     BstState = DeviceExtension->BstData.State;
853 
854     /* Is the battery both charging and discharging? */
855     if ((BstState & ACPI_BATT_STAT_DISCHARG) && (BstState & ACPI_BATT_STAT_CHARGING) &&
856         (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
857             DbgPrint("************************ ACPI BIOS BUG ********************\n* "
858                      "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
859                      "* One battery cannot be charging and discharging at the same time.\n",
860                      BstState);
861 
862     /* Is the battery discharging? */
863     if (BstState & ACPI_BATT_STAT_DISCHARG)
864     {
865         /* Set power state and check if it just started discharging now */
866         DeviceExtension->State |= BATTERY_DISCHARGING;
867         if (!(DeviceExtension->State & ACPI_BATT_STAT_DISCHARG))
868         {
869             /* Remember the time when the state changed */
870             DeviceExtension->InterruptTime = KeQueryInterruptTime();
871         }
872     }
873     else if (BstState & ACPI_BATT_STAT_CHARGING)
874     {
875         /* Battery is charging, update power state */
876         DeviceExtension->State |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
877     }
878 
879     /* Is the battery in a critical state? */
880     if (BstState & ACPI_BATT_STAT_CRITICAL) DeviceExtension->State |= BATTERY_CRITICAL;
881 
882     /* Read the voltage data */
883     DeviceExtension->PresentVoltage = DeviceExtension->BstData.PresentVoltage;
884 
885     /* Check if we have an A/C adapter */
886     if (AcAdapterPdo)
887     {
888         /* Query information on it */
889         CmBattGetPsrData(AcAdapterPdo, &PsrData);
890     }
891     else
892     {
893         /* Otherwise, check if the battery is charging */
894         if (BstState & ACPI_BATT_STAT_CHARGING)
895         {
896             /* Then we'll assume there's a charger */
897             PsrData = 1;
898         }
899         else
900         {
901             /* Assume no charger */
902             PsrData = 0;
903         }
904     }
905 
906     /* Is there a charger? */
907     if (PsrData)
908     {
909         /* Set the power state flag to reflect this */
910         DeviceExtension->State |= BATTERY_POWER_ON_LINE;
911         if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
912             DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
913     }
914     else if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
915     {
916         DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
917     }
918 
919     /* Get some data we'll need */
920     DesignVoltage = DeviceExtension->BifData.DesignVoltage;
921     PresentRate = DeviceExtension->BstData.PresentRate;
922     RemainingCapacity = DeviceExtension->BstData.RemainingCapacity;
923 
924     /* Check if we have battery data in Watts instead of Amps */
925     if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_WATTS)
926     {
927         /* Get the data from the BST */
928         DeviceExtension->RemainingCapacity = RemainingCapacity;
929         DeviceExtension->Rate = PresentRate;
930 
931         /* Check if the rate is invalid */
932         if (PresentRate > CM_MAX_VALUE)
933         {
934             /* Set an unknown rate and don't touch the old value */
935             DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
936             if ((PresentRate != CM_UNKNOWN_VALUE) && (CmBattDebug & CMBATT_ACPI_WARNING))
937             {
938                 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
939                 DbgPrint("----------------------   PresentRate = 0x%08x\n", PresentRate);
940             }
941         }
942     }
943     else if ((DesignVoltage != CM_UNKNOWN_VALUE) && (DesignVoltage))
944     {
945         /* We have voltage data, what about capacity? */
946         if (RemainingCapacity == CM_UNKNOWN_VALUE)
947         {
948             /* Unable to calculate it */
949             DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
950             if (CmBattDebug & CMBATT_ACPI_WARNING)
951             {
952                 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
953                 DbgPrint("----------------------   RemainingCapacity = CM_UNKNOWN_VALUE\n");
954             }
955         }
956         else
957         {
958             /* Compute the capacity with the information we have */
959             DeviceExtension->RemainingCapacity = (DesignVoltage * RemainingCapacity + 500) / 1000;
960         }
961 
962         /* Check if we have a rate */
963         if (PresentRate != CM_UNKNOWN_VALUE)
964         {
965             /* Make sure the rate isn't too large */
966             if (PresentRate > (-500 / DesignVoltage))
967             {
968                 /* It is, so set unknown state */
969                 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
970                 if (CmBattDebug & CMBATT_ACPI_WARNING)
971                 {
972                     DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
973                     DbgPrint("----------------------   Overflow: PresentRate = 0x%08x\n", PresentRate);
974                 }
975             }
976 
977             /* Compute the rate */
978             DeviceExtension->Rate = (PresentRate * DesignVoltage + 500) / 1000;
979         }
980         else
981         {
982             /* We don't have a rate, so set unknown value */
983             DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
984             if (CmBattDebug & CMBATT_ACPI_WARNING)
985             {
986                 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
987                 DbgPrint("----------------------   Present Rate = CM_UNKNOWN_VALUE\n");
988             }
989         }
990     }
991     else
992     {
993         /* We have no rate, and no capacity, set unknown values */
994         DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
995         DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
996         if (CmBattDebug & CMBATT_ACPI_WARNING)
997         {
998             DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
999             DbgPrint("----------------------   DesignVoltage = 0x%08x\n", DesignVoltage);
1000         }
1001     }
1002 
1003     /* Check if we have an unknown rate */
1004     if (DeviceExtension->Rate == BATTERY_UNKNOWN_RATE)
1005     {
1006         /* The battery is discharging but we don't know by how much... this is bad! */
1007         if ((BstState & ACPI_BATT_STAT_DISCHARG) &&
1008             (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
1009             DbgPrint("CmBattGetBatteryStatus: battery rate is unknown when battery is not charging!\n");
1010     }
1011     else if (DeviceExtension->State & BATTERY_DISCHARGING)
1012     {
1013         /* The battery is discharging, so treat the rate as a negative rate */
1014         DeviceExtension->Rate = -(LONG)DeviceExtension->Rate;
1015     }
1016     else if (!(DeviceExtension->State & BATTERY_CHARGING) && (DeviceExtension->Rate))
1017     {
1018         /* We are not charging, not discharging, but have a rate? Ignore it! */
1019         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1020             DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
1021                      DeviceExtension->Rate);
1022         DeviceExtension->Rate = 0;
1023     }
1024 
1025     /* Done */
1026     return STATUS_SUCCESS;
1027 }
1028 
1029 NTSTATUS
1030 NTAPI
1031 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,
1032                        IN ULONG Tag,
1033                        IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
1034                        IN OPTIONAL LONG AtRate,
1035                        IN PVOID Buffer,
1036                        IN ULONG BufferLength,
1037                        OUT PULONG ReturnedLength)
1038 {
1039     NTSTATUS Status;
1040     PVOID QueryData = NULL;
1041     ULONG QueryLength = 0;
1042     ULONG RemainingTime = 0;
1043     ANSI_STRING TempString;
1044     UNICODE_STRING TempString2;
1045     WCHAR InfoBuffer[256];
1046     WCHAR TempBuffer[256];
1047     UNICODE_STRING InfoString;
1048     ULONG RemainingCapacity;
1049     BATTERY_REPORTING_SCALE BatteryReportingScale[2];
1050     LONG Rate;
1051     PAGED_CODE();
1052     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1053         DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
1054                  Tag,
1055                  FdoExtension->DeviceId,
1056                  InfoLevel);
1057 
1058     /* Check ACPI Data */
1059     Status = CmBattVerifyStaticInfo(FdoExtension, Tag);
1060     if (!NT_SUCCESS(Status)) return Status;
1061 
1062     /* Check what caller wants */
1063     switch (InfoLevel)
1064     {
1065         case BatteryInformation:
1066             /* Just return our static information */
1067             QueryData = &FdoExtension->BatteryInformation;
1068             QueryLength = sizeof(BATTERY_INFORMATION);
1069             break;
1070 
1071         case BatteryGranularityInformation:
1072 
1073             /* Return our static information, we have two scales */
1074             BatteryReportingScale[0].Granularity = FdoExtension->BatteryCapacityGranularity1;
1075             BatteryReportingScale[0].Capacity = FdoExtension->BatteryInformation.DefaultAlert1;
1076             BatteryReportingScale[1].Granularity = FdoExtension->BatteryCapacityGranularity2;
1077             BatteryReportingScale[1].Capacity = FdoExtension->BatteryInformation.DesignedCapacity;
1078             QueryData = BatteryReportingScale;
1079             QueryLength = sizeof(BATTERY_REPORTING_SCALE) * 2;
1080             break;
1081 
1082         case BatteryEstimatedTime:
1083 
1084             /* Check if it's been more than 2 1/2 minutes since the last change */
1085             if ((KeQueryInterruptTime() - 150000000) > (FdoExtension->InterruptTime))
1086             {
1087                 /* Get new battery status */
1088                 CmBattGetBatteryStatus(FdoExtension, FdoExtension->Tag);
1089 
1090                 /* If the caller didn't specify a rate, use our static one */
1091                 Rate = AtRate;
1092                 if (!Rate) Rate = FdoExtension->Rate;
1093 
1094                 /* If we don't have a valid negative rate, use unknown value */
1095                 if (Rate >= 0) Rate = BATTERY_UNKNOWN_RATE;
1096 
1097                 /* Grab the remaining capacity */
1098                 RemainingCapacity = FdoExtension->RemainingCapacity;
1099 
1100                 /* See if we don't know one or the other */
1101                 if ((Rate == BATTERY_UNKNOWN_RATE) ||
1102                     (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY))
1103                 {
1104                     /* If the battery is discharging, we can't give out a time */
1105                     if ((FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG) &&
1106                         (CmBattDebug & CMBATT_GENERIC_WARNING))
1107                             DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
1108 
1109                     /* Check if we don't have a rate and capacity is going down */
1110                     if ((FdoExtension->Rate == BATTERY_UNKNOWN_RATE) &&
1111                         (FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG))
1112                     {
1113                         /* We have to fail, since we lack data */
1114                         Status = STATUS_INVALID_DEVICE_REQUEST;
1115                         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1116                             DbgPrint("----------------------   PresentRate = BATTERY_UNKNOWN_RATE\n");
1117                     }
1118 
1119                     /* If we don't have capacity, the rate is useless */
1120                     if (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY)
1121                     {
1122                         /* We have to fail the request */
1123                         Status = STATUS_INVALID_DEVICE_REQUEST;
1124                         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1125                             DbgPrint("----------------------   RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
1126                     }
1127                 }
1128                 else
1129                 {
1130                     /* We have data, but is it valid? */
1131                     if (RemainingCapacity > 0x123456)
1132                     {
1133                         /* The capacity seems bogus, so don't use it */
1134                         if (CmBattDebug & CMBATT_ACPI_WARNING)
1135                             DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
1136                     }
1137                     else
1138                     {
1139                         /* Compute the remaining time in seconds, based on rate */
1140                         RemainingTime = (RemainingCapacity * 3600) / -Rate;
1141                     }
1142                 }
1143             }
1144 
1145             /* Return the remaining time */
1146             QueryData = &RemainingTime;
1147             QueryLength = sizeof(ULONG);
1148             break;
1149 
1150         case BatteryDeviceName:
1151 
1152             /* Build the model number string */
1153             RtlInitAnsiString(&TempString, FdoExtension->BifData.ModelNumber);
1154 
1155             /* Convert it to Unicode */
1156             InfoString.Buffer = InfoBuffer;
1157             InfoString.MaximumLength = sizeof(InfoBuffer);
1158             Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1159 
1160             /* Return the unicode buffer */
1161             QueryData = InfoString.Buffer;
1162             QueryLength = InfoString.Length;
1163             break;
1164 
1165         case BatteryTemperature:
1166         case BatteryManufactureDate:
1167 
1168             /* We don't support these */
1169             Status = STATUS_INVALID_DEVICE_REQUEST;
1170             break;
1171 
1172         case BatteryManufactureName:
1173 
1174             /* Build the OEM info string */
1175             RtlInitAnsiString(&TempString, FdoExtension->BifData.OemInfo);
1176 
1177             /* Convert it to Unicode */
1178             InfoString.Buffer = InfoBuffer;
1179             InfoString.MaximumLength = sizeof(InfoBuffer);
1180             Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1181 
1182             /* Return the unicode buffer */
1183             QueryData = InfoString.Buffer;
1184             QueryLength = InfoString.Length;
1185             break;
1186 
1187         case BatteryUniqueID:
1188 
1189             /* Build the serial number string */
1190             RtlInitAnsiString(&TempString, FdoExtension->BifData.SerialNumber);
1191 
1192             /* Convert it to Unicode */
1193             InfoString.Buffer = InfoBuffer;
1194             InfoString.MaximumLength = sizeof(InfoBuffer);
1195             RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1196 
1197             /* Setup a temporary string for concatenation */
1198             TempString2.Buffer = TempBuffer;
1199             TempString2.MaximumLength = sizeof(TempBuffer);
1200 
1201             /* Check if there's an OEM string */
1202             if (FdoExtension->BifData.OemInfo[0])
1203             {
1204                 /* Build the OEM info string */
1205                 RtlInitAnsiString(&TempString, FdoExtension->BifData.OemInfo);
1206 
1207                 /* Convert it to Unicode and append it */
1208                 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1209                 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1210             }
1211 
1212             /* Build the model number string */
1213             RtlInitAnsiString(&TempString, FdoExtension->BifData.ModelNumber);
1214 
1215             /* Convert it to Unicode and append it */
1216             RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1217             RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1218 
1219             /* Return the final appended string */
1220             QueryData = InfoString.Buffer;
1221             QueryLength = InfoString.Length;
1222             break;
1223 
1224         default:
1225 
1226             /* Everything else is unknown */
1227             Status = STATUS_INVALID_PARAMETER;
1228             break;
1229     }
1230 
1231     /* Return the required length and check if the caller supplied enough */
1232     *ReturnedLength = QueryLength;
1233     if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
1234 
1235     /* Copy the data if there's enough space and it exists */
1236     if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
1237 
1238     /* Return function result */
1239     return Status;
1240 }
1241 
1242 NTSTATUS
1243 NTAPI
1244 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
1245                   IN ULONG Tag,
1246                   IN PBATTERY_STATUS BatteryStatus)
1247 {
1248     NTSTATUS Status;
1249     PAGED_CODE();
1250     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1251         DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag, DeviceExtension->DeviceId);
1252 
1253     /* Query ACPI information */
1254     Status = CmBattGetBatteryStatus(DeviceExtension, Tag);
1255     if (NT_SUCCESS(Status))
1256     {
1257         BatteryStatus->PowerState = DeviceExtension->State;
1258         BatteryStatus->Capacity = DeviceExtension->RemainingCapacity;
1259         BatteryStatus->Voltage = DeviceExtension->PresentVoltage;
1260         BatteryStatus->Rate = DeviceExtension->Rate;
1261     }
1262 
1263     /* Return status */
1264     if (CmBattDebug & (CMBATT_GENERIC_INFO))
1265         DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
1266                  BatteryStatus->PowerState,
1267                  BatteryStatus->Capacity,
1268                  BatteryStatus->Voltage,
1269                  BatteryStatus->Rate);
1270     return Status;
1271 }
1272 
1273 NTSTATUS
1274 NTAPI
1275 DriverEntry(IN PDRIVER_OBJECT DriverObject,
1276             IN PUNICODE_STRING RegistryPath)
1277 {
1278     NTSTATUS Status;
1279     PDRIVER_EXTENSION DriverExtension;
1280     OBJECT_ATTRIBUTES ObjectAttributes;
1281     UNICODE_STRING CallbackName;
1282 
1283     /* Allocate registry path */
1284     GlobalRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
1285     GlobalRegistryPath.Length = RegistryPath->Length;
1286     GlobalRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
1287                                                       GlobalRegistryPath.MaximumLength,
1288                                                       'MtaB');
1289     if (!GlobalRegistryPath.Buffer)
1290     {
1291         /* Fail if we're out of memory this early */
1292         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1293             DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
1294         return STATUS_INSUFFICIENT_RESOURCES;
1295     }
1296 
1297     /* Buffer allocated, copy the string */
1298     RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
1299     if (CmBattDebug & CMBATT_GENERIC_INFO)
1300         DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%wZ\"\n",
1301                  DriverObject,
1302                  RegistryPath);
1303 
1304     /* Setup the major dispatchers */
1305     DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
1306     DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
1307     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
1308     DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
1309     DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
1310     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
1311 
1312     /* And the unload routine */
1313     DriverObject->DriverUnload = CmBattUnload;
1314 
1315     /* And the add device routine */
1316     DriverExtension = DriverObject->DriverExtension;
1317     DriverExtension->AddDevice = CmBattAddDevice;
1318 
1319     /* Create a power callback */
1320     RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
1321     InitializeObjectAttributes(&ObjectAttributes,
1322                                &CallbackName,
1323                                OBJ_KERNEL_HANDLE,
1324                                NULL,
1325                                NULL);
1326     Status = ExCreateCallback(&CmBattPowerCallBackObject, &ObjectAttributes, 0, TRUE);
1327     if (!NT_SUCCESS(Status))
1328     {
1329         /* No callback, fail */
1330         CmBattPowerCallBackObject = 0;
1331         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1332             DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status);
1333     }
1334     else
1335     {
1336         /* Register the power callback now */
1337         CmBattPowerCallBackRegistration = ExRegisterCallback(CmBattPowerCallBackObject,
1338                                                              (PVOID)CmBattPowerCallBack,
1339                                                              DriverObject);
1340         if (CmBattPowerCallBackRegistration)
1341         {
1342             /* Last thing: setup our DPC and timer for battery wake */
1343             KeInitializeDpc(&CmBattWakeDpcObject, (PVOID)CmBattWakeDpc, DriverObject);
1344             KeInitializeTimer(&CmBattWakeDpcTimerObject);
1345         }
1346         else
1347         {
1348             ObDereferenceObject(CmBattPowerCallBackObject);
1349             if (CmBattDebug & CMBATT_GENERIC_WARNING)
1350                 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1351         }
1352 
1353         /* All good */
1354         Status = STATUS_SUCCESS;
1355     }
1356 
1357     /* Return failure or success */
1358     return Status;
1359 }
1360 
1361 /* EOF */
1362