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