xref: /reactos/drivers/bus/acpi/cmbatt/cmbatt.c (revision 81db5e1d)
1 /*
2  * PROJECT:         ReactOS ACPI-Compliant Control Method Battery
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            boot/drivers/bus/acpi/cmbatt/cmbatt.c
5  * PURPOSE:         Main Initialization Code and IRP Handling
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "cmbatt.h"
12 
13 #include <debug.h>
14 
15 /* GLOBALS ********************************************************************/
16 
17 ULONG CmBattDebug;
18 PCALLBACK_OBJECT CmBattPowerCallBackObject;
19 PVOID CmBattPowerCallBackRegistration;
20 UNICODE_STRING GlobalRegistryPath;
21 KTIMER CmBattWakeDpcTimerObject;
22 KDPC CmBattWakeDpcObject;
23 PDEVICE_OBJECT AcAdapterPdo;
24 LARGE_INTEGER CmBattWakeDpcDelay;
25 
26 /* FUNCTIONS ******************************************************************/
27 
28 VOID
29 NTAPI
30 CmBattPowerCallBack(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
31                     IN ULONG Action,
32                     IN ULONG Value)
33 {
34     BOOLEAN Cancelled;
35     PDEVICE_OBJECT DeviceObject;
36     if (CmBattDebug & 0x10)
37         DbgPrint("CmBattPowerCallBack: action: %d, value: %d \n", Action, Value);
38 
39     /* Check if a transition is going to happen */
40     if (Action == PO_CB_SYSTEM_STATE_LOCK)
41     {
42         /* We have just re-entered S0: call the wake DPC in 10 seconds */
43         if (Value == 1)
44         {
45             if (CmBattDebug & 0x10)
46                 DbgPrint("CmBattPowerCallBack: Calling CmBattWakeDpc after 10 seconds.\n");
47             Cancelled = KeSetTimer(&CmBattWakeDpcTimerObject, CmBattWakeDpcDelay, &CmBattWakeDpcObject);
48             if (CmBattDebug & 0x10)
49                 DbgPrint("CmBattPowerCallBack: timerCanceled = %d.\n", Cancelled);
50         }
51         else if (Value == 0)
52         {
53             /* We are exiting the S0 state: loop all devices to set the delay flag */
54             if (CmBattDebug & 0x10)
55                 DbgPrint("CmBattPowerCallBack: Delaying Notifications\n");
56             for (DeviceObject = DeviceExtension->DeviceObject;
57                  DeviceObject;
58                  DeviceObject = DeviceObject->NextDevice)
59             {
60                 /* Set the delay flag */
61                 DeviceExtension = DeviceObject->DeviceExtension;
62                 DeviceExtension->DelayNotification = TRUE;
63             }
64         }
65         else if (CmBattDebug & 0x10)
66         {
67             /* Unknown value */
68             DbgPrint("CmBattPowerCallBack: unknown argument2 = %08x\n", Value);
69         }
70     }
71 }
72 
73 VOID
74 NTAPI
75 CmBattWakeDpc(IN PKDPC Dpc,
76               IN PCMBATT_DEVICE_EXTENSION FdoExtension,
77               IN PVOID SystemArgument1,
78               IN PVOID SystemArgument2)
79 {
80     PDEVICE_OBJECT CurrentObject;
81     BOOLEAN AcNotify = FALSE;
82     PCMBATT_DEVICE_EXTENSION DeviceExtension;
83     ULONG ArFlag;
84     if (CmBattDebug & 2) DbgPrint("CmBattWakeDpc: Entered.\n");
85 
86     /* Loop all device objects */
87     for (CurrentObject = FdoExtension->DeviceObject;
88          CurrentObject;
89          CurrentObject = CurrentObject->NextDevice)
90     {
91         /* Turn delay flag off, we're back in S0 */
92         DeviceExtension = CurrentObject->DeviceExtension;
93         DeviceExtension->DelayNotification = 0;
94 
95         /* Check if this is an AC adapter */
96         if (DeviceExtension->FdoType == CmBattAcAdapter)
97         {
98             /* Was there a pending notify? */
99             if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
100             {
101                 /* We'll send a notify on the next pass */
102                 AcNotify = TRUE;
103                 DeviceExtension->ArFlag = 0;
104                 if (CmBattDebug & 0x20)
105                     DbgPrint("CmBattWakeDpc: AC adapter notified\n");
106             }
107         }
108     }
109 
110     /* Loop the device objects again */
111     for (CurrentObject = FdoExtension->DeviceObject;
112          CurrentObject;
113          CurrentObject = CurrentObject->NextDevice)
114     {
115         /* Check if this is a battery */
116         DeviceExtension = CurrentObject->DeviceExtension;
117         if (DeviceExtension->FdoType == CmBattBattery)
118         {
119             /* Check what ARs are pending */
120             ArFlag = DeviceExtension->ArFlag;
121             if (CmBattDebug & 0x20)
122                 DbgPrint("CmBattWakeDpc: Performing delayed ARs: %01x\n", ArFlag);
123 
124             /* Insert notification, clear the lock value */
125             if (ArFlag & CMBATT_AR_INSERT) InterlockedExchange(&DeviceExtension->ArLockValue, 0);
126 
127             /* Removal, clear the battery tag */
128             if (ArFlag & CMBATT_AR_REMOVE) DeviceExtension->Tag = 0;
129 
130             /* Notification (or AC/DC adapter change from first pass above) */
131             if ((ArFlag & CMBATT_AR_NOTIFY) || (AcNotify))
132             {
133                 /* Notify the class driver */
134                 BatteryClassStatusNotify(DeviceExtension->ClassData);
135             }
136         }
137     }
138 }
139 
140 VOID
141 NTAPI
142 CmBattNotifyHandler(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
143                     IN ULONG NotifyValue)
144 {
145     ULONG ArFlag;
146     PCMBATT_DEVICE_EXTENSION FdoExtension;
147     PDEVICE_OBJECT DeviceObject;
148 
149     if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_PNP_INFO))
150         DbgPrint("CmBattNotifyHandler: CmBatt 0x%08x Type %d Number %d Notify Value: %x\n",
151                  DeviceExtension,
152                  DeviceExtension->FdoType,
153                  DeviceExtension->DeviceId,
154                  NotifyValue);
155 
156     /* Check what kind of notification was received */
157     switch (NotifyValue)
158     {
159         /* ACPI Specification says is sends a "Bus Check" when power source changes */
160         case ACPI_BUS_CHECK:
161 
162             /* We treat it as possible physical change */
163             DeviceExtension->ArFlag |= (CMBATT_AR_NOTIFY | CMBATT_AR_INSERT);
164             if ((DeviceExtension->Tag) &&
165                 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
166                 DbgPrint("CmBattNotifyHandler: Received battery #%x insertion, but tag was not invalid.\n",
167                          DeviceExtension->DeviceId);
168             break;
169 
170         /* Status of the battery has changed */
171         case ACPI_BATT_NOTIFY_STATUS:
172 
173             /* All we'll do is notify the class driver */
174             DeviceExtension->ArFlag |= CMBATT_AR_NOTIFY;
175             break;
176 
177         /* Information on the battery has changed, such as physical presence */
178         case ACPI_DEVICE_CHECK:
179         case ACPI_BATT_NOTIFY_INFO:
180 
181             /* Reset all state and let the class driver re-evaluate it all */
182             DeviceExtension->ArFlag |= (CMBATT_AR_NOTIFY |
183                                         CMBATT_AR_INSERT |
184                                         CMBATT_AR_REMOVE);
185             break;
186 
187         default:
188 
189             if (CmBattDebug & CMBATT_PNP_INFO)
190                 DbgPrint("CmBattNotifyHandler: Unknown Notify Value: %x\n", NotifyValue);
191     }
192 
193     /* Check if we're supposed to delay the notification till later */
194     if (DeviceExtension->DelayNotification)
195     {
196         /* We'll handle this when we get a status query later on */
197         if (CmBattDebug & CMBATT_PNP_INFO)
198             DbgPrint("CmBattNotifyHandler: Notification delayed: ARs = %01x\n",
199                       DeviceExtension->ArFlag);
200         return;
201     }
202 
203     /* We're going to handle this now */
204     if (CmBattDebug & CMBATT_PNP_INFO)
205         DbgPrint("CmBattNotifyHandler: Performing ARs: %01x\n", DeviceExtension->ArFlag);
206 
207     /* Check if this is a battery or AC adapter notification */
208     if (DeviceExtension->FdoType == CmBattBattery)
209     {
210         /* Reset the current trip point */
211         DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
212 
213         /* Check what ARs have to be done */
214         ArFlag = DeviceExtension->ArFlag;
215 
216         /* New battery inserted, reset lock value */
217         if (ArFlag & CMBATT_AR_INSERT) InterlockedExchange(&DeviceExtension->ArLockValue, 0);
218 
219         /* Check if the battery may have been removed */
220         if (ArFlag & CMBATT_AR_REMOVE) DeviceExtension->Tag = 0;
221 
222         /* Check if there's been any sort of change to the battery */
223         if (ArFlag & CMBATT_AR_NOTIFY)
224         {
225             /* We'll probably end up re-evaluating _BIF and _BST */
226             DeviceExtension->NotifySent = TRUE;
227             BatteryClassStatusNotify(DeviceExtension->ClassData);
228         }
229     }
230     else if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
231     {
232         /* The only known notification is AC/DC change. Loop device objects. */
233         for (DeviceObject = DeviceExtension->FdoDeviceObject->DriverObject->DeviceObject;
234              DeviceObject;
235              DeviceObject = DeviceObject->NextDevice)
236         {
237             /* Is this a battery? */
238             FdoExtension = DeviceObject->DeviceExtension;
239             if (FdoExtension->FdoType == CmBattBattery)
240             {
241                 /* Send a notification to the class driver */
242                 FdoExtension->NotifySent = TRUE;
243                 BatteryClassStatusNotify(FdoExtension->ClassData);
244             }
245         }
246     }
247 
248     /* ARs have been processed */
249     DeviceExtension->ArFlag = 0;
250 }
251 
252 VOID
253 NTAPI
254 CmBattUnload(IN PDRIVER_OBJECT DriverObject)
255 {
256     if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattUnload: \n");
257 
258     /* Check if we have a registered power callback */
259     if (CmBattPowerCallBackObject)
260     {
261         /* Get rid of it */
262         ExUnregisterCallback(CmBattPowerCallBackRegistration);
263         ObDereferenceObject(CmBattPowerCallBackObject);
264     }
265 
266     /* Free the registry buffer if it exists */
267     if (GlobalRegistryPath.Buffer) ExFreePool(GlobalRegistryPath.Buffer);
268 
269     /* Make sure we don't still have references to the DO */
270     if ((DriverObject->DeviceObject) && (CmBattDebug & CMBATT_GENERIC_WARNING))
271     {
272         DbgPrint("Unload called before all devices removed.\n");
273     }
274 }
275 
276 NTSTATUS
277 NTAPI
278 CmBattVerifyStaticInfo(PCMBATT_DEVICE_EXTENSION DeviceExtension,
279                        ULONG BatteryTag)
280 {
281     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 %lx\n",
774                     BatteryNotify->PowerState,
775                     BatteryNotify->LowCapacity,
776                     BatteryNotify->HighCapacity,
777                     Status);
778     return Status;
779 }
780 
781 NTSTATUS
782 NTAPI
783 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
784                        IN ULONG Tag)
785 {
786     ULONG PsrData = 0;
787     NTSTATUS Status;
788     ULONG BstState;
789     ULONG DesignVoltage, PresentRate, RemainingCapacity;
790     PAGED_CODE();
791     if (CmBattDebug & CMBATT_GENERIC_INFO)
792         DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension, Tag);
793 
794     /* Validate ACPI data */
795     Status = CmBattVerifyStaticInfo(DeviceExtension, Tag);
796     if (!NT_SUCCESS(Status)) return Status;
797 
798     /* Check for delayed status notifications */
799     if (DeviceExtension->DelayNotification)
800     {
801         /* Process them now and don't do any other work */
802         CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
803         return Status;
804     }
805 
806     /* Get _BST from ACPI */
807     Status = CmBattGetBstData(DeviceExtension, &DeviceExtension->BstData);
808     if (!NT_SUCCESS(Status))
809     {
810         /* Fail */
811         InterlockedExchange(&DeviceExtension->ArLockValue, 0);
812         return Status;
813     }
814 
815     /* Clear current BST information */
816     DeviceExtension->State = 0;
817     DeviceExtension->RemainingCapacity = 0;
818     DeviceExtension->PresentVoltage = 0;
819     DeviceExtension->Rate = 0;
820 
821     /* Get battery state */
822     BstState = DeviceExtension->BstData.State;
823 
824     /* Is the battery both charging and discharging? */
825     if ((BstState & ACPI_BATT_STAT_DISCHARG) && (BstState & ACPI_BATT_STAT_CHARGING) &&
826         (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
827             DbgPrint("************************ ACPI BIOS BUG ********************\n* "
828                      "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
829                      "* One battery cannot be charging and discharging at the same time.\n",
830                      BstState);
831 
832     /* Is the battery discharging? */
833     if (BstState & ACPI_BATT_STAT_DISCHARG)
834     {
835         /* Set power state and check if it just started discharging now */
836         DeviceExtension->State |= BATTERY_DISCHARGING;
837         if (!(DeviceExtension->State & ACPI_BATT_STAT_DISCHARG))
838         {
839             /* Remember the time when the state changed */
840             DeviceExtension->InterruptTime = KeQueryInterruptTime();
841         }
842     }
843     else if (BstState & ACPI_BATT_STAT_CHARGING)
844     {
845         /* Battery is charging, update power state */
846         DeviceExtension->State |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
847     }
848 
849     /* Is the battery in a critical state? */
850     if (BstState & ACPI_BATT_STAT_CRITICAL) DeviceExtension->State |= BATTERY_CRITICAL;
851 
852     /* Read the voltage data */
853     DeviceExtension->PresentVoltage = DeviceExtension->BstData.PresentVoltage;
854 
855     /* Check if we have an A/C adapter */
856     if (AcAdapterPdo)
857     {
858         /* Query information on it */
859         CmBattGetPsrData(AcAdapterPdo, &PsrData);
860     }
861     else
862     {
863         /* Otherwise, check if the battery is charging */
864         if (BstState & ACPI_BATT_STAT_CHARGING)
865         {
866             /* Then we'll assume there's a charger */
867             PsrData = 1;
868         }
869         else
870         {
871             /* Assume no charger */
872             PsrData = 0;
873         }
874     }
875 
876     /* Is there a charger? */
877     if (PsrData)
878     {
879         /* Set the power state flag to reflect this */
880         DeviceExtension->State |= BATTERY_POWER_ON_LINE;
881         if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
882             DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
883     }
884     else if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
885     {
886         DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
887     }
888 
889     /* Get some data we'll need */
890     DesignVoltage = DeviceExtension->BifData.DesignVoltage;
891     PresentRate = DeviceExtension->BstData.PresentRate;
892     RemainingCapacity = DeviceExtension->BstData.RemainingCapacity;
893 
894     /* Check if we have battery data in Watts instead of Amps */
895     if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_WATTS)
896     {
897         /* Get the data from the BST */
898         DeviceExtension->RemainingCapacity = RemainingCapacity;
899         DeviceExtension->Rate = PresentRate;
900 
901         /* Check if the rate is invalid */
902         if (PresentRate > CM_MAX_VALUE)
903         {
904             /* Set an unknown rate and don't touch the old value */
905             DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
906             if ((PresentRate != CM_UNKNOWN_VALUE) && (CmBattDebug & CMBATT_ACPI_WARNING))
907             {
908                 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
909                 DbgPrint("----------------------   PresentRate = 0x%08x\n", PresentRate);
910             }
911         }
912     }
913     else if ((DesignVoltage != CM_UNKNOWN_VALUE) && (DesignVoltage))
914     {
915         /* We have voltage data, what about capacity? */
916         if (RemainingCapacity == CM_UNKNOWN_VALUE)
917         {
918             /* Unable to calculate it */
919             DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
920             if (CmBattDebug & CMBATT_ACPI_WARNING)
921             {
922                 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
923                 DbgPrint("----------------------   RemainingCapacity = CM_UNKNOWN_VALUE\n");
924             }
925         }
926         else
927         {
928             /* Compute the capacity with the information we have */
929             DeviceExtension->RemainingCapacity = (DesignVoltage * RemainingCapacity + 500) / 1000;
930         }
931 
932         /* Check if we have a rate */
933         if (PresentRate != CM_UNKNOWN_VALUE)
934         {
935             /* Make sure the rate isn't too large */
936             if (PresentRate > (-500 / DesignVoltage))
937             {
938                 /* It is, so set unknown state */
939                 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
940                 if (CmBattDebug & CMBATT_ACPI_WARNING)
941                 {
942                     DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
943                     DbgPrint("----------------------   Overflow: PresentRate = 0x%08x\n", PresentRate);
944                 }
945             }
946 
947             /* Compute the rate */
948             DeviceExtension->Rate = (PresentRate * DesignVoltage + 500) / 1000;
949         }
950         else
951         {
952             /* We don't have a rate, so set unknown value */
953             DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
954             if (CmBattDebug & CMBATT_ACPI_WARNING)
955             {
956                 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
957                 DbgPrint("----------------------   Present Rate = CM_UNKNOWN_VALUE\n");
958             }
959         }
960     }
961     else
962     {
963         /* We have no rate, and no capacity, set unknown values */
964         DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
965         DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
966         if (CmBattDebug & CMBATT_ACPI_WARNING)
967         {
968             DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
969             DbgPrint("----------------------   DesignVoltage = 0x%08x\n", DesignVoltage);
970         }
971     }
972 
973     /* Check if we have an unknown rate */
974     if (DeviceExtension->Rate == BATTERY_UNKNOWN_RATE)
975     {
976         /* The battery is discharging but we don't know by how much... this is bad! */
977         if ((BstState & ACPI_BATT_STAT_DISCHARG) &&
978             (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
979             DbgPrint("CmBattGetBatteryStatus: battery rate is unknown when battery is not charging!\n");
980     }
981     else if (DeviceExtension->State & BATTERY_DISCHARGING)
982     {
983         /* The battery is discharging, so treat the rate as a negative rate */
984         DeviceExtension->Rate = -(LONG)DeviceExtension->Rate;
985     }
986     else if (!(DeviceExtension->State & BATTERY_CHARGING) && (DeviceExtension->Rate))
987     {
988         /* We are not charging, not discharging, but have a rate? Ignore it! */
989         if (CmBattDebug & CMBATT_GENERIC_WARNING)
990             DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
991                      DeviceExtension->Rate);
992         DeviceExtension->Rate = 0;
993     }
994 
995     /* Done */
996     return STATUS_SUCCESS;
997 }
998 
999 NTSTATUS
1000 NTAPI
1001 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,
1002                        IN ULONG Tag,
1003                        IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
1004                        IN OPTIONAL LONG AtRate,
1005                        IN PVOID Buffer,
1006                        IN ULONG BufferLength,
1007                        OUT PULONG ReturnedLength)
1008 {
1009     NTSTATUS Status;
1010     PVOID QueryData = NULL;
1011     ULONG QueryLength = 0;
1012     ULONG RemainingTime = 0;
1013     ANSI_STRING TempString;
1014     UNICODE_STRING TempString2;
1015     WCHAR InfoBuffer[256];
1016     WCHAR TempBuffer[256];
1017     UNICODE_STRING InfoString;
1018     ULONG RemainingCapacity;
1019     BATTERY_REPORTING_SCALE BatteryReportingScale[2];
1020     LONG Rate;
1021     PAGED_CODE();
1022     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1023         DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
1024                  Tag,
1025                  FdoExtension->DeviceId,
1026                  InfoLevel);
1027 
1028     /* Check ACPI Data */
1029     Status = CmBattVerifyStaticInfo(FdoExtension, Tag);
1030     if (!NT_SUCCESS(Status)) return Status;
1031 
1032     /* Check what caller wants */
1033     switch (InfoLevel)
1034     {
1035         case BatteryInformation:
1036             /* Just return our static information */
1037             QueryData = &FdoExtension->BatteryInformation;
1038             QueryLength = sizeof(BATTERY_INFORMATION);
1039             break;
1040 
1041         case BatteryGranularityInformation:
1042 
1043             /* Return our static information, we have two scales */
1044             BatteryReportingScale[0].Granularity = FdoExtension->BatteryCapacityGranularity1;
1045             BatteryReportingScale[0].Capacity = FdoExtension->BatteryInformation.DefaultAlert1;
1046             BatteryReportingScale[1].Granularity = FdoExtension->BatteryCapacityGranularity2;
1047             BatteryReportingScale[1].Capacity = FdoExtension->BatteryInformation.DesignedCapacity;
1048             QueryData = BatteryReportingScale;
1049             QueryLength = sizeof(BATTERY_REPORTING_SCALE) * 2;
1050             break;
1051 
1052         case BatteryEstimatedTime:
1053 
1054             /* Check if it's been more than 2 1/2 minutes since the last change */
1055             if ((KeQueryInterruptTime() - 150000000) > (FdoExtension->InterruptTime))
1056             {
1057                 /* Get new battery status */
1058                 CmBattGetBatteryStatus(FdoExtension, FdoExtension->Tag);
1059 
1060                 /* If the caller didn't specify a rate, use our static one */
1061                 Rate = AtRate;
1062                 if (!Rate) Rate = FdoExtension->Rate;
1063 
1064                 /* If we don't have a valid negative rate, use unknown value */
1065                 if (Rate >= 0) Rate = BATTERY_UNKNOWN_RATE;
1066 
1067                 /* Grab the remaining capacity */
1068                 RemainingCapacity = FdoExtension->RemainingCapacity;
1069 
1070                 /* See if we don't know one or the other */
1071                 if ((Rate == BATTERY_UNKNOWN_RATE) ||
1072                     (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY))
1073                 {
1074                     /* If the battery is discharging, we can't give out a time */
1075                     if ((FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG) &&
1076                         (CmBattDebug & CMBATT_GENERIC_WARNING))
1077                             DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
1078 
1079                     /* Check if we don't have a rate and capacity is going down */
1080                     if ((FdoExtension->Rate == BATTERY_UNKNOWN_RATE) &&
1081                         (FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG))
1082                     {
1083                         /* We have to fail, since we lack data */
1084                         Status = STATUS_INVALID_DEVICE_REQUEST;
1085                         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1086                             DbgPrint("----------------------   PresentRate = BATTERY_UNKNOWN_RATE\n");
1087                     }
1088 
1089                     /* If we don't have capacity, the rate is useless */
1090                     if (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY)
1091                     {
1092                         /* We have to fail the request */
1093                         Status = STATUS_INVALID_DEVICE_REQUEST;
1094                         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1095                             DbgPrint("----------------------   RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
1096                     }
1097                 }
1098                 else
1099                 {
1100                     /* We have data, but is it valid? */
1101                     if (RemainingCapacity > 0x123456)
1102                     {
1103                         /* The capacity seems bogus, so don't use it */
1104                         if (CmBattDebug & CMBATT_ACPI_WARNING)
1105                             DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
1106                     }
1107                     else
1108                     {
1109                         /* Compute the remaining time in seconds, based on rate */
1110                         RemainingTime = (RemainingCapacity * 3600) / -Rate;
1111                     }
1112                 }
1113             }
1114 
1115             /* Return the remaining time */
1116             QueryData = &RemainingTime;
1117             QueryLength = sizeof(ULONG);
1118             break;
1119 
1120         case BatteryDeviceName:
1121 
1122             /* Build the model number string */
1123             RtlInitAnsiString(&TempString, FdoExtension->ModelNumber);
1124 
1125             /* Convert it to Unicode */
1126             InfoString.Buffer = InfoBuffer;
1127             InfoString.MaximumLength = sizeof(InfoBuffer);
1128             Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1129 
1130             /* Return the unicode buffer */
1131             QueryData = InfoString.Buffer;
1132             QueryLength = InfoString.Length;
1133             break;
1134 
1135         case BatteryTemperature:
1136         case BatteryManufactureDate:
1137 
1138             /* We don't support these */
1139             Status = STATUS_INVALID_DEVICE_REQUEST;
1140             break;
1141 
1142         case BatteryManufactureName:
1143 
1144             /* Build the OEM info string */
1145             RtlInitAnsiString(&TempString, FdoExtension->OemInfo);
1146 
1147             /* Convert it to Unicode */
1148             InfoString.Buffer = InfoBuffer;
1149             InfoString.MaximumLength = sizeof(InfoBuffer);
1150             Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1151 
1152             /* Return the unicode buffer */
1153             QueryData = InfoString.Buffer;
1154             QueryLength = InfoString.Length;
1155             break;
1156 
1157         case BatteryUniqueID:
1158 
1159             /* Build the serial number string */
1160             RtlInitAnsiString(&TempString, FdoExtension->SerialNumber);
1161 
1162             /* Convert it to Unicode */
1163             InfoString.Buffer = InfoBuffer;
1164             InfoString.MaximumLength = sizeof(InfoBuffer);
1165             RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1166 
1167             /* Setup a temporary string for concatenation */
1168             TempString2.Buffer = TempBuffer;
1169             TempString2.MaximumLength = sizeof(TempBuffer);
1170 
1171             /* Check if there's an OEM string */
1172             if (FdoExtension->OemInfo[0])
1173             {
1174                 /* Build the OEM info string */
1175                 RtlInitAnsiString(&TempString, FdoExtension->OemInfo);
1176 
1177                 /* Convert it to Unicode and append it */
1178                 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1179                 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1180             }
1181 
1182             /* Build the model number string */
1183             RtlInitAnsiString(&TempString, FdoExtension->ModelNumber);
1184 
1185             /* Convert it to Unicode and append it */
1186             RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1187             RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1188 
1189             /* Return the final appended string */
1190             QueryData = InfoString.Buffer;
1191             QueryLength = InfoString.Length;
1192             break;
1193 
1194         default:
1195 
1196             /* Everything else is unknown */
1197             Status = STATUS_INVALID_PARAMETER;
1198             break;
1199     }
1200 
1201     /* Return the required length and check if the caller supplied enough */
1202     *ReturnedLength = QueryLength;
1203     if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
1204 
1205     /* Copy the data if there's enough space and it exists */
1206     if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
1207 
1208     /* Return function result */
1209     return Status;
1210 }
1211 
1212 NTSTATUS
1213 NTAPI
1214 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
1215                   IN ULONG Tag,
1216                   IN PBATTERY_STATUS BatteryStatus)
1217 {
1218     NTSTATUS Status;
1219     PAGED_CODE();
1220     if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1221         DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag, DeviceExtension->DeviceId);
1222 
1223     /* Query ACPI information */
1224     Status = CmBattGetBatteryStatus(DeviceExtension, Tag);
1225     if (NT_SUCCESS(Status))
1226     {
1227         BatteryStatus->PowerState = DeviceExtension->State;
1228         BatteryStatus->Capacity = DeviceExtension->RemainingCapacity;
1229         BatteryStatus->Voltage = DeviceExtension->PresentVoltage;
1230         BatteryStatus->Rate = DeviceExtension->Rate;
1231     }
1232 
1233     /* Return status */
1234     if (CmBattDebug & (CMBATT_GENERIC_INFO))
1235         DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
1236                  BatteryStatus->PowerState,
1237                  BatteryStatus->Capacity,
1238                  BatteryStatus->Voltage,
1239                  BatteryStatus->Rate);
1240     return Status;
1241 }
1242 
1243 NTSTATUS
1244 NTAPI
1245 DriverEntry(IN PDRIVER_OBJECT DriverObject,
1246             IN PUNICODE_STRING RegistryPath)
1247 {
1248     NTSTATUS Status;
1249     PDRIVER_EXTENSION DriverExtension;
1250     OBJECT_ATTRIBUTES ObjectAttributes;
1251     UNICODE_STRING CallbackName;
1252 
1253     /* Allocate registry path */
1254     GlobalRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
1255     GlobalRegistryPath.Length = RegistryPath->Length;
1256     GlobalRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
1257                                                       GlobalRegistryPath.MaximumLength,
1258                                                       'MtaB');
1259     if (!GlobalRegistryPath.Buffer)
1260     {
1261         /* Fail if we're out of memory this early */
1262         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1263             DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
1264         return STATUS_INSUFFICIENT_RESOURCES;
1265     }
1266 
1267     /* Buffer allocated, copy the string */
1268     RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
1269     if (CmBattDebug & CMBATT_GENERIC_INFO)
1270         DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
1271                  DriverObject,
1272                  RegistryPath->Buffer);
1273 
1274     /* Setup the major dispatchers */
1275     DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
1276     DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
1277     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
1278     DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
1279     DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
1280     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
1281 
1282     /* And the unload routine */
1283     DriverObject->DriverUnload = CmBattUnload;
1284 
1285     /* And the add device routine */
1286     DriverExtension = DriverObject->DriverExtension;
1287     DriverExtension->AddDevice = CmBattAddDevice;
1288 
1289     /* Create a power callback */
1290     RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
1291     InitializeObjectAttributes(&ObjectAttributes,
1292                                &CallbackName,
1293                                OBJ_KERNEL_HANDLE,
1294                                NULL,
1295                                NULL);
1296     Status = ExCreateCallback(&CmBattPowerCallBackObject, &ObjectAttributes, 0, TRUE);
1297     if (!NT_SUCCESS(Status))
1298     {
1299         /* No callback, fail */
1300         CmBattPowerCallBackObject = 0;
1301         if (CmBattDebug & CMBATT_GENERIC_WARNING)
1302             DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status);
1303     }
1304     else
1305     {
1306         /* Register the power callback now */
1307         CmBattPowerCallBackRegistration = ExRegisterCallback(CmBattPowerCallBackObject,
1308                                                              (PVOID)CmBattPowerCallBack,
1309                                                              DriverObject);
1310         if (CmBattPowerCallBackRegistration)
1311         {
1312             /* Last thing: setup our DPC and timer for battery wake */
1313             KeInitializeDpc(&CmBattWakeDpcObject, (PVOID)CmBattWakeDpc, DriverObject);
1314             KeInitializeTimer(&CmBattWakeDpcTimerObject);
1315         }
1316         else
1317         {
1318             ObDereferenceObject(CmBattPowerCallBackObject);
1319             if (CmBattDebug & CMBATT_GENERIC_WARNING)
1320                 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1321         }
1322 
1323         /* All good */
1324         Status = STATUS_SUCCESS;
1325     }
1326 
1327     /* Return failure or success */
1328     return Status;
1329 }
1330 
1331 /* EOF */
1332