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
9 /* INCLUDES *******************************************************************/
13 /* GLOBALS ********************************************************************/
16 PCALLBACK_OBJECT CmBattPowerCallBackObject
;
17 PVOID CmBattPowerCallBackRegistration
;
18 UNICODE_STRING GlobalRegistryPath
;
19 KTIMER CmBattWakeDpcTimerObject
;
20 KDPC CmBattWakeDpcObject
;
21 PDEVICE_OBJECT AcAdapterPdo
;
22 LARGE_INTEGER CmBattWakeDpcDelay
;
24 /* FUNCTIONS ******************************************************************/
28 CmBattPowerCallBack(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
33 PDEVICE_OBJECT DeviceObject
;
34 if (CmBattDebug
& 0x10)
35 DbgPrint("CmBattPowerCallBack: action: %d, value: %d \n", Action
, Value
);
37 /* Check if a transition is going to happen */
38 if (Action
== PO_CB_SYSTEM_STATE_LOCK
)
40 /* We have just re-entered S0: call the wake DPC in 10 seconds */
43 if (CmBattDebug
& 0x10)
44 DbgPrint("CmBattPowerCallBack: Calling CmBattWakeDpc after 10 seconds.\n");
45 Cancelled
= KeSetTimer(&CmBattWakeDpcTimerObject
, CmBattWakeDpcDelay
, &CmBattWakeDpcObject
);
46 if (CmBattDebug
& 0x10)
47 DbgPrint("CmBattPowerCallBack: timerCanceled = %d.\n", Cancelled
);
51 /* We are exiting the S0 state: loop all devices to set the delay flag */
52 if (CmBattDebug
& 0x10)
53 DbgPrint("CmBattPowerCallBack: Delaying Notifications\n");
54 for (DeviceObject
= DeviceExtension
->DeviceObject
;
56 DeviceObject
= DeviceObject
->NextDevice
)
58 /* Set the delay flag */
59 DeviceExtension
= DeviceObject
->DeviceExtension
;
60 DeviceExtension
->DelayNotification
= TRUE
;
63 else if (CmBattDebug
& 0x10)
66 DbgPrint("CmBattPowerCallBack: unknown argument2 = %08x\n");
73 CmBattWakeDpc(IN PKDPC Dpc
,
74 IN PCMBATT_DEVICE_EXTENSION FdoExtension
,
75 IN PVOID SystemArgument1
,
76 IN PVOID SystemArgument2
)
78 PDEVICE_OBJECT CurrentObject
;
79 BOOLEAN AcNotify
= FALSE
;
80 PCMBATT_DEVICE_EXTENSION DeviceExtension
;
82 if (CmBattDebug
& 2) DbgPrint("CmBattWakeDpc: Entered.\n");
84 /* Loop all device objects */
85 for (CurrentObject
= FdoExtension
->DeviceObject
;
87 CurrentObject
= CurrentObject
->NextDevice
)
89 /* Turn delay flag off, we're back in S0 */
90 DeviceExtension
= CurrentObject
->DeviceExtension
;
91 DeviceExtension
->DelayNotification
= 0;
93 /* Check if this is an AC adapter */
94 if (DeviceExtension
->FdoType
== CmBattAcAdapter
)
96 /* Was there a pending notify? */
97 if (DeviceExtension
->ArFlag
& CMBATT_AR_NOTIFY
)
99 /* We'll send a notify on the next pass */
101 DeviceExtension
->ArFlag
= 0;
102 if (CmBattDebug
& 0x20)
103 DbgPrint("CmBattWakeDpc: AC adapter notified\n");
108 /* Loop the device objects again */
109 for (CurrentObject
= FdoExtension
->DeviceObject
;
111 CurrentObject
= CurrentObject
->NextDevice
)
113 /* Check if this is a battery */
114 DeviceExtension
= CurrentObject
->DeviceExtension
;
115 if (DeviceExtension
->FdoType
== CmBattBattery
)
117 /* Check what ARs are pending */
118 ArFlag
= DeviceExtension
->ArFlag
;
119 if (CmBattDebug
& 0x20)
120 DbgPrint("CmBattWakeDpc: Performing delayed ARs: %01x\n", ArFlag
);
122 /* Insert notification, clear the lock value */
123 if (ArFlag
& CMBATT_AR_INSERT
) InterlockedExchange(&DeviceExtension
->ArLockValue
, 0);
125 /* Removal, clear the battery tag */
126 if (ArFlag
& CMBATT_AR_REMOVE
) DeviceExtension
->Tag
= 0;
128 /* Notification (or AC/DC adapter change from first pass above) */
129 if ((ArFlag
& CMBATT_AR_NOTIFY
) || (AcNotify
))
131 /* Notify the class driver */
132 BatteryClassStatusNotify(DeviceExtension
->ClassData
);
140 CmBattNotifyHandler(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
141 IN ULONG NotifyValue
)
144 PCMBATT_DEVICE_EXTENSION FdoExtension
;
145 PDEVICE_OBJECT DeviceObject
;
147 if (CmBattDebug
& (CMBATT_ACPI_ASSERT
| CMBATT_PNP_INFO
))
148 DbgPrint("CmBattNotifyHandler: CmBatt 0x%08x Type %d Number %d Notify Value: %x\n",
150 DeviceExtension
->FdoType
,
151 DeviceExtension
->DeviceId
,
154 /* Check what kind of notification was received */
157 /* ACPI Specification says is sends a "Bus Check" when power source changes */
160 /* We treat it as possible physical change */
161 DeviceExtension
->ArFlag
|= (CMBATT_AR_NOTIFY
| CMBATT_AR_INSERT
);
162 if ((DeviceExtension
->Tag
) &&
163 (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_WARNING
)))
164 DbgPrint("CmBattNotifyHandler: Received battery #%x insertion, but tag was not invalid.\n",
165 DeviceExtension
->DeviceId
);
168 /* Status of the battery has changed */
169 case ACPI_BATT_NOTIFY_STATUS
:
171 /* All we'll do is notify the class driver */
172 DeviceExtension
->ArFlag
|= CMBATT_AR_NOTIFY
;
175 /* Information on the battery has changed, such as physical presence */
176 case ACPI_DEVICE_CHECK
:
177 case ACPI_BATT_NOTIFY_INFO
:
179 /* Reset all state and let the class driver re-evaluate it all */
180 DeviceExtension
->ArFlag
|= (CMBATT_AR_NOTIFY
|
187 if (CmBattDebug
& CMBATT_PNP_INFO
)
188 DbgPrint("CmBattNotifyHandler: Unknown Notify Value: %x\n", NotifyValue
);
191 /* Check if we're supposed to delay the notification till later */
192 if (DeviceExtension
->DelayNotification
)
194 /* We'll handle this when we get a status query later on */
195 if (CmBattDebug
& CMBATT_PNP_INFO
)
196 DbgPrint("CmBattNotifyHandler: Notification delayed: ARs = %01x\n",
197 DeviceExtension
->ArFlag
);
201 /* We're going to handle this now */
202 if (CmBattDebug
& CMBATT_PNP_INFO
)
203 DbgPrint("CmBattNotifyHandler: Performing ARs: %01x\n", DeviceExtension
->ArFlag
);
205 /* Check if this is a battery or AC adapter notification */
206 if (DeviceExtension
->FdoType
== CmBattBattery
)
208 /* Reset the current trip point */
209 DeviceExtension
->TripPointValue
= BATTERY_UNKNOWN_CAPACITY
;
211 /* Check what ARs have to be done */
212 ArFlag
= DeviceExtension
->ArFlag
;
214 /* New battery inserted, reset lock value */
215 if (ArFlag
& CMBATT_AR_INSERT
) InterlockedExchange(&DeviceExtension
->ArLockValue
, 0);
217 /* Check if the battery may have been removed */
218 if (ArFlag
& CMBATT_AR_REMOVE
) DeviceExtension
->Tag
= 0;
220 /* Check if there's been any sort of change to the battery */
221 if (ArFlag
& CMBATT_AR_NOTIFY
)
223 /* We'll probably end up re-evaluating _BIF and _BST */
224 DeviceExtension
->NotifySent
= TRUE
;
225 BatteryClassStatusNotify(DeviceExtension
->ClassData
);
230 /* The only known notification is AC/DC change */
231 if (DeviceExtension
->ArFlag
& CMBATT_AR_NOTIFY
)
233 for (DeviceObject
= DeviceExtension
->FdoDeviceObject
->DriverObject
->DeviceObject
;
235 DeviceObject
= DeviceObject
->NextDevice
)
237 /* Is this a battery? */
238 FdoExtension
= DeviceObject
->DeviceExtension
;
239 if (FdoExtension
->FdoType
== CmBattBattery
)
241 /* Send a notification to the class driver */
242 FdoExtension
->NotifySent
= TRUE
;
243 BatteryClassStatusNotify(FdoExtension
->ClassData
);
249 /* ARs have been processed */
250 DeviceExtension
->ArFlag
= 0;
255 CmBattUnload(IN PDRIVER_OBJECT DriverObject
)
257 if (CmBattDebug
& CMBATT_GENERIC_INFO
) DPRINT("CmBattUnload: \n");
259 /* Check if we have a registered power callback */
260 if (CmBattPowerCallBackObject
)
263 ExUnregisterCallback(CmBattPowerCallBackRegistration
);
264 ObfDereferenceObject(CmBattPowerCallBackObject
);
267 /* Free the registry buffer if it exists */
268 if (GlobalRegistryPath
.Buffer
) ExFreePool(GlobalRegistryPath
.Buffer
);
270 /* Make sure we don't still have references to the DO */
271 if ((DriverObject
->DeviceObject
) && (CmBattDebug
& CMBATT_GENERIC_WARNING
))
273 DbgPrint("Unload called before all devices removed.\n");
279 CmBattVerifyStaticInfo(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
283 return STATUS_NOT_IMPLEMENTED
;
288 CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject
,
291 NTSTATUS Status
= STATUS_SUCCESS
;
292 PIO_STACK_LOCATION IoStackLocation
;
295 PCMBATT_DEVICE_EXTENSION DeviceExtension
;
297 if (CmBattDebug
& CMBATT_GENERIC_INFO
) DPRINT("CmBattOpenClose\n");
299 /* Grab the device extension and lock it */
300 DeviceExtension
= DeviceObject
->DeviceExtension
;
301 ExAcquireFastMutex(&DeviceExtension
->FastMutex
);
303 /* Check if someone is trying to open a device that doesn't exist yet */
304 Count
= DeviceExtension
->HandleCount
;
305 if (Count
== 0xFFFFFFFF)
307 /* Fail the request */
308 Status
= STATUS_NO_SUCH_DEVICE
;
309 if (CmBattDebug
& CMBATT_PNP_INFO
)
311 DbgPrint("CmBattOpenClose: Failed (UID = %x)(device being removed).\n",
312 DeviceExtension
->Tag
);
317 /* Check if this is an open or close */
318 IoStackLocation
= IoGetCurrentIrpStackLocation(Irp
);
319 Major
= IoStackLocation
->MajorFunction
;
320 if (Major
== IRP_MJ_CREATE
)
322 /* Increment the open count */
323 DeviceExtension
->HandleCount
= Count
+ 1;
324 if (CmBattDebug
& CMBATT_PNP_INFO
)
326 DbgPrint("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
327 DeviceExtension
->DeviceId
, Count
+ 1);
330 else if (Major
== IRP_MJ_CLOSE
)
332 /* Decrement the open count */
333 DeviceExtension
->HandleCount
= Count
- 1;
334 if (CmBattDebug
& CMBATT_PNP_INFO
)
336 DbgPrint("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
337 DeviceExtension
->DeviceId
, Count
+ 1);
342 /* Release lock and complete request */
343 ExReleaseFastMutex(&DeviceExtension
->FastMutex
);
344 Irp
->IoStatus
.Status
= Status
;
345 IofCompleteRequest(Irp
, IO_NO_INCREMENT
);
351 CmBattIoctl(PDEVICE_OBJECT DeviceObject
,
355 return STATUS_NOT_IMPLEMENTED
;
360 CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
363 PDEVICE_OBJECT PdoDevice
;
368 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
369 DbgPrint("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
370 *Tag
, DeviceExtension
, DeviceExtension
->DeviceId
);
372 /* Get PDO and clear notification flag */
373 PdoDevice
= DeviceExtension
->PdoDeviceObject
;
374 DeviceExtension
->NotifySent
= 0;
376 /* Get _STA from PDO (we need the machine status, not the battery status) */
377 Status
= CmBattGetStaData(PdoDevice
, &StaData
);
378 if (NT_SUCCESS(Status
))
380 /* Is a battery present? */
381 if (StaData
& ACPI_STA_BATTERY_PRESENT
)
383 /* Do we not have a tag yet? */
384 if (!DeviceExtension
->Tag
)
386 /* Set the new tag value, reset tags if we reached the maximum */
387 NewTag
= DeviceExtension
->TagData
;
388 if (DeviceExtension
->TagData
++ == 0xFFFFFFFF) NewTag
= 1;
389 DeviceExtension
->Tag
= NewTag
;
390 if (CmBattDebug
& CMBATT_GENERIC_INFO
)
391 DbgPrint("CmBattQueryTag - New Tag: (%d)\n", DeviceExtension
->Tag
);
393 /* Reset trip point data */
394 DeviceExtension
->TripPointOld
= 0;
395 DeviceExtension
->TripPointValue
= BATTERY_UNKNOWN_CAPACITY
;
397 /* Clear AR lock and set new interrupt time */
398 InterlockedExchange(&DeviceExtension
->ArLockValue
, 0);
399 DeviceExtension
->InterruptTime
= KeQueryInterruptTime();
404 /* No battery, so no tag */
405 DeviceExtension
->Tag
= 0;
406 Status
= STATUS_NO_SUCH_DEVICE
;
410 /* Return the tag and status result */
411 *Tag
= DeviceExtension
->Tag
;
412 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
413 DbgPrint("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n", *Tag
, Status
);
419 CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
)
423 if (CmBattDebug
& 0xA) DbgPrint("CmBattDisableStatusNotify\n");
425 /* Do we have a trip point */
426 if (DeviceExtension
->TripPointSet
)
428 /* Is there a current value set? */
429 if (DeviceExtension
->TripPointValue
)
431 /* Reset it back to 0 */
432 DeviceExtension
->TripPointValue
= 0;
433 Status
= CmBattSetTripPpoint(DeviceExtension
, 0);
434 if (!NT_SUCCESS(Status
))
436 /* If it failed, set unknown/invalid value */
437 DeviceExtension
->TripPointValue
= BATTERY_UNKNOWN_CAPACITY
;
439 DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status
);
444 /* No trip point set, so this is a successful no-op */
445 Status
= STATUS_SUCCESS
;
450 /* Nothing we can do */
451 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
460 CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
462 IN PBATTERY_NOTIFY BatteryNotify
)
465 ACPI_BST_DATA BstData
;
466 ULONG Capacity
, NewTripPoint
, TripPoint
, DesignVoltage
;
469 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
470 DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
471 BatteryTag
, BatteryNotify
->LowCapacity
);
473 /* Update any ACPI evaluations */
474 Status
= CmBattVerifyStaticInfo(DeviceExtension
, BatteryTag
);
475 if (!NT_SUCCESS(Status
)) return Status
;
477 /* Trip point not supported, fail */
478 if (!DeviceExtension
->TripPointSet
) return STATUS_OBJECT_NAME_NOT_FOUND
;
480 /* Are both capacities known? */
481 if ((BatteryNotify
->HighCapacity
== BATTERY_UNKNOWN_CAPACITY
) ||
482 (BatteryNotify
->LowCapacity
== BATTERY_UNKNOWN_CAPACITY
))
484 /* We can't set trip points without these */
485 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
486 DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
487 return STATUS_NOT_SUPPORTED
;
490 /* Is the battery charging? */
491 Charging
= DeviceExtension
->BstData
.State
& ACPI_BATT_STAT_CHARGING
;
494 /* Then the trip point is when we hit the cap */
495 Capacity
= BatteryNotify
->HighCapacity
;
496 NewTripPoint
= BatteryNotify
->HighCapacity
;
500 /* Otherwise it's when we discharge to the bottom */
501 Capacity
= BatteryNotify
->LowCapacity
;
502 NewTripPoint
= BatteryNotify
->LowCapacity
;
505 /* Do we have data in Amps or Watts? */
506 if (DeviceExtension
->BifData
.PowerUnit
== ACPI_BATT_POWER_UNIT_AMPS
)
508 /* We need the voltage to do the conversion */
509 DesignVoltage
= DeviceExtension
->BifData
.DesignVoltage
;
510 if ((DesignVoltage
!= BATTERY_UNKNOWN_VOLTAGE
) && (DesignVoltage
))
512 /* Convert from mAh into Ah */
513 TripPoint
= 1000 * NewTripPoint
;
516 /* Scale the high trip point */
517 NewTripPoint
= (TripPoint
+ 500) / DesignVoltage
+ ((TripPoint
+ 500) % DesignVoltage
!= 0);
521 /* Scale the low trip point */
522 NewTripPoint
= (TripPoint
- 500) / DesignVoltage
- ((TripPoint
- 500) % DesignVoltage
== 0);
527 /* Without knowing the voltage, Amps are not enough data on consumption */
528 Status
= STATUS_NOT_SUPPORTED
;
529 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
530 DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
536 /* Make it trip just one past the charge cap */
539 else if (NewTripPoint
> 0)
541 /* Make it trip just one below the drain cap */
545 /* Do we actually have a new trip point? */
546 if (NewTripPoint
== DeviceExtension
->TripPointValue
)
548 /* No, so there is no work to be done */
549 if (CmBattDebug
& CMBATT_GENERIC_STATUS
)
550 DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension
->TripPointValue
);
551 return STATUS_SUCCESS
;
554 /* Set the trip point with ACPI and check for success */
555 DeviceExtension
->TripPointValue
= NewTripPoint
;
556 Status
= CmBattSetTripPpoint(DeviceExtension
, NewTripPoint
);
557 if (!(NewTripPoint
) && (Capacity
)) Status
= STATUS_NOT_SUPPORTED
;
558 if (!NT_SUCCESS(Status
))
560 /* We failed to set the trip point, or there wasn't one settable */
561 DeviceExtension
->TripPointValue
= BATTERY_UNKNOWN_CAPACITY
;
562 if (CmBattDebug
& (CMBATT_GENERIC_WARNING
| CMBATT_ACPI_WARNING
))
563 DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status
);
567 /* Read the new BST data to see the latest state */
568 Status
= CmBattGetBstData(DeviceExtension
, &BstData
);
569 if (!NT_SUCCESS(Status
))
571 /* We'll return failure to the caller */
572 if (CmBattDebug
& (CMBATT_GENERIC_WARNING
| CMBATT_ACPI_WARNING
))
573 DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status
);
575 else if ((Charging
) && (BstData
.RemainingCapacity
>= NewTripPoint
))
577 /* We are charging and our capacity is past the trip point, so trip now */
578 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
579 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
580 NewTripPoint
, BstData
.RemainingCapacity
);
581 CmBattNotifyHandler(DeviceExtension
, ACPI_BATT_NOTIFY_STATUS
);
583 else if ((BstData
.RemainingCapacity
) && (Capacity
))
585 /* We are discharging, and our capacity is below the trip point, trip now */
586 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
587 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
588 NewTripPoint
, BstData
.RemainingCapacity
);
589 CmBattNotifyHandler(DeviceExtension
, ACPI_BATT_NOTIFY_STATUS
);
592 /* All should've went well if we got here, unless BST failed... return! */
593 if (CmBattDebug
& CMBATT_GENERIC_STATUS
)
594 DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
595 Capacity
, DeviceExtension
->RemainingCapacity
);
596 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
597 DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %x\n",
598 BatteryNotify
->PowerState
,
599 BatteryNotify
->LowCapacity
,
600 BatteryNotify
->HighCapacity
);
606 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
612 ULONG DesignVoltage
, PresentRate
, RemainingCapacity
;
614 if (CmBattDebug
& CMBATT_GENERIC_INFO
)
615 DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension
, Tag
);
617 /* Validate ACPI data */
618 Status
= CmBattVerifyStaticInfo(DeviceExtension
, Tag
);
619 if (!NT_SUCCESS(Status
)) return Status
;
621 /* Check for delayed status notifications */
622 if (DeviceExtension
->DelayNotification
)
624 /* Process them now and don't do any other work */
625 CmBattNotifyHandler(DeviceExtension
, ACPI_BATT_NOTIFY_STATUS
);
629 /* Get _BST from ACPI */
630 Status
= CmBattGetBstData(DeviceExtension
, &DeviceExtension
->BstData
);
631 if (!NT_SUCCESS(Status
))
634 InterlockedExchange(&DeviceExtension
->ArLockValue
, 0);
638 /* Clear current BST information */
639 DeviceExtension
->State
= 0;
640 DeviceExtension
->RemainingCapacity
= 0;
641 DeviceExtension
->PresentVoltage
= 0;
642 DeviceExtension
->Rate
= 0;
644 /* Get battery state */
645 BstState
= DeviceExtension
->BstData
.State
;
647 /* Is the battery both charging and discharging? */
648 if ((BstState
& ACPI_BATT_STAT_DISCHARG
) && (BstState
& ACPI_BATT_STAT_CHARGING
) &&
649 (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_WARNING
)))
650 DbgPrint("************************ ACPI BIOS BUG ********************\n* "
651 "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
652 "* One battery cannot be charging and discharging at the same time.\n",
655 /* Is the battery discharging? */
656 if (BstState
& ACPI_BATT_STAT_DISCHARG
)
658 /* Set power state and check if it just started discharging now */
659 DeviceExtension
->State
|= BATTERY_DISCHARGING
;
660 if (!(DeviceExtension
->State
& ACPI_BATT_STAT_DISCHARG
))
662 /* Remember the time when the state changed */
663 DeviceExtension
->InterruptTime
= KeQueryInterruptTime();
666 else if (BstState
& ACPI_BATT_STAT_CHARGING
)
668 /* Battery is charging, update power state */
669 DeviceExtension
->State
|= (BATTERY_CHARGING
| BATTERY_POWER_ON_LINE
);
672 /* Is the battery in a critical state? */
673 if (BstState
& ACPI_BATT_STAT_CRITICAL
) DeviceExtension
->State
|= BATTERY_CRITICAL
;
675 /* Read the voltage data */
676 DeviceExtension
->PresentVoltage
= DeviceExtension
->BstData
.PresentVoltage
;
678 /* Check if we have an A/C adapter */
681 /* Query information on it */
682 CmBattGetPsrData(AcAdapterPdo
, &PsrData
);
686 /* Otherwise, check if the battery is charging */
687 if (BstState
& ACPI_BATT_STAT_CHARGING
)
689 /* Then we'll assume there's a charger */
694 /* Assume no charger */
699 /* Is there a charger? */
702 /* Set the power state flag to reflect this */
703 DeviceExtension
->State
|= BATTERY_POWER_ON_LINE
;
704 if (CmBattDebug
& (CMBATT_GENERIC_INFO
| CMBATT_GENERIC_STATUS
))
705 DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
707 else if (CmBattDebug
& (CMBATT_GENERIC_INFO
| CMBATT_GENERIC_STATUS
))
709 DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
712 /* Get some data we'll need */
713 DesignVoltage
= DeviceExtension
->BifData
.DesignVoltage
;
714 PresentRate
= DeviceExtension
->BstData
.PresentRate
;
715 RemainingCapacity
= DeviceExtension
->BstData
.RemainingCapacity
;
717 /* Check if we have battery data in Watts instead of Amps */
718 if (DeviceExtension
->BifData
.PowerUnit
== ACPI_BATT_POWER_UNIT_WATTS
)
720 /* Get the data from the BST */
721 DeviceExtension
->RemainingCapacity
= RemainingCapacity
;
722 DeviceExtension
->Rate
= PresentRate
;
724 /* Check if the rate is invalid */
725 if (PresentRate
> CM_MAX_VALUE
)
727 /* Set an unknown rate and don't touch the old value */
728 DeviceExtension
->Rate
= BATTERY_UNKNOWN_RATE
;
729 if ((PresentRate
!= CM_UNKNOWN_VALUE
) && (CmBattDebug
& CMBATT_ACPI_WARNING
))
731 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
732 DbgPrint("---------------------- PresentRate = 0x%08x\n", PresentRate
);
736 else if ((DesignVoltage
!= CM_UNKNOWN_VALUE
) && (DesignVoltage
))
738 /* We have voltage data, what about capacity? */
739 if (RemainingCapacity
== CM_UNKNOWN_VALUE
)
741 /* Unable to calculate it */
742 DeviceExtension
->RemainingCapacity
= BATTERY_UNKNOWN_CAPACITY
;
743 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
745 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
746 DbgPrint("---------------------- RemainingCapacity = CM_UNKNOWN_VALUE\n");
751 /* Compute the capacity with the information we have */
752 DeviceExtension
->RemainingCapacity
= (DesignVoltage
* RemainingCapacity
+ 500) / 1000;
755 /* Check if we have a rate */
756 if (PresentRate
!= CM_UNKNOWN_VALUE
)
758 /* Make sure the rate isn't too large */
759 if (PresentRate
> (-500 / DesignVoltage
))
761 /* It is, so set unknown state */
762 DeviceExtension
->Rate
= BATTERY_UNKNOWN_RATE
;
763 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
765 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
766 DbgPrint("---------------------- Overflow: PresentRate = 0x%08x\n", PresentRate
);
770 /* Compute the rate */
771 DeviceExtension
->Rate
= (PresentRate
* DesignVoltage
+ 500) / 1000;
775 /* We don't have a rate, so set unknown value */
776 DeviceExtension
->Rate
= BATTERY_UNKNOWN_RATE
;
777 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
779 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
780 DbgPrint("---------------------- Present Rate = CM_UNKNOWN_VALUE\n");
786 /* We have no rate, and no capacity, set unknown values */
787 DeviceExtension
->Rate
= BATTERY_UNKNOWN_RATE
;
788 DeviceExtension
->RemainingCapacity
= BATTERY_UNKNOWN_CAPACITY
;
789 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
791 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
792 DbgPrint("---------------------- DesignVoltage = 0x%08x\n", DesignVoltage
);
796 /* Check if we have an unknown rate */
797 if (DeviceExtension
->Rate
== BATTERY_UNKNOWN_RATE
)
799 /* The battery is discharging but we don't know by how much... this is bad! */
800 if ((BstState
& ACPI_BATT_STAT_DISCHARG
) &&
801 (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_WARNING
)))
802 DbgPrint("CmBattGetBatteryStatus: battery rate is unkown when battery is not charging!\n");
804 else if (DeviceExtension
->State
& BATTERY_DISCHARGING
)
806 /* The battery is discharging, so treat the rate as a negative rate */
807 DeviceExtension
->Rate
= -DeviceExtension
->Rate
;
809 else if (!(DeviceExtension
->State
& BATTERY_CHARGING
) && (DeviceExtension
->Rate
))
811 /* We are not charging, not discharging, but have a rate? Ignore it! */
812 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
813 DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
814 DeviceExtension
->Rate
);
815 DeviceExtension
->Rate
= 0;
819 return STATUS_SUCCESS
;
824 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension
,
826 IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel
,
827 IN OPTIONAL LONG AtRate
,
829 IN ULONG BufferLength
,
830 OUT PULONG ReturnedLength
)
833 PVOID QueryData
= NULL
;
834 ULONG QueryLength
= 0;
835 ULONG RemainingTime
= 0;
836 ANSI_STRING TempString
;
837 UNICODE_STRING TempString2
;
838 WCHAR InfoBuffer
[256];
839 WCHAR TempBuffer
[256];
840 UNICODE_STRING InfoString
;
841 ULONG RemainingCapacity
;
842 BATTERY_REPORTING_SCALE BatteryReportingScale
[2];
845 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
846 DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
848 FdoExtension
->DeviceId
,
851 /* Check ACPI Data */
852 Status
= CmBattVerifyStaticInfo(FdoExtension
, Tag
);
853 if (!NT_SUCCESS(Status
)) return Status
;
855 /* Check what caller wants */
858 case BatteryInformation
:
859 /* Just return our static information */
860 QueryData
= &FdoExtension
->BatteryInformation
;
861 QueryLength
= sizeof(BATTERY_INFORMATION
);
864 case BatteryGranularityInformation
:
866 /* Return our static information, we have two scales */
867 BatteryReportingScale
[0].Granularity
= FdoExtension
->BatteryCapacityGranularity1
;
868 BatteryReportingScale
[0].Capacity
= FdoExtension
->BatteryInformation
.DefaultAlert1
;
869 BatteryReportingScale
[1].Granularity
= FdoExtension
->BatteryCapacityGranularity2
;
870 BatteryReportingScale
[1].Capacity
= FdoExtension
->BatteryInformation
.DesignedCapacity
;
871 QueryData
= BatteryReportingScale
;
872 QueryLength
= sizeof(BATTERY_REPORTING_SCALE
) * 2;
875 case BatteryEstimatedTime
:
877 /* Check if it's been more than 2 1/2 minutes since the last change */
878 if ((KeQueryInterruptTime() - 150000000) > (FdoExtension
->InterruptTime
))
880 /* Get new battery status */
881 CmBattGetBatteryStatus(FdoExtension
, FdoExtension
->Tag
);
883 /* If the caller didn't specify a rate, use our static one */
885 if (!Rate
) Rate
= FdoExtension
->Rate
;
887 /* If we don't have a valid negative rate, use unknown value */
888 if (Rate
>= 0) Rate
= BATTERY_UNKNOWN_RATE
;
890 /* Grab the remaining capacity */
891 RemainingCapacity
= FdoExtension
->RemainingCapacity
;
893 /* See if we don't know one or the other */
894 if ((Rate
== BATTERY_UNKNOWN_RATE
) ||
895 (RemainingCapacity
== BATTERY_UNKNOWN_CAPACITY
))
897 /* If the battery is discharging, we can't give out a time */
898 if ((FdoExtension
->BstData
.State
& ACPI_BATT_STAT_DISCHARG
) &&
899 (CmBattDebug
& CMBATT_GENERIC_WARNING
))
900 DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
902 /* Check if we don't have a rate and capacity is going down */
903 if ((FdoExtension
->Rate
== BATTERY_UNKNOWN_RATE
) &&
904 (FdoExtension
->BstData
.State
& ACPI_BATT_STAT_DISCHARG
))
906 /* We have to fail, since we lack data */
907 Status
= STATUS_INVALID_DEVICE_REQUEST
;
908 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
909 DbgPrint("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n");
912 /* If we don't have capacity, the rate is useless */
913 if (RemainingCapacity
== BATTERY_UNKNOWN_CAPACITY
)
915 /* We have to fail the request */
916 Status
= STATUS_INVALID_DEVICE_REQUEST
;
917 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
918 DbgPrint("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
923 /* We have data, but is it valid? */
924 if (RemainingCapacity
> 0x123456)
926 /* The capacity seems bogus, so don't use it */
927 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
928 DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
932 /* Compute the remaining time in seconds, based on rate */
933 RemainingTime
= (RemainingCapacity
* 3600) / -Rate
;
938 /* Return the remaining time */
939 QueryData
= &RemainingTime
;
940 QueryLength
= sizeof(ULONG
);
943 case BatteryDeviceName
:
945 /* Build the model number string */
946 RtlInitAnsiString(&TempString
, FdoExtension
->ModelNumber
);
948 /* Convert it to Unicode */
949 InfoString
.Buffer
= InfoBuffer
;
950 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
951 Status
= RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
953 /* Return the unicode buffer */
954 QueryData
= InfoString
.Buffer
;
955 QueryLength
= InfoString
.Length
;
958 case BatteryTemperature
:
959 case BatteryManufactureDate
:
961 /* We don't support these */
962 Status
= STATUS_INVALID_DEVICE_REQUEST
;
965 case BatteryManufactureName
:
967 /* Build the OEM info string */
968 RtlInitAnsiString(&TempString
, FdoExtension
->OemInfo
);
970 /* Convert it to Unicode */
971 InfoString
.Buffer
= InfoBuffer
;
972 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
973 Status
= RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
975 /* Return the unicode buffer */
976 QueryData
= InfoString
.Buffer
;
977 QueryLength
= InfoString
.Length
;
980 case BatteryUniqueID
:
982 /* Build the serial number string */
983 RtlInitAnsiString(&TempString
, FdoExtension
->SerialNumber
);
985 /* Convert it to Unicode */
986 InfoString
.Buffer
= InfoBuffer
;
987 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
988 RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
990 /* Setup a temporary string for concatenation */
991 TempString2
.Buffer
= TempBuffer
;
992 TempString2
.MaximumLength
= sizeof(TempBuffer
);
994 /* Check if there's an OEM string */
995 if (FdoExtension
->OemInfo
[0])
997 /* Build the OEM info string */
998 RtlInitAnsiString(&TempString
, FdoExtension
->OemInfo
);
1000 /* Convert it to Unicode and append it */
1001 RtlAnsiStringToUnicodeString(&TempString2
, &TempString
, 0);
1002 RtlAppendUnicodeStringToString(&InfoString
, &TempString2
);
1005 /* Build the model number string */
1006 RtlInitAnsiString(&TempString
, FdoExtension
->ModelNumber
);
1008 /* Convert it to Unicode and append it */
1009 RtlAnsiStringToUnicodeString(&TempString2
, &TempString
, 0);
1010 RtlAppendUnicodeStringToString(&InfoString
, &TempString2
);
1012 /* Return the final appended string */
1013 QueryData
= InfoString
.Buffer
;
1014 QueryLength
= InfoString
.Length
;
1019 /* Everything else is unknown */
1020 Status
= STATUS_INVALID_PARAMETER
;
1024 /* Return the required length and check if the caller supplied enough */
1025 *ReturnedLength
= QueryLength
;
1026 if (BufferLength
< QueryLength
) Status
= STATUS_BUFFER_TOO_SMALL
;
1028 /* Copy the data if there's enough space and it exists */
1029 if ((NT_SUCCESS(Status
)) && (QueryData
)) RtlCopyMemory(Buffer
, QueryData
, QueryLength
);
1031 /* Return function result */
1037 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
1039 IN PBATTERY_STATUS BatteryStatus
)
1043 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
1044 DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag
, DeviceExtension
->DeviceId
);
1046 /* Query ACPI information */
1047 Status
= CmBattGetBatteryStatus(DeviceExtension
, Tag
);
1048 if (NT_SUCCESS(Status
))
1050 BatteryStatus
->PowerState
= DeviceExtension
->State
;
1051 BatteryStatus
->Capacity
= DeviceExtension
->RemainingCapacity
;
1052 BatteryStatus
->Voltage
= DeviceExtension
->PresentVoltage
;
1053 BatteryStatus
->Rate
= DeviceExtension
->Rate
;
1057 if (CmBattDebug
& (CMBATT_GENERIC_INFO
))
1058 DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
1059 BatteryStatus
->PowerState
,
1060 BatteryStatus
->Capacity
,
1061 BatteryStatus
->Voltage
,
1062 BatteryStatus
->Rate
);
1068 DriverEntry(IN PDRIVER_OBJECT DriverObject
,
1069 IN PUNICODE_STRING RegistryPath
)
1072 PDRIVER_EXTENSION DriverExtension
;
1073 OBJECT_ATTRIBUTES ObjectAttributes
;
1074 UNICODE_STRING CallbackName
;
1076 /* Allocate registry path */
1077 GlobalRegistryPath
.MaximumLength
= RegistryPath
->Length
+ sizeof(UNICODE_NULL
);
1078 GlobalRegistryPath
.Length
= RegistryPath
->Length
;
1079 GlobalRegistryPath
.Buffer
= ExAllocatePoolWithTag(PagedPool
,
1080 GlobalRegistryPath
.MaximumLength
,
1082 if (!GlobalRegistryPath
.Buffer
)
1084 /* Fail if we're out of memory this early */
1085 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
1086 DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
1087 return STATUS_INSUFFICIENT_RESOURCES
;
1090 /* Buffer allocated, copy the string */
1091 RtlCopyUnicodeString(&GlobalRegistryPath
, RegistryPath
);
1092 if (CmBattDebug
& CMBATT_GENERIC_INFO
)
1093 DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
1095 RegistryPath
->Buffer
);
1097 /* Setup the major dispatchers */
1098 DriverObject
->MajorFunction
[0] = CmBattOpenClose
;
1099 DriverObject
->MajorFunction
[2] = CmBattOpenClose
;
1100 DriverObject
->MajorFunction
[14] = CmBattIoctl
;
1101 DriverObject
->MajorFunction
[22] = CmBattPowerDispatch
;
1102 DriverObject
->MajorFunction
[27] = CmBattPnpDispatch
;
1103 DriverObject
->MajorFunction
[23] = CmBattSystemControl
;
1105 /* And the unload routine */
1106 DriverObject
->DriverUnload
= CmBattUnload
;
1108 /* And the add device routine */
1109 DriverExtension
= DriverObject
->DriverExtension
;
1110 DriverExtension
->AddDevice
= CmBattAddDevice
;
1112 /* Create a power callback */
1113 RtlInitUnicodeString(&CallbackName
, L
"\\Callback\\PowerState");
1114 InitializeObjectAttributes(&ObjectAttributes
,
1119 Status
= ExCreateCallback(&CmBattPowerCallBackObject
, &ObjectAttributes
, 0, TRUE
);
1120 if (!NT_SUCCESS(Status
))
1122 /* No callback, fail */
1123 CmBattPowerCallBackObject
= 0;
1124 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
1125 DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status
);
1129 /* Register the power callback now */
1130 CmBattPowerCallBackRegistration
= ExRegisterCallback(CmBattPowerCallBackObject
,
1131 (PVOID
)CmBattPowerCallBack
,
1133 if (CmBattPowerCallBackRegistration
)
1135 /* Last thing: setup our DPC and timer for battery wake */
1136 KeInitializeDpc(&CmBattWakeDpcObject
, (PVOID
)CmBattWakeDpc
, DriverObject
);
1137 KeInitializeTimer(&CmBattWakeDpcTimerObject
);
1141 ObfDereferenceObject(CmBattPowerCallBackObject
);
1142 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
1143 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1147 Status
= STATUS_SUCCESS
;
1150 /* Return failure or success */