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
;
23 /* FUNCTIONS ******************************************************************/
27 CmBattPowerCallBack(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
36 CmBattWakeDpc(PKDPC Dpc
,
37 PCMBATT_DEVICE_EXTENSION FdoExtension
,
38 PVOID SystemArgument1
,
39 PVOID SystemArgument2
)
46 CmBattNotifyHandler(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
50 PCMBATT_DEVICE_EXTENSION FdoExtension
;
51 PDEVICE_OBJECT DeviceObject
;
53 if (CmBattDebug
& (CMBATT_ACPI_ASSERT
| CMBATT_PNP_INFO
))
54 DbgPrint("CmBattNotifyHandler: CmBatt 0x%08x Type %d Number %d Notify Value: %x\n",
56 DeviceExtension
->FdoType
,
57 DeviceExtension
->DeviceId
,
60 /* Check what kind of notification was received */
63 /* ACPI Specification says is sends a "Bus Check" when power source changes */
66 /* We treat it as possible physical change */
67 DeviceExtension
->ArFlag
|= (CMBATT_AR_NOTIFY
| CMBATT_AR_INSERT
);
68 if ((DeviceExtension
->Tag
) &&
69 (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_WARNING
)))
70 DbgPrint("CmBattNotifyHandler: Received battery #%x insertion, but tag was not invalid.\n",
71 DeviceExtension
->DeviceId
);
74 /* Status of the battery has changed */
75 case ACPI_BATT_NOTIFY_STATUS
:
77 /* All we'll do is notify the class driver */
78 DeviceExtension
->ArFlag
|= CMBATT_AR_NOTIFY
;
81 /* Information on the battery has changed, such as physical presence */
82 case ACPI_DEVICE_CHECK
:
83 case ACPI_BATT_NOTIFY_INFO
:
85 /* Reset all state and let the class driver re-evaluate it all */
86 DeviceExtension
->ArFlag
|= (CMBATT_AR_NOTIFY
|
93 if (CmBattDebug
& CMBATT_PNP_INFO
)
94 DbgPrint("CmBattNotifyHandler: Unknown Notify Value: %x\n", NotifyValue
);
97 /* Check if we're supposed to delay the notification till later */
98 if (DeviceExtension
->DelayNotification
)
100 /* We'll handle this when we get a status query later on */
101 if (CmBattDebug
& CMBATT_PNP_INFO
)
102 DbgPrint("CmBattNotifyHandler: Notification delayed: ARs = %01x\n",
103 DeviceExtension
->ArFlag
);
107 /* We're going to handle this now */
108 if (CmBattDebug
& CMBATT_PNP_INFO
)
109 DbgPrint("CmBattNotifyHandler: Performing ARs: %01x\n", DeviceExtension
->ArFlag
);
111 /* Check if this is a battery or AC adapter notification */
112 if (DeviceExtension
->FdoType
== CmBattBattery
)
114 /* Reset the current trip point */
115 DeviceExtension
->TripPointValue
= BATTERY_UNKNOWN_CAPACITY
;
117 /* Check what ARs have to be done */
118 ArFlag
= DeviceExtension
->ArFlag
;
120 /* New battery inserted, reset lock value */
121 if (ArFlag
& CMBATT_AR_INSERT
) InterlockedExchange(&DeviceExtension
->ArLockValue
, 0);
123 /* Check if the battery may have been removed */
124 if (ArFlag
& CMBATT_AR_REMOVE
) DeviceExtension
->Tag
= 0;
126 /* Check if there's been any sort of change to the battery */
127 if (ArFlag
& CMBATT_AR_NOTIFY
)
129 /* We'll probably end up re-evaluating _BIF and _BST */
130 DeviceExtension
->NotifySent
= TRUE
;
131 BatteryClassStatusNotify(DeviceExtension
->ClassData
);
136 /* The only known notification is AC/DC change */
137 if (DeviceExtension
->ArFlag
& CMBATT_AR_NOTIFY
)
139 for (DeviceObject
= DeviceExtension
->FdoDeviceObject
->DriverObject
->DeviceObject
;
141 DeviceObject
= DeviceObject
->NextDevice
)
143 /* Is this a battery? */
144 FdoExtension
= DeviceObject
->DeviceExtension
;
145 if (FdoExtension
->FdoType
== CmBattBattery
)
147 /* Send a notification to the class driver */
148 FdoExtension
->NotifySent
= TRUE
;
149 BatteryClassStatusNotify(FdoExtension
->ClassData
);
155 /* ARs have been processed */
156 DeviceExtension
->ArFlag
= 0;
161 CmBattUnload(IN PDRIVER_OBJECT DriverObject
)
163 if (CmBattDebug
& CMBATT_GENERIC_INFO
) DPRINT("CmBattUnload: \n");
165 /* Check if we have a registered power callback */
166 if (CmBattPowerCallBackObject
)
169 ExUnregisterCallback(CmBattPowerCallBackRegistration
);
170 ObfDereferenceObject(CmBattPowerCallBackObject
);
173 /* Free the registry buffer if it exists */
174 if (GlobalRegistryPath
.Buffer
) ExFreePool(GlobalRegistryPath
.Buffer
);
176 /* Make sure we don't still have references to the DO */
177 if ((DriverObject
->DeviceObject
) && (CmBattDebug
& CMBATT_GENERIC_WARNING
))
179 DbgPrint("Unload called before all devices removed.\n");
185 CmBattVerifyStaticInfo(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
189 return STATUS_NOT_IMPLEMENTED
;
194 CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject
,
197 NTSTATUS Status
= STATUS_SUCCESS
;
198 PIO_STACK_LOCATION IoStackLocation
;
201 PCMBATT_DEVICE_EXTENSION DeviceExtension
;
203 if (CmBattDebug
& CMBATT_GENERIC_INFO
) DPRINT("CmBattOpenClose\n");
205 /* Grab the device extension and lock it */
206 DeviceExtension
= DeviceObject
->DeviceExtension
;
207 ExAcquireFastMutex(&DeviceExtension
->FastMutex
);
209 /* Check if someone is trying to open a device that doesn't exist yet */
210 Count
= DeviceExtension
->HandleCount
;
211 if (Count
== 0xFFFFFFFF)
213 /* Fail the request */
214 Status
= STATUS_NO_SUCH_DEVICE
;
215 if (CmBattDebug
& CMBATT_PNP_INFO
)
217 DbgPrint("CmBattOpenClose: Failed (UID = %x)(device being removed).\n",
218 DeviceExtension
->Tag
);
223 /* Check if this is an open or close */
224 IoStackLocation
= IoGetCurrentIrpStackLocation(Irp
);
225 Major
= IoStackLocation
->MajorFunction
;
226 if (Major
== IRP_MJ_CREATE
)
228 /* Increment the open count */
229 DeviceExtension
->HandleCount
= Count
+ 1;
230 if (CmBattDebug
& CMBATT_PNP_INFO
)
232 DbgPrint("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
233 DeviceExtension
->DeviceId
, Count
+ 1);
236 else if (Major
== IRP_MJ_CLOSE
)
238 /* Decrement the open count */
239 DeviceExtension
->HandleCount
= Count
- 1;
240 if (CmBattDebug
& CMBATT_PNP_INFO
)
242 DbgPrint("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
243 DeviceExtension
->DeviceId
, Count
+ 1);
248 /* Release lock and complete request */
249 ExReleaseFastMutex(&DeviceExtension
->FastMutex
);
250 Irp
->IoStatus
.Status
= Status
;
251 IofCompleteRequest(Irp
, IO_NO_INCREMENT
);
257 CmBattIoctl(PDEVICE_OBJECT DeviceObject
,
261 return STATUS_NOT_IMPLEMENTED
;
266 CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
269 PDEVICE_OBJECT PdoDevice
;
274 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
275 DbgPrint("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
276 *Tag
, DeviceExtension
, DeviceExtension
->DeviceId
);
278 /* Get PDO and clear notification flag */
279 PdoDevice
= DeviceExtension
->PdoDeviceObject
;
280 DeviceExtension
->NotifySent
= 0;
282 /* Get _STA from PDO (we need the machine status, not the battery status) */
283 Status
= CmBattGetStaData(PdoDevice
, &StaData
);
284 if (NT_SUCCESS(Status
))
286 /* Is a battery present? */
287 if (StaData
& ACPI_STA_BATTERY_PRESENT
)
289 /* Do we not have a tag yet? */
290 if (!DeviceExtension
->Tag
)
292 /* Set the new tag value, reset tags if we reached the maximum */
293 NewTag
= DeviceExtension
->TagData
;
294 if (DeviceExtension
->TagData
++ == 0xFFFFFFFF) NewTag
= 1;
295 DeviceExtension
->Tag
= NewTag
;
296 if (CmBattDebug
& CMBATT_GENERIC_INFO
)
297 DbgPrint("CmBattQueryTag - New Tag: (%d)\n", DeviceExtension
->Tag
);
299 /* Reset trip point data */
300 DeviceExtension
->TripPointOld
= 0;
301 DeviceExtension
->TripPointValue
= BATTERY_UNKNOWN_CAPACITY
;
303 /* Clear AR lock and set new interrupt time */
304 InterlockedExchange(&DeviceExtension
->ArLockValue
, 0);
305 DeviceExtension
->InterruptTime
= KeQueryInterruptTime();
310 /* No battery, so no tag */
311 DeviceExtension
->Tag
= 0;
312 Status
= STATUS_NO_SUCH_DEVICE
;
316 /* Return the tag and status result */
317 *Tag
= DeviceExtension
->Tag
;
318 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
319 DbgPrint("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n", *Tag
, Status
);
325 CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
)
329 if (CmBattDebug
& 0xA) DbgPrint("CmBattDisableStatusNotify\n");
331 /* Do we have a trip point */
332 if (DeviceExtension
->TripPointSet
)
334 /* Is there a current value set? */
335 if (DeviceExtension
->TripPointValue
)
337 /* Reset it back to 0 */
338 DeviceExtension
->TripPointValue
= 0;
339 Status
= CmBattSetTripPpoint(DeviceExtension
, 0);
340 if (!NT_SUCCESS(Status
))
342 /* If it failed, set unknown/invalid value */
343 DeviceExtension
->TripPointValue
= BATTERY_UNKNOWN_CAPACITY
;
345 DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status
);
350 /* No trip point set, so this is a successful no-op */
351 Status
= STATUS_SUCCESS
;
356 /* Nothing we can do */
357 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
366 CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
368 IN PBATTERY_NOTIFY BatteryNotify
)
371 ACPI_BST_DATA BstData
;
372 ULONG Capacity
, NewTripPoint
, TripPoint
, DesignVoltage
;
375 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
376 DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
377 BatteryTag
, BatteryNotify
->LowCapacity
);
379 /* Update any ACPI evaluations */
380 Status
= CmBattVerifyStaticInfo(DeviceExtension
, BatteryTag
);
381 if (!NT_SUCCESS(Status
)) return Status
;
383 /* Trip point not supported, fail */
384 if (!DeviceExtension
->TripPointSet
) return STATUS_OBJECT_NAME_NOT_FOUND
;
386 /* Are both capacities known? */
387 if ((BatteryNotify
->HighCapacity
== BATTERY_UNKNOWN_CAPACITY
) ||
388 (BatteryNotify
->LowCapacity
== BATTERY_UNKNOWN_CAPACITY
))
390 /* We can't set trip points without these */
391 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
392 DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
393 return STATUS_NOT_SUPPORTED
;
396 /* Is the battery charging? */
397 Charging
= DeviceExtension
->BstData
.State
& ACPI_BATT_STAT_CHARGING
;
400 /* Then the trip point is when we hit the cap */
401 Capacity
= BatteryNotify
->HighCapacity
;
402 NewTripPoint
= BatteryNotify
->HighCapacity
;
406 /* Otherwise it's when we discharge to the bottom */
407 Capacity
= BatteryNotify
->LowCapacity
;
408 NewTripPoint
= BatteryNotify
->LowCapacity
;
411 /* Do we have data in Amps or Watts? */
412 if (DeviceExtension
->BifData
.PowerUnit
== ACPI_BATT_POWER_UNIT_AMPS
)
414 /* We need the voltage to do the conversion */
415 DesignVoltage
= DeviceExtension
->BifData
.DesignVoltage
;
416 if ((DesignVoltage
!= BATTERY_UNKNOWN_VOLTAGE
) && (DesignVoltage
))
418 /* Convert from mAh into Ah */
419 TripPoint
= 1000 * NewTripPoint
;
422 /* Scale the high trip point */
423 NewTripPoint
= (TripPoint
+ 500) / DesignVoltage
+ ((TripPoint
+ 500) % DesignVoltage
!= 0);
427 /* Scale the low trip point */
428 NewTripPoint
= (TripPoint
- 500) / DesignVoltage
- ((TripPoint
- 500) % DesignVoltage
== 0);
433 /* Without knowing the voltage, Amps are not enough data on consumption */
434 Status
= STATUS_NOT_SUPPORTED
;
435 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
436 DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
442 /* Make it trip just one past the charge cap */
445 else if (NewTripPoint
> 0)
447 /* Make it trip just one below the drain cap */
451 /* Do we actually have a new trip point? */
452 if (NewTripPoint
== DeviceExtension
->TripPointValue
)
454 /* No, so there is no work to be done */
455 if (CmBattDebug
& CMBATT_GENERIC_STATUS
)
456 DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension
->TripPointValue
);
457 return STATUS_SUCCESS
;
460 /* Set the trip point with ACPI and check for success */
461 DeviceExtension
->TripPointValue
= NewTripPoint
;
462 Status
= CmBattSetTripPpoint(DeviceExtension
, NewTripPoint
);
463 if (!(NewTripPoint
) && (Capacity
)) Status
= STATUS_NOT_SUPPORTED
;
464 if (!NT_SUCCESS(Status
))
466 /* We failed to set the trip point, or there wasn't one settable */
467 DeviceExtension
->TripPointValue
= BATTERY_UNKNOWN_CAPACITY
;
468 if (CmBattDebug
& (CMBATT_GENERIC_WARNING
| CMBATT_ACPI_WARNING
))
469 DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status
);
473 /* Read the new BST data to see the latest state */
474 Status
= CmBattGetBstData(DeviceExtension
, &BstData
);
475 if (!NT_SUCCESS(Status
))
477 /* We'll return failure to the caller */
478 if (CmBattDebug
& (CMBATT_GENERIC_WARNING
| CMBATT_ACPI_WARNING
))
479 DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status
);
481 else if ((Charging
) && (BstData
.RemainingCapacity
>= NewTripPoint
))
483 /* We are charging and our capacity is past the trip point, so trip now */
484 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
485 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
486 NewTripPoint
, BstData
.RemainingCapacity
);
487 CmBattNotifyHandler(DeviceExtension
, ACPI_BATT_NOTIFY_STATUS
);
489 else if ((BstData
.RemainingCapacity
) && (Capacity
))
491 /* We are discharging, and our capacity is below the trip point, trip now */
492 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
493 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
494 NewTripPoint
, BstData
.RemainingCapacity
);
495 CmBattNotifyHandler(DeviceExtension
, ACPI_BATT_NOTIFY_STATUS
);
498 /* All should've went well if we got here, unless BST failed... return! */
499 if (CmBattDebug
& CMBATT_GENERIC_STATUS
)
500 DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
501 Capacity
, DeviceExtension
->RemainingCapacity
);
502 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
503 DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %x\n",
504 BatteryNotify
->PowerState
,
505 BatteryNotify
->LowCapacity
,
506 BatteryNotify
->HighCapacity
);
512 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
518 ULONG DesignVoltage
, PresentRate
, RemainingCapacity
;
520 if (CmBattDebug
& CMBATT_GENERIC_INFO
)
521 DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension
, Tag
);
523 /* Validate ACPI data */
524 Status
= CmBattVerifyStaticInfo(DeviceExtension
, Tag
);
525 if (!NT_SUCCESS(Status
)) return Status
;
527 /* Check for delayed status notifications */
528 if (DeviceExtension
->DelayNotification
)
530 /* Process them now and don't do any other work */
531 CmBattNotifyHandler(DeviceExtension
, ACPI_BATT_NOTIFY_STATUS
);
535 /* Get _BST from ACPI */
536 Status
= CmBattGetBstData(DeviceExtension
, &DeviceExtension
->BstData
);
537 if (!NT_SUCCESS(Status
))
540 InterlockedExchange(&DeviceExtension
->ArLockValue
, 0);
544 /* Clear current BST information */
545 DeviceExtension
->State
= 0;
546 DeviceExtension
->RemainingCapacity
= 0;
547 DeviceExtension
->PresentVoltage
= 0;
548 DeviceExtension
->Rate
= 0;
550 /* Get battery state */
551 BstState
= DeviceExtension
->BstData
.State
;
553 /* Is the battery both charging and discharging? */
554 if ((BstState
& ACPI_BATT_STAT_DISCHARG
) && (BstState
& ACPI_BATT_STAT_CHARGING
) &&
555 (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_WARNING
)))
556 DbgPrint("************************ ACPI BIOS BUG ********************\n* "
557 "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
558 "* One battery cannot be charging and discharging at the same time.\n",
561 /* Is the battery discharging? */
562 if (BstState
& ACPI_BATT_STAT_DISCHARG
)
564 /* Set power state and check if it just started discharging now */
565 DeviceExtension
->State
|= BATTERY_DISCHARGING
;
566 if (!(DeviceExtension
->State
& ACPI_BATT_STAT_DISCHARG
))
568 /* Remember the time when the state changed */
569 DeviceExtension
->InterruptTime
= KeQueryInterruptTime();
572 else if (BstState
& ACPI_BATT_STAT_CHARGING
)
574 /* Battery is charging, update power state */
575 DeviceExtension
->State
|= (BATTERY_CHARGING
| BATTERY_POWER_ON_LINE
);
578 /* Is the battery in a critical state? */
579 if (BstState
& ACPI_BATT_STAT_CRITICAL
) DeviceExtension
->State
|= BATTERY_CRITICAL
;
581 /* Read the voltage data */
582 DeviceExtension
->PresentVoltage
= DeviceExtension
->BstData
.PresentVoltage
;
584 /* Check if we have an A/C adapter */
587 /* Query information on it */
588 CmBattGetPsrData(AcAdapterPdo
, &PsrData
);
592 /* Otherwise, check if the battery is charging */
593 if (BstState
& ACPI_BATT_STAT_CHARGING
)
595 /* Then we'll assume there's a charger */
600 /* Assume no charger */
605 /* Is there a charger? */
608 /* Set the power state flag to reflect this */
609 DeviceExtension
->State
|= BATTERY_POWER_ON_LINE
;
610 if (CmBattDebug
& (CMBATT_GENERIC_INFO
| CMBATT_GENERIC_STATUS
))
611 DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
613 else if (CmBattDebug
& (CMBATT_GENERIC_INFO
| CMBATT_GENERIC_STATUS
))
615 DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
618 /* Get some data we'll need */
619 DesignVoltage
= DeviceExtension
->BifData
.DesignVoltage
;
620 PresentRate
= DeviceExtension
->BstData
.PresentRate
;
621 RemainingCapacity
= DeviceExtension
->BstData
.RemainingCapacity
;
623 /* Check if we have battery data in Watts instead of Amps */
624 if (DeviceExtension
->BifData
.PowerUnit
== ACPI_BATT_POWER_UNIT_WATTS
)
626 /* Get the data from the BST */
627 DeviceExtension
->RemainingCapacity
= RemainingCapacity
;
628 DeviceExtension
->Rate
= PresentRate
;
630 /* Check if the rate is invalid */
631 if (PresentRate
> CM_MAX_VALUE
)
633 /* Set an unknown rate and don't touch the old value */
634 DeviceExtension
->Rate
= BATTERY_UNKNOWN_RATE
;
635 if ((PresentRate
!= CM_UNKNOWN_VALUE
) && (CmBattDebug
& CMBATT_ACPI_WARNING
))
637 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
638 DbgPrint("---------------------- PresentRate = 0x%08x\n", PresentRate
);
642 else if ((DesignVoltage
!= CM_UNKNOWN_VALUE
) && (DesignVoltage
))
644 /* We have voltage data, what about capacity? */
645 if (RemainingCapacity
== CM_UNKNOWN_VALUE
)
647 /* Unable to calculate it */
648 DeviceExtension
->RemainingCapacity
= BATTERY_UNKNOWN_CAPACITY
;
649 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
651 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
652 DbgPrint("---------------------- RemainingCapacity = CM_UNKNOWN_VALUE\n");
657 /* Compute the capacity with the information we have */
658 DeviceExtension
->RemainingCapacity
= (DesignVoltage
* RemainingCapacity
+ 500) / 1000;
661 /* Check if we have a rate */
662 if (PresentRate
!= CM_UNKNOWN_VALUE
)
664 /* Make sure the rate isn't too large */
665 if (PresentRate
> (-500 / DesignVoltage
))
667 /* It is, so set unknown state */
668 DeviceExtension
->Rate
= BATTERY_UNKNOWN_RATE
;
669 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
671 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
672 DbgPrint("---------------------- Overflow: PresentRate = 0x%08x\n", PresentRate
);
676 /* Compute the rate */
677 DeviceExtension
->Rate
= (PresentRate
* DesignVoltage
+ 500) / 1000;
681 /* We don't have a rate, so set unknown value */
682 DeviceExtension
->Rate
= BATTERY_UNKNOWN_RATE
;
683 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
685 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
686 DbgPrint("---------------------- Present Rate = CM_UNKNOWN_VALUE\n");
692 /* We have no rate, and no capacity, set unknown values */
693 DeviceExtension
->Rate
= BATTERY_UNKNOWN_RATE
;
694 DeviceExtension
->RemainingCapacity
= BATTERY_UNKNOWN_CAPACITY
;
695 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
697 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
698 DbgPrint("---------------------- DesignVoltage = 0x%08x\n", DesignVoltage
);
702 /* Check if we have an unknown rate */
703 if (DeviceExtension
->Rate
== BATTERY_UNKNOWN_RATE
)
705 /* The battery is discharging but we don't know by how much... this is bad! */
706 if ((BstState
& ACPI_BATT_STAT_DISCHARG
) &&
707 (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_WARNING
)))
708 DbgPrint("CmBattGetBatteryStatus: battery rate is unkown when battery is not charging!\n");
710 else if (DeviceExtension
->State
& BATTERY_DISCHARGING
)
712 /* The battery is discharging, so treat the rate as a negative rate */
713 DeviceExtension
->Rate
= -DeviceExtension
->Rate
;
715 else if (!(DeviceExtension
->State
& BATTERY_CHARGING
) && (DeviceExtension
->Rate
))
717 /* We are not charging, not discharging, but have a rate? Ignore it! */
718 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
719 DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
720 DeviceExtension
->Rate
);
721 DeviceExtension
->Rate
= 0;
725 return STATUS_SUCCESS
;
730 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension
,
732 IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel
,
733 IN OPTIONAL LONG AtRate
,
735 IN ULONG BufferLength
,
736 OUT PULONG ReturnedLength
)
739 PVOID QueryData
= NULL
;
740 ULONG QueryLength
= 0;
741 ULONG RemainingTime
= 0;
742 ANSI_STRING TempString
;
743 UNICODE_STRING TempString2
;
744 WCHAR InfoBuffer
[256];
745 WCHAR TempBuffer
[256];
746 UNICODE_STRING InfoString
;
747 ULONG RemainingCapacity
;
748 BATTERY_REPORTING_SCALE BatteryReportingScale
[2];
751 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
752 DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
754 FdoExtension
->DeviceId
,
757 /* Check ACPI Data */
758 Status
= CmBattVerifyStaticInfo(FdoExtension
, Tag
);
759 if (!NT_SUCCESS(Status
)) return Status
;
761 /* Check what caller wants */
764 case BatteryInformation
:
765 /* Just return our static information */
766 QueryData
= &FdoExtension
->BatteryInformation
;
767 QueryLength
= sizeof(BATTERY_INFORMATION
);
770 case BatteryGranularityInformation
:
772 /* Return our static information, we have two scales */
773 BatteryReportingScale
[0].Granularity
= FdoExtension
->BatteryCapacityGranularity1
;
774 BatteryReportingScale
[0].Capacity
= FdoExtension
->BatteryInformation
.DefaultAlert1
;
775 BatteryReportingScale
[1].Granularity
= FdoExtension
->BatteryCapacityGranularity2
;
776 BatteryReportingScale
[1].Capacity
= FdoExtension
->BatteryInformation
.DesignedCapacity
;
777 QueryData
= BatteryReportingScale
;
778 QueryLength
= sizeof(BATTERY_REPORTING_SCALE
) * 2;
781 case BatteryEstimatedTime
:
783 /* Check if it's been more than 2 1/2 minutes since the last change */
784 if ((KeQueryInterruptTime() - 150000000) > (FdoExtension
->InterruptTime
))
786 /* Get new battery status */
787 CmBattGetBatteryStatus(FdoExtension
, FdoExtension
->Tag
);
789 /* If the caller didn't specify a rate, use our static one */
791 if (!Rate
) Rate
= FdoExtension
->Rate
;
793 /* If we don't have a valid negative rate, use unknown value */
794 if (Rate
>= 0) Rate
= BATTERY_UNKNOWN_RATE
;
796 /* Grab the remaining capacity */
797 RemainingCapacity
= FdoExtension
->RemainingCapacity
;
799 /* See if we don't know one or the other */
800 if ((Rate
== BATTERY_UNKNOWN_RATE
) ||
801 (RemainingCapacity
== BATTERY_UNKNOWN_CAPACITY
))
803 /* If the battery is discharging, we can't give out a time */
804 if ((FdoExtension
->BstData
.State
& ACPI_BATT_STAT_DISCHARG
) &&
805 (CmBattDebug
& CMBATT_GENERIC_WARNING
))
806 DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
808 /* Check if we don't have a rate and capacity is going down */
809 if ((FdoExtension
->Rate
== BATTERY_UNKNOWN_RATE
) &&
810 (FdoExtension
->BstData
.State
& ACPI_BATT_STAT_DISCHARG
))
812 /* We have to fail, since we lack data */
813 Status
= STATUS_INVALID_DEVICE_REQUEST
;
814 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
815 DbgPrint("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n");
818 /* If we don't have capacity, the rate is useless */
819 if (RemainingCapacity
== BATTERY_UNKNOWN_CAPACITY
)
821 /* We have to fail the request */
822 Status
= STATUS_INVALID_DEVICE_REQUEST
;
823 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
824 DbgPrint("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
829 /* We have data, but is it valid? */
830 if (RemainingCapacity
> 0x123456)
832 /* The capacity seems bogus, so don't use it */
833 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
834 DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
838 /* Compute the remaining time in seconds, based on rate */
839 RemainingTime
= (RemainingCapacity
* 3600) / -Rate
;
844 /* Return the remaining time */
845 QueryData
= &RemainingTime
;
846 QueryLength
= sizeof(ULONG
);
849 case BatteryDeviceName
:
851 /* Build the model number string */
852 RtlInitAnsiString(&TempString
, FdoExtension
->ModelNumber
);
854 /* Convert it to Unicode */
855 InfoString
.Buffer
= InfoBuffer
;
856 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
857 Status
= RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
859 /* Return the unicode buffer */
860 QueryData
= InfoString
.Buffer
;
861 QueryLength
= InfoString
.Length
;
864 case BatteryTemperature
:
865 case BatteryManufactureDate
:
867 /* We don't support these */
868 Status
= STATUS_INVALID_DEVICE_REQUEST
;
871 case BatteryManufactureName
:
873 /* Build the OEM info string */
874 RtlInitAnsiString(&TempString
, FdoExtension
->OemInfo
);
876 /* Convert it to Unicode */
877 InfoString
.Buffer
= InfoBuffer
;
878 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
879 Status
= RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
881 /* Return the unicode buffer */
882 QueryData
= InfoString
.Buffer
;
883 QueryLength
= InfoString
.Length
;
886 case BatteryUniqueID
:
888 /* Build the serial number string */
889 RtlInitAnsiString(&TempString
, FdoExtension
->SerialNumber
);
891 /* Convert it to Unicode */
892 InfoString
.Buffer
= InfoBuffer
;
893 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
894 RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
896 /* Setup a temporary string for concatenation */
897 TempString2
.Buffer
= TempBuffer
;
898 TempString2
.MaximumLength
= sizeof(TempBuffer
);
900 /* Check if there's an OEM string */
901 if (FdoExtension
->OemInfo
[0])
903 /* Build the OEM info string */
904 RtlInitAnsiString(&TempString
, FdoExtension
->OemInfo
);
906 /* Convert it to Unicode and append it */
907 RtlAnsiStringToUnicodeString(&TempString2
, &TempString
, 0);
908 RtlAppendUnicodeStringToString(&InfoString
, &TempString2
);
911 /* Build the model number string */
912 RtlInitAnsiString(&TempString
, FdoExtension
->ModelNumber
);
914 /* Convert it to Unicode and append it */
915 RtlAnsiStringToUnicodeString(&TempString2
, &TempString
, 0);
916 RtlAppendUnicodeStringToString(&InfoString
, &TempString2
);
918 /* Return the final appended string */
919 QueryData
= InfoString
.Buffer
;
920 QueryLength
= InfoString
.Length
;
925 /* Everything else is unknown */
926 Status
= STATUS_INVALID_PARAMETER
;
930 /* Return the required length and check if the caller supplied enough */
931 *ReturnedLength
= QueryLength
;
932 if (BufferLength
< QueryLength
) Status
= STATUS_BUFFER_TOO_SMALL
;
934 /* Copy the data if there's enough space and it exists */
935 if ((NT_SUCCESS(Status
)) && (QueryData
)) RtlCopyMemory(Buffer
, QueryData
, QueryLength
);
937 /* Return function result */
943 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
945 IN PBATTERY_STATUS BatteryStatus
)
949 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
950 DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag
, DeviceExtension
->DeviceId
);
952 /* Query ACPI information */
953 Status
= CmBattGetBatteryStatus(DeviceExtension
, Tag
);
954 if (NT_SUCCESS(Status
))
956 BatteryStatus
->PowerState
= DeviceExtension
->State
;
957 BatteryStatus
->Capacity
= DeviceExtension
->RemainingCapacity
;
958 BatteryStatus
->Voltage
= DeviceExtension
->PresentVoltage
;
959 BatteryStatus
->Rate
= DeviceExtension
->Rate
;
963 if (CmBattDebug
& (CMBATT_GENERIC_INFO
))
964 DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
965 BatteryStatus
->PowerState
,
966 BatteryStatus
->Capacity
,
967 BatteryStatus
->Voltage
,
968 BatteryStatus
->Rate
);
974 DriverEntry(IN PDRIVER_OBJECT DriverObject
,
975 IN PUNICODE_STRING RegistryPath
)
978 PDRIVER_EXTENSION DriverExtension
;
979 OBJECT_ATTRIBUTES ObjectAttributes
;
980 UNICODE_STRING CallbackName
;
982 /* Allocate registry path */
983 GlobalRegistryPath
.MaximumLength
= RegistryPath
->Length
+ sizeof(UNICODE_NULL
);
984 GlobalRegistryPath
.Length
= RegistryPath
->Length
;
985 GlobalRegistryPath
.Buffer
= ExAllocatePoolWithTag(PagedPool
,
986 GlobalRegistryPath
.MaximumLength
,
988 if (!GlobalRegistryPath
.Buffer
)
990 /* Fail if we're out of memory this early */
991 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
992 DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
993 return STATUS_INSUFFICIENT_RESOURCES
;
996 /* Buffer allocated, copy the string */
997 RtlCopyUnicodeString(&GlobalRegistryPath
, RegistryPath
);
998 if (CmBattDebug
& CMBATT_GENERIC_INFO
)
999 DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
1001 RegistryPath
->Buffer
);
1003 /* Setup the major dispatchers */
1004 DriverObject
->MajorFunction
[0] = CmBattOpenClose
;
1005 DriverObject
->MajorFunction
[2] = CmBattOpenClose
;
1006 DriverObject
->MajorFunction
[14] = CmBattIoctl
;
1007 DriverObject
->MajorFunction
[22] = CmBattPowerDispatch
;
1008 DriverObject
->MajorFunction
[27] = CmBattPnpDispatch
;
1009 DriverObject
->MajorFunction
[23] = CmBattSystemControl
;
1011 /* And the unload routine */
1012 DriverObject
->DriverUnload
= CmBattUnload
;
1014 /* And the add device routine */
1015 DriverExtension
= DriverObject
->DriverExtension
;
1016 DriverExtension
->AddDevice
= CmBattAddDevice
;
1018 /* Create a power callback */
1019 RtlInitUnicodeString(&CallbackName
, L
"\\Callback\\PowerState");
1020 InitializeObjectAttributes(&ObjectAttributes
,
1025 Status
= ExCreateCallback(&CmBattPowerCallBackObject
, &ObjectAttributes
, 0, TRUE
);
1026 if (!NT_SUCCESS(Status
))
1028 /* No callback, fail */
1029 CmBattPowerCallBackObject
= 0;
1030 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
1031 DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status
);
1035 /* Register the power callback now */
1036 CmBattPowerCallBackRegistration
= ExRegisterCallback(CmBattPowerCallBackObject
,
1037 (PVOID
)CmBattPowerCallBack
,
1039 if (CmBattPowerCallBackRegistration
)
1041 /* Last thing: setup our DPC and timer for battery wake */
1042 KeInitializeDpc(&CmBattWakeDpcObject
, (PVOID
)CmBattWakeDpc
, DriverObject
);
1043 KeInitializeTimer(&CmBattWakeDpcTimerObject
);
1047 ObfDereferenceObject(CmBattPowerCallBackObject
);
1048 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
1049 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1053 Status
= STATUS_SUCCESS
;
1056 /* Return failure or success */