xref: /reactos/drivers/bus/acpi/cmbatt/cmbatt.c (revision 81860b48)
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 NTSTATUS
277 NTAPI
CmBattVerifyStaticInfo(PCMBATT_DEVICE_EXTENSION DeviceExtension,ULONG BatteryTag)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
CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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
CmBattIoctl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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
CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,OUT PULONG Tag)566 CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
567                OUT PULONG Tag)
568 {
569     PDEVICE_OBJECT PdoDevice;
570     ULONG StaData;
571     NTSTATUS Status;
572     PAGED_CODE();
573     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
574       DbgPrint("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
575                 *Tag, DeviceExtension, DeviceExtension->DeviceId);
576 
577     /* Get PDO and clear notification flag */
578     PdoDevice = DeviceExtension->PdoDeviceObject;
579     DeviceExtension->NotifySent = 0;
580 
581     /* Get _STA from PDO (we need the machine status, not the battery status) */
582     Status = CmBattGetStaData(PdoDevice, &StaData);
583     if (NT_SUCCESS(Status))
584     {
585         /* Is a battery present? */
586         if (StaData & ACPI_STA_BATTERY_PRESENT)
587         {
588             /* Do we not have a tag yet? */
589             if (DeviceExtension->Tag == BATTERY_TAG_INVALID)
590             {
591                 /* Set the new tag value, reset tags if we reached the maximum */
592                 if (++DeviceExtension->TagData == BATTERY_TAG_INVALID)
593                     DeviceExtension->TagData = 1;
594                 DeviceExtension->Tag = DeviceExtension->TagData;
595                 if (CmBattDebug & CMBATT_GENERIC_INFO)
596                     DbgPrint("CmBattQueryTag - New Tag: (%d)\n", DeviceExtension->Tag);
597 
598                 /* Reset trip point data */
599                 DeviceExtension->TripPointOld = 0;
600                 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
601 
602                 /* Clear AR lock and set new interrupt time */
603                 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
604                 DeviceExtension->InterruptTime = KeQueryInterruptTime();
605             }
606         }
607         else
608         {
609             /* No battery, so no tag */
610             DeviceExtension->Tag = BATTERY_TAG_INVALID;
611             Status = STATUS_NO_SUCH_DEVICE;
612         }
613     }
614 
615     /* Return the tag and status result */
616     *Tag = DeviceExtension->Tag;
617     if (CmBattDebug & CMBATT_ACPI_WARNING)
618       DbgPrint("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n", *Tag, Status);
619     return Status;
620 }
621 
622 NTSTATUS
623 NTAPI
CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)624 CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)
625 {
626     NTSTATUS Status;
627     PAGED_CODE();
628     if (CmBattDebug & 0xA) DbgPrint("CmBattDisableStatusNotify\n");
629 
630     /* Do we have a trip point */
631     if (DeviceExtension->TripPointSet)
632     {
633         /* Is there a current value set? */
634         if (DeviceExtension->TripPointValue)
635         {
636             /* Reset it back to 0 */
637             DeviceExtension->TripPointValue = 0;
638             Status = CmBattSetTripPpoint(DeviceExtension, 0);
639             if (!NT_SUCCESS(Status))
640             {
641                 /* If it failed, set unknown/invalid value */
642                 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
643                 if (CmBattDebug & 8)
644                     DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status);
645             }
646         }
647         else
648         {
649             /* No trip point set, so this is a successful no-op */
650             Status = STATUS_SUCCESS;
651         }
652     }
653     else
654     {
655         /* Nothing we can do */
656         Status = STATUS_OBJECT_NAME_NOT_FOUND;
657     }
658 
659     /* Return status */
660     return Status;
661 }
662 
663 NTSTATUS
664 NTAPI
CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG BatteryTag,IN PBATTERY_NOTIFY BatteryNotify)665 CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
666                       IN ULONG BatteryTag,
667                       IN PBATTERY_NOTIFY BatteryNotify)
668 {
669     NTSTATUS Status;
670     ACPI_BST_DATA BstData;
671     ULONG Capacity, NewTripPoint, TripPoint, DesignVoltage;
672     BOOLEAN Charging;
673     PAGED_CODE();
674     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
675         DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
676                  BatteryTag, BatteryNotify->LowCapacity);
677 
678     /* Update any ACPI evaluations */
679     Status = CmBattVerifyStaticInfo(DeviceExtension, BatteryTag);
680     if (!NT_SUCCESS(Status)) return Status;
681 
682     /* Trip point not supported, fail */
683     if (!DeviceExtension->TripPointSet) return STATUS_OBJECT_NAME_NOT_FOUND;
684 
685     /* Are both capacities known? */
686     if ((BatteryNotify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
687         (BatteryNotify->LowCapacity == BATTERY_UNKNOWN_CAPACITY))
688     {
689         /* We can't set trip points without these */
690         if (CmBattDebug & CMBATT_GENERIC_WARNING)
691             DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
692         return STATUS_NOT_SUPPORTED;
693     }
694 
695     /* Is the battery charging? */
696     Charging = DeviceExtension->BstData.State & ACPI_BATT_STAT_CHARGING;
697     if (Charging)
698     {
699         /* Then the trip point is when we hit the cap */
700         Capacity = BatteryNotify->HighCapacity;
701         NewTripPoint = BatteryNotify->HighCapacity;
702     }
703     else
704     {
705         /* Otherwise it's when we discharge to the bottom */
706         Capacity = BatteryNotify->LowCapacity;
707         NewTripPoint = BatteryNotify->LowCapacity;
708     }
709 
710     /* Do we have data in Amps or Watts? */
711     if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
712     {
713         /* We need the voltage to do the conversion */
714         DesignVoltage = DeviceExtension->BifData.DesignVoltage;
715         if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage))
716         {
717             /* Convert from mAh into Ah */
718             TripPoint = 1000 * NewTripPoint;
719             if (Charging)
720             {
721                 /* Scale the high trip point */
722                 NewTripPoint = (TripPoint + 500) / DesignVoltage + ((TripPoint + 500) % DesignVoltage != 0);
723             }
724             else
725             {
726                 /* Scale the low trip point */
727                 NewTripPoint = (TripPoint - 500) / DesignVoltage - ((TripPoint - 500) % DesignVoltage == 0);
728             }
729         }
730         else
731         {
732             /* Without knowing the voltage, Amps are not enough data on consumption */
733             Status = STATUS_NOT_SUPPORTED;
734             if (CmBattDebug & CMBATT_ACPI_WARNING)
735                 DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
736                         DesignVoltage);
737         }
738     }
739     else if (Charging)
740     {
741         /* Make it trip just one past the charge cap */
742         ++NewTripPoint;
743     }
744     else if (NewTripPoint > 0)
745     {
746         /* Make it trip just one below the drain cap */
747         --NewTripPoint;
748     }
749 
750     /* Do we actually have a new trip point? */
751     if (NewTripPoint == DeviceExtension->TripPointValue)
752     {
753         /* No, so there is no work to be done */
754         if (CmBattDebug & CMBATT_GENERIC_STATUS)
755             DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension->TripPointValue);
756         return STATUS_SUCCESS;
757     }
758 
759     /* Set the trip point with ACPI and check for success */
760     DeviceExtension->TripPointValue = NewTripPoint;
761     Status = CmBattSetTripPpoint(DeviceExtension, NewTripPoint);
762     if (!(NewTripPoint) && (Capacity)) Status = STATUS_NOT_SUPPORTED;
763     if (!NT_SUCCESS(Status))
764     {
765         /* We failed to set the trip point, or there wasn't one settable */
766         DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
767         if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
768             DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status);
769         return Status;
770     }
771 
772     /* Read the new BST data to see the latest state */
773     Status = CmBattGetBstData(DeviceExtension, &BstData);
774     if (!NT_SUCCESS(Status))
775     {
776         /* We'll return failure to the caller */
777         if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
778             DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status);
779     }
780     else if ((Charging) && (BstData.RemainingCapacity >= NewTripPoint))
781     {
782         /* We are charging and our capacity is past the trip point, so trip now */
783         if (CmBattDebug & CMBATT_GENERIC_WARNING)
784             DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
785                      NewTripPoint, BstData.RemainingCapacity);
786         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
787     }
788     else if ((BstData.RemainingCapacity) && (Capacity))
789     {
790         /* We are discharging, and our capacity is below the trip point, trip now */
791         if (CmBattDebug & CMBATT_GENERIC_WARNING)
792             DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
793                      NewTripPoint, BstData.RemainingCapacity);
794         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
795     }
796 
797     /* All should've went well if we got here, unless BST failed... return! */
798     if (CmBattDebug & CMBATT_GENERIC_STATUS)
799           DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
800                     Capacity, DeviceExtension->RemainingCapacity);
801     if (CmBattDebug & CMBATT_ACPI_WARNING)
802           DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %lx\n",
803                     BatteryNotify->PowerState,
804                     BatteryNotify->LowCapacity,
805                     BatteryNotify->HighCapacity,
806                     Status);
807     return Status;
808 }
809 
810 NTSTATUS
811 NTAPI
CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG Tag)812 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
813                        IN ULONG Tag)
814 {
815     ULONG PsrData = 0;
816     NTSTATUS Status;
817     ULONG BstState;
818     ULONG DesignVoltage, PresentRate, RemainingCapacity;
819     PAGED_CODE();
820     if (CmBattDebug & CMBATT_GENERIC_INFO)
821         DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension, Tag);
822 
823     /* Validate ACPI data */
824     Status = CmBattVerifyStaticInfo(DeviceExtension, Tag);
825     if (!NT_SUCCESS(Status)) return Status;
826 
827     /* Check for delayed status notifications */
828     if (DeviceExtension->DelayNotification)
829     {
830         /* Process them now and don't do any other work */
831         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
832         return Status;
833     }
834 
835     /* Get _BST from ACPI */
836     Status = CmBattGetBstData(DeviceExtension, &DeviceExtension->BstData);
837     if (!NT_SUCCESS(Status))
838     {
839         /* Fail */
840         InterlockedExchange(&DeviceExtension->ArLockValue, 0);
841         return Status;
842     }
843 
844     /* Clear current BST information */
845     DeviceExtension->State = 0;
846     DeviceExtension->RemainingCapacity = 0;
847     DeviceExtension->PresentVoltage = 0;
848     DeviceExtension->Rate = 0;
849 
850     /* Get battery state */
851     BstState = DeviceExtension->BstData.State;
852 
853     /* Is the battery both charging and discharging? */
854     if ((BstState & ACPI_BATT_STAT_DISCHARG) && (BstState & ACPI_BATT_STAT_CHARGING) &&
855         (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
856             DbgPrint("************************ ACPI BIOS BUG ********************\n* "
857                      "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
858                      "* One battery cannot be charging and discharging at the same time.\n",
859                      BstState);
860 
861     /* Is the battery discharging? */
862     if (BstState & ACPI_BATT_STAT_DISCHARG)
863     {
864         /* Set power state and check if it just started discharging now */
865         DeviceExtension->State |= BATTERY_DISCHARGING;
866         if (!(DeviceExtension->State & ACPI_BATT_STAT_DISCHARG))
867         {
868             /* Remember the time when the state changed */
869             DeviceExtension->InterruptTime = KeQueryInterruptTime();
870         }
871     }
872     else if (BstState & ACPI_BATT_STAT_CHARGING)
873     {
874         /* Battery is charging, update power state */
875         DeviceExtension->State |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
876     }
877 
878     /* Is the battery in a critical state? */
879     if (BstState & ACPI_BATT_STAT_CRITICAL) DeviceExtension->State |= BATTERY_CRITICAL;
880 
881     /* Read the voltage data */
882     DeviceExtension->PresentVoltage = DeviceExtension->BstData.PresentVoltage;
883 
884     /* Check if we have an A/C adapter */
885     if (AcAdapterPdo)
886     {
887         /* Query information on it */
888         CmBattGetPsrData(AcAdapterPdo, &PsrData);
889     }
890     else
891     {
892         /* Otherwise, check if the battery is charging */
893         if (BstState & ACPI_BATT_STAT_CHARGING)
894         {
895             /* Then we'll assume there's a charger */
896             PsrData = 1;
897         }
898         else
899         {
900             /* Assume no charger */
901             PsrData = 0;
902         }
903     }
904 
905     /* Is there a charger? */
906     if (PsrData)
907     {
908         /* Set the power state flag to reflect this */
909         DeviceExtension->State |= BATTERY_POWER_ON_LINE;
910         if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
911             DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
912     }
913     else if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
914     {
915         DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
916     }
917 
918     /* Get some data we'll need */
919     DesignVoltage = DeviceExtension->BifData.DesignVoltage;
920     PresentRate = DeviceExtension->BstData.PresentRate;
921     RemainingCapacity = DeviceExtension->BstData.RemainingCapacity;
922 
923     /* Check if we have battery data in Watts instead of Amps */
924     if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_WATTS)
925     {
926         /* Get the data from the BST */
927         DeviceExtension->RemainingCapacity = RemainingCapacity;
928         DeviceExtension->Rate = PresentRate;
929 
930         /* Check if the rate is invalid */
931         if (PresentRate > CM_MAX_VALUE)
932         {
933             /* Set an unknown rate and don't touch the old value */
934             DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
935             if ((PresentRate != CM_UNKNOWN_VALUE) && (CmBattDebug & CMBATT_ACPI_WARNING))
936             {
937                 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
938                 DbgPrint("----------------------   PresentRate = 0x%08x\n", PresentRate);
939             }
940         }
941     }
942     else if ((DesignVoltage != CM_UNKNOWN_VALUE) && (DesignVoltage))
943     {
944         /* We have voltage data, what about capacity? */
945         if (RemainingCapacity == CM_UNKNOWN_VALUE)
946         {
947             /* Unable to calculate it */
948             DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
949             if (CmBattDebug & CMBATT_ACPI_WARNING)
950             {
951                 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
952                 DbgPrint("----------------------   RemainingCapacity = CM_UNKNOWN_VALUE\n");
953             }
954         }
955         else
956         {
957             /* Compute the capacity with the information we have */
958             DeviceExtension->RemainingCapacity = (DesignVoltage * RemainingCapacity + 500) / 1000;
959         }
960 
961         /* Check if we have a rate */
962         if (PresentRate != CM_UNKNOWN_VALUE)
963         {
964             /* Make sure the rate isn't too large */
965             if (PresentRate > (-500 / DesignVoltage))
966             {
967                 /* It is, so set unknown state */
968                 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
969                 if (CmBattDebug & CMBATT_ACPI_WARNING)
970                 {
971                     DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
972                     DbgPrint("----------------------   Overflow: PresentRate = 0x%08x\n", PresentRate);
973                 }
974             }
975 
976             /* Compute the rate */
977             DeviceExtension->Rate = (PresentRate * DesignVoltage + 500) / 1000;
978         }
979         else
980         {
981             /* We don't have a rate, so set unknown value */
982             DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
983             if (CmBattDebug & CMBATT_ACPI_WARNING)
984             {
985                 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
986                 DbgPrint("----------------------   Present Rate = CM_UNKNOWN_VALUE\n");
987             }
988         }
989     }
990     else
991     {
992         /* We have no rate, and no capacity, set unknown values */
993         DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
994         DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
995         if (CmBattDebug & CMBATT_ACPI_WARNING)
996         {
997             DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
998             DbgPrint("----------------------   DesignVoltage = 0x%08x\n", DesignVoltage);
999         }
1000     }
1001 
1002     /* Check if we have an unknown rate */
1003     if (DeviceExtension->Rate == BATTERY_UNKNOWN_RATE)
1004     {
1005         /* The battery is discharging but we don't know by how much... this is bad! */
1006         if ((BstState & ACPI_BATT_STAT_DISCHARG) &&
1007             (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
1008             DbgPrint("CmBattGetBatteryStatus: battery rate is unknown when battery is not charging!\n");
1009     }
1010     else if (DeviceExtension->State & BATTERY_DISCHARGING)
1011     {
1012         /* The battery is discharging, so treat the rate as a negative rate */
1013         DeviceExtension->Rate = -(LONG)DeviceExtension->Rate;
1014     }
1015     else if (!(DeviceExtension->State & BATTERY_CHARGING) && (DeviceExtension->Rate))
1016     {
1017         /* We are not charging, not discharging, but have a rate? Ignore it! */
1018         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1019             DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
1020                      DeviceExtension->Rate);
1021         DeviceExtension->Rate = 0;
1022     }
1023 
1024     /* Done */
1025     return STATUS_SUCCESS;
1026 }
1027 
1028 NTSTATUS
1029 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)1030 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,
1031                        IN ULONG Tag,
1032                        IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
1033                        IN OPTIONAL LONG AtRate,
1034                        IN PVOID Buffer,
1035                        IN ULONG BufferLength,
1036                        OUT PULONG ReturnedLength)
1037 {
1038     NTSTATUS Status;
1039     PVOID QueryData = NULL;
1040     ULONG QueryLength = 0;
1041     ULONG RemainingTime = 0;
1042     ANSI_STRING TempString;
1043     UNICODE_STRING TempString2;
1044     WCHAR InfoBuffer[256];
1045     WCHAR TempBuffer[256];
1046     UNICODE_STRING InfoString;
1047     ULONG RemainingCapacity;
1048     BATTERY_REPORTING_SCALE BatteryReportingScale[2];
1049     LONG Rate;
1050     PAGED_CODE();
1051     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1052         DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
1053                  Tag,
1054                  FdoExtension->DeviceId,
1055                  InfoLevel);
1056 
1057     /* Check ACPI Data */
1058     Status = CmBattVerifyStaticInfo(FdoExtension, Tag);
1059     if (!NT_SUCCESS(Status)) return Status;
1060 
1061     /* Check what caller wants */
1062     switch (InfoLevel)
1063     {
1064         case BatteryInformation:
1065             /* Just return our static information */
1066             QueryData = &FdoExtension->BatteryInformation;
1067             QueryLength = sizeof(BATTERY_INFORMATION);
1068             break;
1069 
1070         case BatteryGranularityInformation:
1071 
1072             /* Return our static information, we have two scales */
1073             BatteryReportingScale[0].Granularity = FdoExtension->BatteryCapacityGranularity1;
1074             BatteryReportingScale[0].Capacity = FdoExtension->BatteryInformation.DefaultAlert1;
1075             BatteryReportingScale[1].Granularity = FdoExtension->BatteryCapacityGranularity2;
1076             BatteryReportingScale[1].Capacity = FdoExtension->BatteryInformation.DesignedCapacity;
1077             QueryData = BatteryReportingScale;
1078             QueryLength = sizeof(BATTERY_REPORTING_SCALE) * 2;
1079             break;
1080 
1081         case BatteryEstimatedTime:
1082 
1083             /* Check if it's been more than 2 1/2 minutes since the last change */
1084             if ((KeQueryInterruptTime() - 150000000) > (FdoExtension->InterruptTime))
1085             {
1086                 /* Get new battery status */
1087                 CmBattGetBatteryStatus(FdoExtension, FdoExtension->Tag);
1088 
1089                 /* If the caller didn't specify a rate, use our static one */
1090                 Rate = AtRate;
1091                 if (!Rate) Rate = FdoExtension->Rate;
1092 
1093                 /* If we don't have a valid negative rate, use unknown value */
1094                 if (Rate >= 0) Rate = BATTERY_UNKNOWN_RATE;
1095 
1096                 /* Grab the remaining capacity */
1097                 RemainingCapacity = FdoExtension->RemainingCapacity;
1098 
1099                 /* See if we don't know one or the other */
1100                 if ((Rate == BATTERY_UNKNOWN_RATE) ||
1101                     (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY))
1102                 {
1103                     /* If the battery is discharging, we can't give out a time */
1104                     if ((FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG) &&
1105                         (CmBattDebug & CMBATT_GENERIC_WARNING))
1106                             DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
1107 
1108                     /* Check if we don't have a rate and capacity is going down */
1109                     if ((FdoExtension->Rate == BATTERY_UNKNOWN_RATE) &&
1110                         (FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG))
1111                     {
1112                         /* We have to fail, since we lack data */
1113                         Status = STATUS_INVALID_DEVICE_REQUEST;
1114                         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1115                             DbgPrint("----------------------   PresentRate = BATTERY_UNKNOWN_RATE\n");
1116                     }
1117 
1118                     /* If we don't have capacity, the rate is useless */
1119                     if (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY)
1120                     {
1121                         /* We have to fail the request */
1122                         Status = STATUS_INVALID_DEVICE_REQUEST;
1123                         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1124                             DbgPrint("----------------------   RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
1125                     }
1126                 }
1127                 else
1128                 {
1129                     /* We have data, but is it valid? */
1130                     if (RemainingCapacity > 0x123456)
1131                     {
1132                         /* The capacity seems bogus, so don't use it */
1133                         if (CmBattDebug & CMBATT_ACPI_WARNING)
1134                             DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
1135                     }
1136                     else
1137                     {
1138                         /* Compute the remaining time in seconds, based on rate */
1139                         RemainingTime = (RemainingCapacity * 3600) / -Rate;
1140                     }
1141                 }
1142             }
1143 
1144             /* Return the remaining time */
1145             QueryData = &RemainingTime;
1146             QueryLength = sizeof(ULONG);
1147             break;
1148 
1149         case BatteryDeviceName:
1150 
1151             /* Build the model number string */
1152             RtlInitAnsiString(&TempString, FdoExtension->BifData.ModelNumber);
1153 
1154             /* Convert it to Unicode */
1155             InfoString.Buffer = InfoBuffer;
1156             InfoString.MaximumLength = sizeof(InfoBuffer);
1157             Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1158 
1159             /* Return the unicode buffer */
1160             QueryData = InfoString.Buffer;
1161             QueryLength = InfoString.Length;
1162             break;
1163 
1164         case BatteryTemperature:
1165         case BatteryManufactureDate:
1166 
1167             /* We don't support these */
1168             Status = STATUS_INVALID_DEVICE_REQUEST;
1169             break;
1170 
1171         case BatteryManufactureName:
1172 
1173             /* Build the OEM info string */
1174             RtlInitAnsiString(&TempString, FdoExtension->BifData.OemInfo);
1175 
1176             /* Convert it to Unicode */
1177             InfoString.Buffer = InfoBuffer;
1178             InfoString.MaximumLength = sizeof(InfoBuffer);
1179             Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1180 
1181             /* Return the unicode buffer */
1182             QueryData = InfoString.Buffer;
1183             QueryLength = InfoString.Length;
1184             break;
1185 
1186         case BatteryUniqueID:
1187 
1188             /* Build the serial number string */
1189             RtlInitAnsiString(&TempString, FdoExtension->BifData.SerialNumber);
1190 
1191             /* Convert it to Unicode */
1192             InfoString.Buffer = InfoBuffer;
1193             InfoString.MaximumLength = sizeof(InfoBuffer);
1194             RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1195 
1196             /* Setup a temporary string for concatenation */
1197             TempString2.Buffer = TempBuffer;
1198             TempString2.MaximumLength = sizeof(TempBuffer);
1199 
1200             /* Check if there's an OEM string */
1201             if (FdoExtension->BifData.OemInfo[0])
1202             {
1203                 /* Build the OEM info string */
1204                 RtlInitAnsiString(&TempString, FdoExtension->BifData.OemInfo);
1205 
1206                 /* Convert it to Unicode and append it */
1207                 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1208                 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1209             }
1210 
1211             /* Build the model number string */
1212             RtlInitAnsiString(&TempString, FdoExtension->BifData.ModelNumber);
1213 
1214             /* Convert it to Unicode and append it */
1215             RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1216             RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1217 
1218             /* Return the final appended string */
1219             QueryData = InfoString.Buffer;
1220             QueryLength = InfoString.Length;
1221             break;
1222 
1223         default:
1224 
1225             /* Everything else is unknown */
1226             Status = STATUS_INVALID_PARAMETER;
1227             break;
1228     }
1229 
1230     /* Return the required length and check if the caller supplied enough */
1231     *ReturnedLength = QueryLength;
1232     if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
1233 
1234     /* Copy the data if there's enough space and it exists */
1235     if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
1236 
1237     /* Return function result */
1238     return Status;
1239 }
1240 
1241 NTSTATUS
1242 NTAPI
CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG Tag,IN PBATTERY_STATUS BatteryStatus)1243 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
1244                   IN ULONG Tag,
1245                   IN PBATTERY_STATUS BatteryStatus)
1246 {
1247     NTSTATUS Status;
1248     PAGED_CODE();
1249     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1250         DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag, DeviceExtension->DeviceId);
1251 
1252     /* Query ACPI information */
1253     Status = CmBattGetBatteryStatus(DeviceExtension, Tag);
1254     if (NT_SUCCESS(Status))
1255     {
1256         BatteryStatus->PowerState = DeviceExtension->State;
1257         BatteryStatus->Capacity = DeviceExtension->RemainingCapacity;
1258         BatteryStatus->Voltage = DeviceExtension->PresentVoltage;
1259         BatteryStatus->Rate = DeviceExtension->Rate;
1260     }
1261 
1262     /* Return status */
1263     if (CmBattDebug & (CMBATT_GENERIC_INFO))
1264         DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
1265                  BatteryStatus->PowerState,
1266                  BatteryStatus->Capacity,
1267                  BatteryStatus->Voltage,
1268                  BatteryStatus->Rate);
1269     return Status;
1270 }
1271 
1272 NTSTATUS
1273 NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)1274 DriverEntry(IN PDRIVER_OBJECT DriverObject,
1275             IN PUNICODE_STRING RegistryPath)
1276 {
1277     NTSTATUS Status;
1278     PDRIVER_EXTENSION DriverExtension;
1279     OBJECT_ATTRIBUTES ObjectAttributes;
1280     UNICODE_STRING CallbackName;
1281 
1282     /* Allocate registry path */
1283     GlobalRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
1284     GlobalRegistryPath.Length = RegistryPath->Length;
1285     GlobalRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
1286                                                       GlobalRegistryPath.MaximumLength,
1287                                                       'MtaB');
1288     if (!GlobalRegistryPath.Buffer)
1289     {
1290         /* Fail if we're out of memory this early */
1291         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1292             DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
1293         return STATUS_INSUFFICIENT_RESOURCES;
1294     }
1295 
1296     /* Buffer allocated, copy the string */
1297     RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
1298     if (CmBattDebug & CMBATT_GENERIC_INFO)
1299         DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%wZ\"\n",
1300                  DriverObject,
1301                  RegistryPath);
1302 
1303     /* Setup the major dispatchers */
1304     DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
1305     DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
1306     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
1307     DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
1308     DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
1309     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
1310 
1311     /* And the unload routine */
1312     DriverObject->DriverUnload = CmBattUnload;
1313 
1314     /* And the add device routine */
1315     DriverExtension = DriverObject->DriverExtension;
1316     DriverExtension->AddDevice = CmBattAddDevice;
1317 
1318     /* Create a power callback */
1319     RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
1320     InitializeObjectAttributes(&ObjectAttributes,
1321                                &CallbackName,
1322                                OBJ_KERNEL_HANDLE,
1323                                NULL,
1324                                NULL);
1325     Status = ExCreateCallback(&CmBattPowerCallBackObject, &ObjectAttributes, 0, TRUE);
1326     if (!NT_SUCCESS(Status))
1327     {
1328         /* No callback, fail */
1329         CmBattPowerCallBackObject = 0;
1330         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1331             DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status);
1332     }
1333     else
1334     {
1335         /* Register the power callback now */
1336         CmBattPowerCallBackRegistration = ExRegisterCallback(CmBattPowerCallBackObject,
1337                                                              (PVOID)CmBattPowerCallBack,
1338                                                              DriverObject);
1339         if (CmBattPowerCallBackRegistration)
1340         {
1341             /* Last thing: setup our DPC and timer for battery wake */
1342             KeInitializeDpc(&CmBattWakeDpcObject, (PVOID)CmBattWakeDpc, DriverObject);
1343             KeInitializeTimer(&CmBattWakeDpcTimerObject);
1344         }
1345         else
1346         {
1347             ObDereferenceObject(CmBattPowerCallBackObject);
1348             if (CmBattDebug & CMBATT_GENERIC_WARNING)
1349                 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1350         }
1351 
1352         /* All good */
1353         Status = STATUS_SUCCESS;
1354     }
1355 
1356     /* Return failure or success */
1357     return Status;
1358 }
1359 
1360 /* EOF */
1361