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