You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2338 lines
72 KiB
2338 lines
72 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
CompBatt.c
|
|
|
|
Abstract:
|
|
|
|
Composite Battery device functions
|
|
|
|
The purpose of the composite battery device is to open all batteries
|
|
in the system which supply system power and provide a logical sumation
|
|
of the information under one battery device.
|
|
|
|
Author:
|
|
|
|
Ken Reneris
|
|
|
|
Environment:
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
07/02/97: Local cache timestamps/timeouts
|
|
|
|
--*/
|
|
|
|
#include "compbatt.h"
|
|
|
|
#if DEBUG
|
|
#if DBG
|
|
ULONG CompBattDebug = BATT_ERRORS;
|
|
#else
|
|
ULONG CompBattDebug = 0;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(PAGE, CompBattUnload)
|
|
#pragma alloc_text(PAGE, CompBattIoctl)
|
|
#pragma alloc_text(PAGE, CompBattQueryTag)
|
|
#pragma alloc_text(PAGE, CompBattQueryInformation)
|
|
#pragma alloc_text(PAGE, CompBattQueryStatus)
|
|
#pragma alloc_text(PAGE, CompBattSetStatusNotify)
|
|
#pragma alloc_text(PAGE, CompBattDisableStatusNotify)
|
|
#pragma alloc_text(PAGE, CompBattGetBatteryInformation)
|
|
#pragma alloc_text(PAGE, CompBattGetBatteryGranularity)
|
|
#pragma alloc_text(PAGE, CompBattGetEstimatedTime)
|
|
#endif
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The first time the battery class driver is loaded it will check to
|
|
see if the composite battery has been created. If not, it will create
|
|
a driver object with this routine as the DriverEntry. This routine
|
|
then does the necessary things to initialize the composite battery.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Driver object for newly created driver
|
|
|
|
RegistryPath - Not used
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
|
|
// DbgBreakPoint ();
|
|
|
|
//
|
|
// Initialize the driver entry points
|
|
//
|
|
|
|
//DriverObject->DriverUnload = CompBattUnload;
|
|
DriverObject->DriverExtension->AddDevice = CompBattAddDevice;
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CompBattIoctl;
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = CompBattOpenClose;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = CompBattOpenClose;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = CompBattPnpDispatch;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = CompBattPowerDispatch;
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CompBattSystemControl;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattAddDevice (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PDO
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
|
|
PDO - PDO for the new device(s)
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT fdo;
|
|
BATTERY_MINIPORT_INFO BattInit;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING DosLinkName;
|
|
PCOMPOSITE_BATTERY compBatt;
|
|
|
|
|
|
BattPrint (BATT_NOTE, ("CompBatt: Got an AddDevice - %x\n", PDO));
|
|
|
|
//
|
|
// Build the composite battery device and register it to the
|
|
// battery class driver (i.e., ourselves)
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"\\Device\\CompositeBattery");
|
|
|
|
Status = IoCreateDevice(
|
|
DriverObject,
|
|
sizeof (COMPOSITE_BATTERY),
|
|
&UnicodeString,
|
|
FILE_DEVICE_BATTERY,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&fdo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
RtlInitUnicodeString(&DosLinkName, L"\\DosDevices\\CompositeBattery");
|
|
IoCreateSymbolicLink(&DosLinkName, &UnicodeString);
|
|
|
|
//
|
|
// Layer our FDO on top of the PDO.
|
|
//
|
|
|
|
compBatt = (PCOMPOSITE_BATTERY) fdo->DeviceExtension;
|
|
RtlZeroMemory (compBatt, sizeof(COMPOSITE_BATTERY));
|
|
|
|
compBatt->LowerDevice = IoAttachDeviceToDeviceStack (fdo,PDO);
|
|
|
|
compBatt->DeviceObject = fdo;
|
|
|
|
//
|
|
// No status. Do the best we can.
|
|
//
|
|
|
|
if (!compBatt->LowerDevice) {
|
|
BattPrint (BATT_ERROR, ("CompBattAddDevice: Could not attach to LowerDevice.\n"));
|
|
IoDeleteDevice(fdo);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Initialize composite battery info
|
|
//
|
|
|
|
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
|
|
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
InitializeListHead (&compBatt->Batteries);
|
|
ExInitializeFastMutex (&compBatt->ListMutex);
|
|
|
|
compBatt->NextTag = 1; // first valid battery tag for composite
|
|
compBatt->Info.Valid = 0;
|
|
|
|
RtlZeroMemory (&BattInit, sizeof(BattInit));
|
|
BattInit.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
|
|
BattInit.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
|
|
BattInit.Context = compBatt;
|
|
BattInit.QueryTag = CompBattQueryTag;
|
|
BattInit.QueryInformation = CompBattQueryInformation;
|
|
BattInit.SetInformation = NULL;
|
|
BattInit.QueryStatus = CompBattQueryStatus;
|
|
BattInit.SetStatusNotify = CompBattSetStatusNotify;
|
|
BattInit.DisableStatusNotify = CompBattDisableStatusNotify;
|
|
|
|
BattInit.Pdo = NULL;
|
|
BattInit.DeviceName = &UnicodeString;
|
|
|
|
//
|
|
// Register myself with the battery class driver
|
|
//
|
|
|
|
Status = BatteryClassInitializeDevice (&BattInit, &compBatt->Class);
|
|
if (!NT_SUCCESS(Status)) {
|
|
IoDetachDevice(compBatt->LowerDevice);
|
|
IoDeleteDevice(fdo);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
CompBattUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup all devices and unload the driver
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Driver object for unload
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
DbgBreakPoint();
|
|
|
|
//
|
|
// Unloading the composite battery is not supported.
|
|
// If it were implemented, we would
|
|
// need to call the class driver's unload and then
|
|
// delete all nodes in the battery list, clean up
|
|
// then delete our FDO.
|
|
//
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattOpenClose(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING OpenClose\n"));
|
|
|
|
|
|
//
|
|
// Complete the request and return status.
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: Exiting OpenClose\n"));
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattIoctl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IOCTL handler. As this is an exclusive battery device, send the
|
|
Irp to the battery class driver to handle battery IOCTLs.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Battery for request
|
|
|
|
Irp - IO request
|
|
|
|
Return Value:
|
|
|
|
Status of request
|
|
|
|
--*/
|
|
{
|
|
PCOMPOSITE_BATTERY compBatt;
|
|
NTSTATUS status;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING Ioctl\n"));
|
|
|
|
compBatt = (PCOMPOSITE_BATTERY) DeviceObject->DeviceExtension;
|
|
status = BatteryClassIoctl (compBatt->Class, Irp);
|
|
|
|
|
|
if (status == STATUS_NOT_SUPPORTED) {
|
|
//
|
|
// Not for the battery, pass it down the stack.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
status = IoCallDriver(compBatt->LowerDevice, Irp);
|
|
}
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING Ioctl\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattSystemControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine forwards System Control requests down the stack
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object in question
|
|
Irp - the request to forward
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PCOMPOSITE_BATTERY compBatt;
|
|
NTSTATUS status;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING System Control\n"));
|
|
|
|
compBatt = (PCOMPOSITE_BATTERY) DeviceObject->DeviceExtension;
|
|
if (compBatt->LowerDevice != NULL) {
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
status = IoCallDriver( compBatt->LowerDevice, Irp );
|
|
|
|
} else {
|
|
|
|
Irp->IoStatus.Status = status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CompBattQueryTag (
|
|
IN PVOID Context,
|
|
OUT PULONG BatteryTag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the class driver to retrieve the batteries current tag value
|
|
|
|
Arguments:
|
|
|
|
Context - Miniport context value for battery
|
|
|
|
BatteryTag - Pointer to return current tag
|
|
|
|
|
|
Return Value:
|
|
|
|
Success if there is a battery currently installed, else no such device.
|
|
|
|
--*/
|
|
{
|
|
PCOMPOSITE_BATTERY compBatt;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryTag\n"));
|
|
|
|
compBatt = (PCOMPOSITE_BATTERY) Context;
|
|
|
|
if (!(compBatt->Info.Valid & VALID_TAG)) {
|
|
//
|
|
// Recalculate the composite's tag.
|
|
//
|
|
|
|
CompBattRecalculateTag(compBatt);
|
|
|
|
}
|
|
|
|
if ((compBatt->Info.Valid & VALID_TAG) && (compBatt->Info.Tag != BATTERY_TAG_INVALID)) {
|
|
*BatteryTag = compBatt->Info.Tag;
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
*BatteryTag = BATTERY_TAG_INVALID;
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryTag\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattQueryInformation (
|
|
IN PVOID Context,
|
|
IN ULONG BatteryTag,
|
|
IN BATTERY_QUERY_INFORMATION_LEVEL Level,
|
|
IN LONG AtRate,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferLength,
|
|
OUT PULONG ReturnedLength
|
|
)
|
|
{
|
|
ULONG resultData;
|
|
NTSTATUS status;
|
|
PVOID returnBuffer;
|
|
ULONG returnBufferLength;
|
|
PCOMPOSITE_BATTERY compBatt;
|
|
BATTERY_INFORMATION totalBattInfo;
|
|
BATTERY_REPORTING_SCALE granularity[4];
|
|
BATTERY_MANUFACTURE_DATE date;
|
|
WCHAR compositeName[] = L"Composite Battery";
|
|
|
|
PAGED_CODE();
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryInformation\n"));
|
|
|
|
compBatt = (PCOMPOSITE_BATTERY) Context;
|
|
|
|
if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
|
|
returnBuffer = NULL;
|
|
returnBufferLength = 0;
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// Get the info requested
|
|
//
|
|
|
|
switch (Level) {
|
|
case BatteryInformation:
|
|
|
|
RtlZeroMemory (&totalBattInfo, sizeof(totalBattInfo));
|
|
status = CompBattGetBatteryInformation (&totalBattInfo, compBatt);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
returnBuffer = &totalBattInfo;
|
|
returnBufferLength = sizeof(totalBattInfo);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BatteryGranularityInformation:
|
|
|
|
RtlZeroMemory (&granularity[0], sizeof(granularity));
|
|
status = CompBattGetBatteryGranularity (&granularity[0], compBatt);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
returnBuffer = &granularity[0];
|
|
returnBufferLength = sizeof(granularity);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BatteryTemperature:
|
|
resultData = 0;
|
|
returnBuffer = &resultData;
|
|
returnBufferLength = sizeof (resultData);
|
|
break;
|
|
|
|
|
|
case BatteryEstimatedTime:
|
|
|
|
RtlZeroMemory (&resultData, sizeof(resultData));
|
|
status = CompBattGetEstimatedTime (&resultData, compBatt);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
returnBuffer = &resultData;
|
|
returnBufferLength = sizeof(resultData);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BatteryDeviceName:
|
|
returnBuffer = compositeName;
|
|
returnBufferLength = sizeof (compositeName);
|
|
break;
|
|
|
|
|
|
case BatteryManufactureDate:
|
|
date.Day = 26;
|
|
date.Month = 6;
|
|
date.Year = 1997;
|
|
returnBuffer = &date;
|
|
returnBufferLength = sizeof (date);
|
|
break;
|
|
|
|
|
|
case BatteryManufactureName:
|
|
returnBuffer = compositeName;
|
|
returnBufferLength = sizeof (compositeName);
|
|
break;
|
|
|
|
|
|
case BatteryUniqueID:
|
|
resultData = 0;
|
|
returnBuffer = &resultData;
|
|
returnBufferLength = sizeof (resultData);
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make sure nothing changed while reading batteries.
|
|
//
|
|
|
|
if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Done, return buffer if needed
|
|
//
|
|
|
|
*ReturnedLength = returnBufferLength;
|
|
if (BufferLength < returnBufferLength) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (NT_SUCCESS(status) && returnBuffer) {
|
|
memcpy (Buffer, returnBuffer, returnBufferLength);
|
|
}
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryInformation\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattQueryStatus (
|
|
IN PVOID Context,
|
|
IN ULONG BatteryTag,
|
|
OUT PBATTERY_STATUS BatteryStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the class driver to retrieve the batteries current status. This
|
|
routine loops through all of the batteries in the system and reports a
|
|
composite battery.
|
|
|
|
Arguments:
|
|
|
|
Context - Miniport context value for battery
|
|
|
|
BatteryTag - Tag of current battery
|
|
|
|
BatteryStatus - Pointer to structure to return the current battery status
|
|
|
|
Return Value:
|
|
|
|
Success if there is a battery currently installed, else no such device.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PCOMPOSITE_ENTRY batt;
|
|
PLIST_ENTRY entry;
|
|
PBATTERY_STATUS localBatteryStatus;
|
|
PCOMPOSITE_BATTERY compBatt;
|
|
BATTERY_WAIT_STATUS batteryWaitStatus;
|
|
ULONGLONG wallClockTime;
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryStatus\n"));
|
|
|
|
compBatt = (PCOMPOSITE_BATTERY) Context;
|
|
|
|
if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Initialize Composite data structure.
|
|
//
|
|
|
|
BatteryStatus->Rate = BATTERY_UNKNOWN_RATE;
|
|
BatteryStatus->Voltage = BATTERY_UNKNOWN_VOLTAGE;
|
|
BatteryStatus->Capacity = BATTERY_UNKNOWN_CAPACITY;
|
|
|
|
// Composite battery will only report POWER_ON_LINE if all batteries report
|
|
// this flag.
|
|
BatteryStatus->PowerState = BATTERY_POWER_ON_LINE;
|
|
|
|
//
|
|
// Set up the local battery status structure for calls to the batteries
|
|
//
|
|
|
|
RtlZeroMemory (&batteryWaitStatus, sizeof (BATTERY_WAIT_STATUS));
|
|
|
|
//
|
|
// Get current time for timestamps
|
|
//
|
|
|
|
wallClockTime = KeQueryInterruptTime ();
|
|
|
|
//
|
|
// If cache is fresh, no need to do anything
|
|
//
|
|
|
|
if ((wallClockTime - compBatt->Info.StatusTimeStamp) <= CACHE_STATUS_TIMEOUT) {
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattQueryStatus: Composite battery status cache is [valid]\n"));
|
|
|
|
//
|
|
// Copy status info to caller's buffer
|
|
//
|
|
RtlCopyMemory (BatteryStatus, &compBatt->Info.Status, sizeof (BATTERY_STATUS));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattQueryStatus: Composite battery status cache is [stale] - refreshing\n"));
|
|
|
|
//
|
|
// Walk the list of batteries, getting status of each
|
|
//
|
|
|
|
ExAcquireFastMutex (&compBatt->ListMutex);
|
|
for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
ExReleaseFastMutex (&compBatt->ListMutex);
|
|
|
|
batteryWaitStatus.BatteryTag = batt->Info.Tag;
|
|
localBatteryStatus = &batt->Info.Status;
|
|
|
|
if (batt->Info.Valid & VALID_TAG) {
|
|
|
|
//
|
|
// If cached status for this battery is stale, refresh it
|
|
//
|
|
|
|
if ((wallClockTime - batt->Info.StatusTimeStamp) > CACHE_STATUS_TIMEOUT) {
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattQueryStatus: Battery status cache is [stale] - refreshing\n"));
|
|
|
|
//
|
|
// issue IOCTL to device
|
|
//
|
|
|
|
RtlZeroMemory (localBatteryStatus, sizeof(BATTERY_STATUS));
|
|
|
|
status = BatteryIoctl (IOCTL_BATTERY_QUERY_STATUS,
|
|
batt->DeviceObject,
|
|
&batteryWaitStatus,
|
|
sizeof (BATTERY_WAIT_STATUS),
|
|
localBatteryStatus,
|
|
sizeof (BATTERY_STATUS),
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// In case of failure, this function should simply return the
|
|
// status code. Invalidating of data is now performed only
|
|
// in MonitorIrpComplete.
|
|
//
|
|
// This raises the slight possibility that the sender of this
|
|
// request could retry before the data is properly invalidated,
|
|
// but worst case, they would again get this same error condition
|
|
// until the data is properly invalidated by MonitorIrpComplete.
|
|
//
|
|
|
|
if (status == STATUS_DEVICE_REMOVED) {
|
|
|
|
//
|
|
// This battery is being removed.
|
|
// The composite battery tag is or will soon be
|
|
// invalidated by MonitorIrpComplete.
|
|
//
|
|
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Return failure code.
|
|
//
|
|
|
|
ExAcquireFastMutex (&compBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
break;
|
|
}
|
|
|
|
// Set new timestamp
|
|
|
|
batt->Info.StatusTimeStamp = wallClockTime;
|
|
|
|
} else {
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattQueryStatus: Battery status cache is [valid]\n"));
|
|
}
|
|
|
|
|
|
//
|
|
// Accumulate data.
|
|
//
|
|
|
|
|
|
//
|
|
// Combine the power states.
|
|
//
|
|
|
|
// Logical OR CHARGING and DISCHARGING
|
|
BatteryStatus->PowerState |= (localBatteryStatus->PowerState &
|
|
(BATTERY_CHARGING |
|
|
BATTERY_DISCHARGING));
|
|
|
|
// Logical AND POWER_ON_LINE
|
|
BatteryStatus->PowerState &= (localBatteryStatus->PowerState |
|
|
~BATTERY_POWER_ON_LINE);
|
|
|
|
// Compbatt is critical if one battery is critical and discharging
|
|
if ((localBatteryStatus->PowerState & BATTERY_CRITICAL) &&
|
|
(localBatteryStatus->PowerState & BATTERY_DISCHARGING)) {
|
|
BatteryStatus->PowerState |= BATTERY_CRITICAL;
|
|
}
|
|
|
|
//
|
|
// The Capacity could possibly be "Unknown" for CMBatt, and if so
|
|
// we should not add it to the total capacity.
|
|
//
|
|
|
|
if (BatteryStatus->Capacity == BATTERY_UNKNOWN_CAPACITY) {
|
|
BatteryStatus->Capacity = localBatteryStatus->Capacity;
|
|
} else if (localBatteryStatus->Capacity != BATTERY_UNKNOWN_CAPACITY) {
|
|
BatteryStatus->Capacity += localBatteryStatus->Capacity;
|
|
}
|
|
|
|
//
|
|
// The Voltage should just be the greatest one encountered.
|
|
//
|
|
|
|
if (BatteryStatus->Voltage == BATTERY_UNKNOWN_VOLTAGE) {
|
|
BatteryStatus->Voltage = localBatteryStatus->Voltage;
|
|
} else if ((localBatteryStatus->Voltage > BatteryStatus->Voltage) &&
|
|
(localBatteryStatus->Voltage != BATTERY_UNKNOWN_VOLTAGE)) {
|
|
BatteryStatus->Voltage = localBatteryStatus->Voltage;
|
|
}
|
|
|
|
//
|
|
// The Current should just be total of all currents encountered. This could
|
|
// also possibly be "Unknown" for CMBatt, and if so we should not use it
|
|
// in the calculation.
|
|
//
|
|
|
|
if (BatteryStatus->Rate == BATTERY_UNKNOWN_RATE) {
|
|
BatteryStatus->Rate = localBatteryStatus->Rate;
|
|
} else if (localBatteryStatus->Rate != BATTERY_UNKNOWN_RATE) {
|
|
BatteryStatus->Rate += localBatteryStatus->Rate;
|
|
}
|
|
|
|
} // if (batt->Tag != BATTERY_TAG_INVALID)
|
|
|
|
ExAcquireFastMutex (&compBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
} // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink)
|
|
|
|
ExReleaseFastMutex (&compBatt->ListMutex);
|
|
|
|
|
|
//
|
|
// If one battery was discharging while another was charging
|
|
// Assume that it is discharging. (This could happen with a UPS attached)
|
|
//
|
|
if ((BatteryStatus->PowerState & BATTERY_CHARGING) &&
|
|
(BatteryStatus->PowerState & BATTERY_DISCHARGING)) {
|
|
BatteryStatus->PowerState &= ~BATTERY_CHARGING;
|
|
}
|
|
|
|
//
|
|
// Make sure nothing changed while reading batteries.
|
|
//
|
|
|
|
if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Save the status in the composites cache
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
RtlCopyMemory (&compBatt->Info.Status, BatteryStatus, sizeof (BATTERY_STATUS));
|
|
|
|
compBatt->Info.StatusTimeStamp = wallClockTime;
|
|
|
|
BattPrint (BATT_DATA, ("CompBatt: Composite's new Status\n"
|
|
"-------- PowerState = %x\n"
|
|
"-------- Capacity = %x\n"
|
|
"-------- Voltage = %x\n"
|
|
"-------- Rate = %x\n",
|
|
compBatt->Info.Status.PowerState,
|
|
compBatt->Info.Status.Capacity,
|
|
compBatt->Info.Status.Voltage,
|
|
compBatt->Info.Status.Rate)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryStatus\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattSetStatusNotify (
|
|
IN PVOID Context,
|
|
IN ULONG BatteryTag,
|
|
IN PBATTERY_NOTIFY BatteryNotify
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the class driver to set the batteries current notification
|
|
setting. When the battery trips the notification, one call to
|
|
BatteryClassStatusNotify is issued. If an error is returned, the
|
|
class driver will poll the battery status - primarily for capacity
|
|
changes. Which is to say the miniport should still issue BatteryClass-
|
|
StatusNotify whenever the power state changes.
|
|
|
|
|
|
Arguments:
|
|
|
|
Context - Miniport context value for battery
|
|
|
|
BatteryTag - Tag of current battery
|
|
|
|
BatteryNotify - The notification setting
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PCOMPOSITE_ENTRY batt;
|
|
PLIST_ENTRY entry;
|
|
PCOMPOSITE_BATTERY compBatt;
|
|
BATTERY_STATUS batteryStatus;
|
|
LONG totalRate = 0;
|
|
ULONG delta;
|
|
ULONG highCapacityDelta;
|
|
ULONG lowCapacityDelta;
|
|
NTSTATUS status;
|
|
BOOLEAN inconsistent = FALSE;
|
|
ULONG battCount = 0;
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING SetStatusNotify\n"));
|
|
|
|
compBatt = (PCOMPOSITE_BATTERY) Context;
|
|
|
|
//
|
|
// Check to see if this is the right battery
|
|
//
|
|
|
|
if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Refresh the composite battery status cache if necessary.
|
|
//
|
|
|
|
status = CompBattQueryStatus (compBatt, BatteryTag, &batteryStatus);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// Save away the composite notification parameters for future reference
|
|
//
|
|
|
|
compBatt->Wait.PowerState = BatteryNotify->PowerState;
|
|
compBatt->Wait.LowCapacity = BatteryNotify->LowCapacity;
|
|
compBatt->Wait.HighCapacity = BatteryNotify->HighCapacity;
|
|
compBatt->Info.Valid |= VALID_NOTIFY;
|
|
|
|
BattPrint (BATT_DATA, ("CompBatt: Got SetStatusNotify\n"
|
|
"-------- PowerState = %x\n"
|
|
"-------- LowCapacity = %x\n"
|
|
"-------- HighCapacity = %x\n",
|
|
compBatt->Wait.PowerState,
|
|
compBatt->Wait.LowCapacity,
|
|
compBatt->Wait.HighCapacity)
|
|
);
|
|
|
|
//
|
|
// Compute capacity deltas based on the total system capacity
|
|
//
|
|
|
|
lowCapacityDelta = compBatt->Info.Status.Capacity - BatteryNotify->LowCapacity;
|
|
highCapacityDelta = BatteryNotify->HighCapacity - compBatt->Info.Status.Capacity;
|
|
|
|
//
|
|
// Run through the list of batteries and add up the total rate
|
|
//
|
|
|
|
//
|
|
// Hold Mutex for this entire loop, since this loop doesn't call any drivers, etc
|
|
//
|
|
ExAcquireFastMutex (&compBatt->ListMutex);
|
|
|
|
for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
|
|
if (!(batt->Info.Valid & VALID_TAG) || (batt->Info.Status.Rate == BATTERY_UNKNOWN_RATE)) {
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
continue;
|
|
}
|
|
|
|
battCount++;
|
|
|
|
if (((batt->Info.Status.PowerState & BATTERY_DISCHARGING) && (batt->Info.Status.Rate >= 0)) ||
|
|
((batt->Info.Status.PowerState & BATTERY_CHARGING) && (batt->Info.Status.Rate <= 0)) ||
|
|
(((batt->Info.Status.PowerState & (BATTERY_CHARGING | BATTERY_DISCHARGING)) == 0) && (batt->Info.Status.Rate != 0))) {
|
|
inconsistent = TRUE;
|
|
BattPrint (BATT_ERROR, ("CompBatt: PowerState 0x%08x does not match Rate 0x%08x\n",
|
|
batt->Info.Status.PowerState,
|
|
batt->Info.Status.Rate));
|
|
}
|
|
|
|
if (((batt->Info.Status.Rate < 0) ^ (totalRate < 0)) && (batt->Info.Status.Rate != 0) && (totalRate != 0)) {
|
|
inconsistent = TRUE;
|
|
BattPrint (BATT_ERROR, ("CompBatt: It appears that one battery is charging while another is discharging.\n"
|
|
" This situation is not handled correctly.\n"));
|
|
}
|
|
|
|
totalRate += batt->Info.Status.Rate;
|
|
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
|
|
}
|
|
ExReleaseFastMutex (&compBatt->ListMutex);
|
|
|
|
//
|
|
// Run through the list of batteries and update the new wait status params
|
|
//
|
|
|
|
ExAcquireFastMutex (&compBatt->ListMutex);
|
|
for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
|
|
ExReleaseFastMutex (&compBatt->ListMutex);
|
|
|
|
if (!(batt->Info.Valid & VALID_TAG) ||
|
|
(batt->Info.Status.Capacity == BATTERY_UNKNOWN_CAPACITY)) {
|
|
batt->Wait.LowCapacity = BATTERY_UNKNOWN_CAPACITY;
|
|
batt->Wait.HighCapacity = BATTERY_UNKNOWN_CAPACITY;
|
|
|
|
#if DEBUG
|
|
if (batt->Info.Status.Capacity == BATTERY_UNKNOWN_CAPACITY) {
|
|
BattPrint (BATT_DEBUG, ("CompBattSetStatusNotify: Unknown Capacity encountered.\n"));
|
|
}
|
|
#endif
|
|
|
|
ExAcquireFastMutex (&compBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Adjust the LowCapacity alarm
|
|
//
|
|
|
|
//
|
|
// Calculate the portion of the composite battery delta that belongs to
|
|
// this battery.
|
|
//
|
|
|
|
if (inconsistent) {
|
|
//
|
|
// If data returned from batteries was inconsistent, don't do anything intelligent.
|
|
// Just divide the notifications evenly between the batteries as if they were
|
|
// draining at the same rate. This will most likely result in early notification,
|
|
// but by that time the data on the batteries ought to have settled down.
|
|
//
|
|
delta = lowCapacityDelta/battCount;
|
|
} else if (totalRate != 0) {
|
|
delta = (ULONG) (((LONGLONG) lowCapacityDelta * batt->Info.Status.Rate) / totalRate);
|
|
} else {
|
|
|
|
//
|
|
// If total rate is zero, we would expect no change in battery
|
|
// capacity, so we should get notified of any.
|
|
//
|
|
|
|
delta = 0;
|
|
}
|
|
|
|
//
|
|
// Check for underflow on low capacity
|
|
//
|
|
|
|
|
|
if (batt->Info.Status.Capacity > delta) {
|
|
batt->Wait.LowCapacity = batt->Info.Status.Capacity - delta;
|
|
|
|
} else {
|
|
//
|
|
// If there is still some charge in the battery set the LowCapacity
|
|
// alarm to 1, else to 0.
|
|
//
|
|
// No need to do that. If this battery runs out, it doesn't
|
|
// need to notify. One of the other batteries will be notifying
|
|
// right away. If there isn't another battery, this shouldn't
|
|
// happen.
|
|
|
|
BattPrint (BATT_NOTE, ("CompBatt: Unexpectedly huge delta encountered. \n"
|
|
" Capacity = %08x\n"
|
|
" LowCapcityDelta = %08x\n"
|
|
" Rate = %08x\n"
|
|
" TotalRate = %08x\n",
|
|
batt->Info.Status.Capacity,
|
|
lowCapacityDelta,
|
|
batt->Info.Status.Rate,
|
|
totalRate));
|
|
batt->Wait.LowCapacity = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Adjust the HighCapacity alarm for charging batteries only
|
|
//
|
|
|
|
//
|
|
// Calculate the portion of the composite battery delta that belongs to
|
|
// this battery.
|
|
//
|
|
|
|
if (inconsistent) {
|
|
delta = highCapacityDelta/battCount;
|
|
} else if (totalRate != 0) {
|
|
delta = (ULONG) (((LONGLONG) highCapacityDelta * batt->Info.Status.Rate) / totalRate);
|
|
} else {
|
|
|
|
//
|
|
// If total rate is zero, we would expect no change in battery
|
|
// capacity, so we should get notified of any.
|
|
//
|
|
|
|
delta = 0;
|
|
}
|
|
|
|
//
|
|
// Check for overflow on high capacity.
|
|
// Allow setting the percentage above full charged capacity
|
|
// since some batteries do that when new.
|
|
//
|
|
|
|
if ((MAX_HIGH_CAPACITY - delta) < batt->Info.Status.Capacity) {
|
|
batt->Wait.HighCapacity = MAX_HIGH_CAPACITY;
|
|
} else {
|
|
batt->Wait.HighCapacity = batt->Info.Status.Capacity + delta;
|
|
}
|
|
|
|
//
|
|
// If we're currently waiting, and the parameters are in
|
|
// conflict, get the irp back to reset it
|
|
//
|
|
|
|
if (batt->State == CB_ST_GET_STATUS &&
|
|
(batt->Wait.PowerState != batt->IrpBuffer.Wait.PowerState ||
|
|
batt->Wait.LowCapacity != batt->IrpBuffer.Wait.LowCapacity ||
|
|
batt->Wait.HighCapacity != batt->IrpBuffer.Wait.HighCapacity)) {
|
|
|
|
IoCancelIrp (batt->StatusIrp);
|
|
}
|
|
|
|
ExAcquireFastMutex (&compBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
}
|
|
ExReleaseFastMutex (&compBatt->ListMutex);
|
|
|
|
//
|
|
// Make sure nothing changed while reading batteries.
|
|
//
|
|
|
|
if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING SetStatusNotify\n"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattDisableStatusNotify (
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the class driver to disable the notification setting
|
|
for the battery supplied by Context. Note, to disable a setting
|
|
does not require the battery tag. Any notification is to be
|
|
masked off until a subsequent call to SmbBattSetStatusNotify.
|
|
|
|
Arguments:
|
|
|
|
Context - Miniport context value for battery
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PCOMPOSITE_ENTRY batt;
|
|
PLIST_ENTRY entry;
|
|
PCOMPOSITE_BATTERY compBatt;
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING DisableStatusNotify\n"));
|
|
|
|
compBatt = (PCOMPOSITE_BATTERY) Context;
|
|
|
|
//
|
|
// Run through the list of batteries and disable the wait status params
|
|
// Hold mutex for entire loop, since loop doesn't make any calls.
|
|
|
|
ExAcquireFastMutex (&compBatt->ListMutex);
|
|
for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
batt->Wait.LowCapacity = MIN_LOW_CAPACITY;
|
|
batt->Wait.HighCapacity = MAX_HIGH_CAPACITY;
|
|
}
|
|
ExReleaseFastMutex (&compBatt->ListMutex);
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING DisableStatusNotify\n"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattGetBatteryInformation (
|
|
IN PBATTERY_INFORMATION TotalBattInfo,
|
|
IN PCOMPOSITE_BATTERY CompBatt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine loops through the batteries in the system and queries them
|
|
for information. It then forms a composite representation of this
|
|
information to send back to the caller.
|
|
|
|
Arguments:
|
|
|
|
TotalBattInfo - Buffer to place the composite battery information in
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or the status returned by the Ioctl to the battery.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PBATTERY_INFORMATION battInfo;
|
|
PCOMPOSITE_ENTRY batt;
|
|
PLIST_ENTRY entry;
|
|
BATTERY_QUERY_INFORMATION bInfo;
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetBatteryInformation\n"));
|
|
|
|
TotalBattInfo->DefaultAlert1 = 0;
|
|
TotalBattInfo->DefaultAlert2 = 0;
|
|
TotalBattInfo->CriticalBias = 0;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Run through the list of batteries getting the information
|
|
//
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
bInfo.BatteryTag = batt->Info.Tag;
|
|
bInfo.InformationLevel = BatteryInformation;
|
|
bInfo.AtRate = 0;
|
|
battInfo = &batt->Info.Info;
|
|
|
|
if (batt->Info.Tag != BATTERY_TAG_INVALID) {
|
|
if (!(batt->Info.Valid & VALID_INFO)) {
|
|
|
|
//
|
|
// issue IOCTL to device
|
|
//
|
|
|
|
RtlZeroMemory (battInfo, sizeof(BATTERY_INFORMATION));
|
|
|
|
status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION,
|
|
batt->DeviceObject,
|
|
&bInfo,
|
|
sizeof (bInfo),
|
|
battInfo,
|
|
sizeof (BATTERY_INFORMATION),
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (status == STATUS_DEVICE_REMOVED) {
|
|
//
|
|
// If one device is removed, that invalidates the tag.
|
|
//
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
break;
|
|
}
|
|
|
|
BattPrint (BATT_DATA, ("CompBattGetBatteryInformation: Read individual BATTERY_INFORMATION\n"
|
|
"-------- Capabilities = %x\n"
|
|
"-------- Technology = %x\n"
|
|
"-------- Chemistry[4] = %x\n"
|
|
"-------- DesignedCapacity = %x\n"
|
|
"-------- FullChargedCapacity = %x\n"
|
|
"-------- DefaultAlert1 = %x\n"
|
|
"-------- DefaultAlert2 = %x\n"
|
|
"-------- CriticalBias = %x\n"
|
|
"-------- CycleCount = %x\n",
|
|
battInfo->Capabilities,
|
|
battInfo->Technology,
|
|
battInfo->Chemistry[4],
|
|
battInfo->DesignedCapacity,
|
|
battInfo->FullChargedCapacity,
|
|
battInfo->DefaultAlert1,
|
|
battInfo->DefaultAlert2,
|
|
battInfo->CriticalBias,
|
|
battInfo->CycleCount)
|
|
);
|
|
|
|
batt->Info.Valid |= VALID_INFO;
|
|
|
|
} // if (!(batt->Info.Valid & VALID_INFO))
|
|
|
|
//
|
|
// Logically OR the capabilities
|
|
//
|
|
|
|
TotalBattInfo->Capabilities |= battInfo->Capabilities;
|
|
|
|
|
|
//
|
|
// Add the designed capacities. If this is UNKNOWN (possible
|
|
// with the control method batteries, don't add them in.
|
|
//
|
|
|
|
if (battInfo->DesignedCapacity != BATTERY_UNKNOWN_CAPACITY) {
|
|
TotalBattInfo->DesignedCapacity += battInfo->DesignedCapacity;
|
|
}
|
|
|
|
if (battInfo->FullChargedCapacity != BATTERY_UNKNOWN_CAPACITY) {
|
|
TotalBattInfo->FullChargedCapacity += battInfo->FullChargedCapacity;
|
|
}
|
|
|
|
if (TotalBattInfo->DefaultAlert1 < battInfo->DefaultAlert1) {
|
|
TotalBattInfo->DefaultAlert1 = battInfo->DefaultAlert1;
|
|
}
|
|
|
|
if (TotalBattInfo->DefaultAlert2 < battInfo->DefaultAlert2) {
|
|
TotalBattInfo->DefaultAlert2 = battInfo->DefaultAlert2;
|
|
}
|
|
|
|
if (TotalBattInfo->CriticalBias < battInfo->CriticalBias) {
|
|
TotalBattInfo->CriticalBias = battInfo->CriticalBias;
|
|
}
|
|
|
|
} // if (batt->Tag != BATTERY_TAG_INVALID)
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
} // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink)
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
//
|
|
// Save the battery information in the composite battery cache
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Check to see if we have an UNKNOWN full charge capacity. If so, set this
|
|
// to the design capacity.
|
|
//
|
|
|
|
if (TotalBattInfo->FullChargedCapacity == 0) {
|
|
TotalBattInfo->FullChargedCapacity = TotalBattInfo->DesignedCapacity;
|
|
}
|
|
|
|
BattPrint (BATT_DATA, ("CompBattGetBatteryInformation: Returning BATTERY_INFORMATION\n"
|
|
"-------- Capabilities = %x\n"
|
|
"-------- Technology = %x\n"
|
|
"-------- Chemistry[4] = %x\n"
|
|
"-------- DesignedCapacity = %x\n"
|
|
"-------- FullChargedCapacity = %x\n"
|
|
"-------- DefaultAlert1 = %x\n"
|
|
"-------- DefaultAlert2 = %x\n"
|
|
"-------- CriticalBias = %x\n"
|
|
"-------- CycleCount = %x\n",
|
|
TotalBattInfo->Capabilities,
|
|
TotalBattInfo->Technology,
|
|
TotalBattInfo->Chemistry[4],
|
|
TotalBattInfo->DesignedCapacity,
|
|
TotalBattInfo->FullChargedCapacity,
|
|
TotalBattInfo->DefaultAlert1,
|
|
TotalBattInfo->DefaultAlert2,
|
|
TotalBattInfo->CriticalBias,
|
|
TotalBattInfo->CycleCount)
|
|
);
|
|
|
|
RtlCopyMemory (&CompBatt->Info.Info, TotalBattInfo, sizeof(BATTERY_INFORMATION));
|
|
CompBatt->Info.Valid |= VALID_INFO;
|
|
}
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING GetBatteryInformation\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattGetBatteryGranularity (
|
|
IN PBATTERY_REPORTING_SCALE GranularityBuffer,
|
|
IN PCOMPOSITE_BATTERY CompBatt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine queries all the batteries in the system to get their granularity
|
|
settings. It then returns the setting that has the finest granularity in each range.
|
|
|
|
Arguments:
|
|
|
|
GranularityBuffer - Buffer for containing the results of the query
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or the status returned by the Ioctl to the battery.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BATTERY_REPORTING_SCALE localGranularity[4];
|
|
PCOMPOSITE_ENTRY batt;
|
|
PLIST_ENTRY entry;
|
|
ULONG i;
|
|
BATTERY_QUERY_INFORMATION bInfo;
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetBatteryGranularity\n"));
|
|
|
|
GranularityBuffer[0].Granularity = 0xFFFFFFFF;
|
|
GranularityBuffer[1].Granularity = 0xFFFFFFFF;
|
|
GranularityBuffer[2].Granularity = 0xFFFFFFFF;
|
|
GranularityBuffer[3].Granularity = 0xFFFFFFFF;
|
|
|
|
//
|
|
// Run through the list of batteries getting the granularity
|
|
//
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
bInfo.BatteryTag = batt->Info.Tag;
|
|
bInfo.InformationLevel = BatteryGranularityInformation;
|
|
|
|
if (batt->Info.Tag != BATTERY_TAG_INVALID) {
|
|
//
|
|
// issue IOCTL to device
|
|
//
|
|
|
|
RtlZeroMemory (localGranularity, sizeof(localGranularity));
|
|
|
|
status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION,
|
|
batt->DeviceObject,
|
|
&bInfo,
|
|
sizeof (bInfo),
|
|
localGranularity,
|
|
sizeof (localGranularity),
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (status == STATUS_DEVICE_REMOVED) {
|
|
//
|
|
// If one device is removed, that invalidates the tag.
|
|
//
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Check for the best granularity in each range.
|
|
//
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
if (localGranularity[i].Granularity) {
|
|
|
|
if (localGranularity[i].Granularity < GranularityBuffer[i].Granularity) {
|
|
GranularityBuffer[i].Granularity = localGranularity[i].Granularity;
|
|
}
|
|
|
|
GranularityBuffer[i].Capacity = localGranularity[i].Capacity;
|
|
}
|
|
|
|
}
|
|
|
|
} // if (batt->Tag != BATTERY_TAG_INVALID)
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
} // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink)
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING GetBatteryGranularity\n"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattGetEstimatedTime (
|
|
IN PULONG TimeBuffer,
|
|
IN PCOMPOSITE_BATTERY CompBatt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine queries all the batteries in the system to get their estimated time left.
|
|
If one of the batteries in the system does not support this function then an error
|
|
is returned.
|
|
|
|
Arguments:
|
|
|
|
TimeBuffer - Buffer for containing cumulative time left
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or the status returned by the Ioctl to the battery.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
LONG localBuffer = 0;
|
|
PCOMPOSITE_ENTRY batt;
|
|
PLIST_ENTRY entry;
|
|
BATTERY_QUERY_INFORMATION bInfo;
|
|
BATTERY_STATUS batteryStatus;
|
|
LONG atRate = 0;
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetEstimatedTime\n"));
|
|
|
|
*TimeBuffer = BATTERY_UNKNOWN_TIME;
|
|
|
|
//
|
|
// Refresh the composite battery status cache if necessary.
|
|
//
|
|
|
|
status = CompBattQueryStatus (CompBatt, CompBatt->Info.Tag, &batteryStatus);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// If we're on AC then our estimated run time is invalid.
|
|
//
|
|
|
|
if (CompBatt->Info.Status.PowerState & BATTERY_POWER_ON_LINE) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// We are on battery power and may have more than one battery in the system.
|
|
//
|
|
// We need to find the total rate of power being drawn from all batteries
|
|
// then we need to ask how long each battery would last at that rate (as
|
|
// if they were being discharged one at a time). This should give us a fairly
|
|
// good measure of how long it will last.
|
|
//
|
|
// To find the power being drawn we read the devide the remaining capacity by
|
|
// the estimated time rather than simply reading the rate. This is because the
|
|
// rate is theoretically the instantanious current wereas the estimated time
|
|
// should be based on average usage. This isn't the case for control method
|
|
// batteries, but it is for smart batteries, and could be for others as well.
|
|
//
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
if (batt->Info.Valid & VALID_TAG) {
|
|
|
|
bInfo.BatteryTag = batt->Info.Tag;
|
|
bInfo.InformationLevel = BatteryEstimatedTime;
|
|
bInfo.AtRate = 0;
|
|
|
|
//
|
|
// issue IOCTL to device
|
|
//
|
|
|
|
status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION,
|
|
batt->DeviceObject,
|
|
&bInfo,
|
|
sizeof (bInfo),
|
|
&localBuffer,
|
|
sizeof (localBuffer),
|
|
FALSE);
|
|
|
|
|
|
if ((localBuffer != BATTERY_UNKNOWN_TIME) &&
|
|
(localBuffer != 0) &&
|
|
(NT_SUCCESS(status))) {
|
|
atRate -= ((long)batt->Info.Status.Capacity)*3600 / localBuffer;
|
|
BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: EstTime: %08x, Capacity: %08x, cumulative AtRate: %08x\n", localBuffer, batt->Info.Status.Capacity, atRate));
|
|
} else {
|
|
BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: Bad Estimated time. Status: %08x, localBuffer: %08x, Capacity: %08x, cumulative AtRate: %08x\n",
|
|
status, localBuffer, batt->Info.Status.Capacity, atRate));
|
|
}
|
|
}
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
|
|
}
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: using atRate - %x\n", atRate));
|
|
|
|
//
|
|
// Did we find a battery?
|
|
//
|
|
if (atRate == 0) {
|
|
// Code could be added to here to handle batteries that return
|
|
// estimated runtime, but not rate information.
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Run through the list of batteries getting their estimated time
|
|
//
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
bInfo.BatteryTag = batt->Info.Tag;
|
|
bInfo.InformationLevel = BatteryEstimatedTime;
|
|
|
|
bInfo.AtRate = atRate;
|
|
|
|
if (batt->Info.Valid & VALID_TAG) {
|
|
//
|
|
// issue IOCTL to device
|
|
//
|
|
|
|
status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION,
|
|
batt->DeviceObject,
|
|
&bInfo,
|
|
sizeof (bInfo),
|
|
&localBuffer,
|
|
sizeof (localBuffer),
|
|
FALSE);
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: Status: %08x, EstTime: %08x\n", status, localBuffer));
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// This could be an invalid device request for this battery.
|
|
// Continue with thte next battery.
|
|
//
|
|
|
|
if (status == STATUS_DEVICE_REMOVED) {
|
|
//
|
|
// If one device is removed, that invalidates the tag.
|
|
//
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Add the estimated time.
|
|
//
|
|
if (localBuffer != BATTERY_UNKNOWN_TIME) {
|
|
if (*TimeBuffer == BATTERY_UNKNOWN_TIME) {
|
|
*TimeBuffer = localBuffer;
|
|
} else {
|
|
*TimeBuffer += localBuffer;
|
|
}
|
|
}
|
|
BattPrint (BATT_DATA, ("CompBattGetEstimatedTime: cumulative time: %08x\n", *TimeBuffer));
|
|
|
|
} // if (batt->Tag != BATTERY_TAG_INVALID)
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
} // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink)
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING GetEstimatedTime\n"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompBattMonitorIrpComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constantly keeps an irp at the battery either querying for the tag or the
|
|
status. This routine fills in the irp, sets itself up as the completion
|
|
routine, and then resends the irp.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object for the battery sent the irp.
|
|
Note: In this case DeviceObject is always NULL, so don't use it.
|
|
|
|
Irp - Current irp to work with
|
|
|
|
Context - Currently unused
|
|
|
|
Return Value:
|
|
|
|
TRUE if there are no changes, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PCOMPOSITE_ENTRY Batt;
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING MonitorIrpComplete\n"));
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
Batt = IrpSp->Parameters.Others.Argument2;
|
|
|
|
//
|
|
// We always want to queue a work item to recycle the IRP. There were too many
|
|
// problems that could happen trying to recycle in the completion routine.
|
|
//
|
|
// If this driver ever gets reworked, it could be done that way, but it would take
|
|
// more time to get right than I have now. Queueing a work item is the safe thing
|
|
// to do.
|
|
//
|
|
|
|
ExQueueWorkItem (&Batt->WorkItem, DelayedWorkQueue);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
VOID CompBattMonitorIrpCompleteWorker (
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is either queued, or called by the completion routine.
|
|
|
|
Constantly keeps an irp at the battery either querying for the tag or the
|
|
status. This routine fills in the irp, sets itself up as the completion
|
|
routine, and then resends the irp.
|
|
|
|
Arguments:
|
|
|
|
Context - Composite battery entry.
|
|
|
|
Return Value:
|
|
|
|
TRUE if there are no changes, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PCOMPOSITE_ENTRY Batt = (PCOMPOSITE_ENTRY) Context;
|
|
PDEVICE_OBJECT DeviceObject = Batt->DeviceObject;
|
|
PIRP Irp = Batt->StatusIrp;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PCOMPOSITE_BATTERY compBatt = IrpSp->Parameters.Others.Argument1;
|
|
BATTERY_STATUS battStatus;
|
|
ULONG oldPowerState;
|
|
NTSTATUS status;
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING MonitorIrpCompleteWorker\n"));
|
|
|
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Reissue irp to battery to wait for a status change
|
|
//
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->IoStatus.Status == STATUS_CANCELLED) {
|
|
switch (Batt->State) {
|
|
case CB_ST_GET_TAG:
|
|
//
|
|
// A battery was just inserted, so the IOCTL_BATTERY_Query_TAG completed.
|
|
//
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: got tag for %x\n", Batt->DeviceObject));
|
|
|
|
//
|
|
// Update the tag, and wait on status
|
|
//
|
|
Batt->Wait.BatteryTag = Batt->IrpBuffer.Tag;
|
|
Batt->Info.Tag = Batt->IrpBuffer.Tag;
|
|
Batt->Info.Valid = VALID_TAG;
|
|
|
|
// Invalidate all cached info.
|
|
compBatt->Info.Valid = 0;
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n"));
|
|
BatteryClassStatusNotify (compBatt->Class);
|
|
break;
|
|
|
|
|
|
case CB_ST_GET_STATUS:
|
|
//
|
|
// IOCTL_BATTERY_QUERY_STATUS just completed. This could mean that the
|
|
// battery state changed, or the charge has left the acceptable range.
|
|
// If the battery was removed, it would not get here.
|
|
//
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: got status for %x\n", Batt->DeviceObject));
|
|
|
|
if (!(Irp->IoStatus.Status == STATUS_CANCELLED)) {
|
|
|
|
BattPrint (BATT_NOTE, ("Battery's state is\n"
|
|
"-------- PowerState = %x\n"
|
|
"-------- Capacity = %x\n"
|
|
"-------- Voltage = %x\n"
|
|
"-------- Rate = %x\n",
|
|
Batt->IrpBuffer.Status.PowerState,
|
|
Batt->IrpBuffer.Status.Capacity,
|
|
Batt->IrpBuffer.Status.Voltage,
|
|
Batt->IrpBuffer.Status.Rate)
|
|
);
|
|
|
|
|
|
//
|
|
// Battery status completed sucessfully.
|
|
// Update our wait, and wait some more
|
|
//
|
|
|
|
Batt->Wait.PowerState = Batt->IrpBuffer.Status.PowerState;
|
|
|
|
if (Batt->IrpBuffer.Status.Capacity != BATTERY_UNKNOWN_CAPACITY) {
|
|
if (Batt->Wait.HighCapacity < Batt->IrpBuffer.Status.Capacity) {
|
|
Batt->Wait.HighCapacity = Batt->IrpBuffer.Status.Capacity;
|
|
}
|
|
|
|
if (Batt->Wait.LowCapacity > Batt->IrpBuffer.Status.Capacity) {
|
|
Batt->Wait.LowCapacity = Batt->IrpBuffer.Status.Capacity;
|
|
}
|
|
} else {
|
|
BattPrint (BATT_DEBUG, ("CompBattMonitorIrpCompleteWorker: Unknown Capacity encountered.\n"));
|
|
Batt->Wait.LowCapacity = BATTERY_UNKNOWN_CAPACITY;
|
|
Batt->Wait.HighCapacity = BATTERY_UNKNOWN_CAPACITY;
|
|
}
|
|
|
|
RtlCopyMemory (&Batt->Info.Status, &Batt->IrpBuffer.Status, sizeof(BATTERY_STATUS));
|
|
|
|
//
|
|
// Set timestamp to Now.
|
|
//
|
|
|
|
Batt->Info.StatusTimeStamp = KeQueryInterruptTime ();
|
|
|
|
//
|
|
// Recalculate the charge/discharge policy and change if needed
|
|
//
|
|
|
|
// Don't change default BIOS policy for discharge.
|
|
// CompBattChargeDischarge (compBatt);
|
|
|
|
//
|
|
// Save the composite's old PowerState and recalculate the composites
|
|
// overall status.
|
|
//
|
|
|
|
oldPowerState = compBatt->Info.Status.PowerState;
|
|
compBatt->Info.StatusTimeStamp = 0; // -CACHE_STATUS_TIMEOUT; // Invalidate cache
|
|
CompBattQueryStatus (compBatt, compBatt->Info.Tag, &battStatus);
|
|
|
|
//
|
|
// Check to see if we need to send a notification on the composite
|
|
// battery. This will be done in a couple of different cases:
|
|
//
|
|
// - There is a VALID_NOTIFY and there was a change in the composite's
|
|
// PowerState, or it went below the Notify.LowCapacity, or it went
|
|
// above the Notify.HighCapacity.
|
|
//
|
|
// - There is no VALID_NOTIFY (SetStatusNotify) and there was a change
|
|
// in the composite's PowerState.
|
|
//
|
|
|
|
if (compBatt->Info.Valid & VALID_NOTIFY) {
|
|
if ((compBatt->Info.Status.PowerState != compBatt->Wait.PowerState) ||
|
|
(compBatt->Info.Status.Capacity < compBatt->Wait.LowCapacity) ||
|
|
(compBatt->Info.Status.Capacity > compBatt->Wait.HighCapacity)) {
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n"));
|
|
BatteryClassStatusNotify (compBatt->Class);
|
|
}
|
|
} else {
|
|
if (compBatt->Info.Status.PowerState != oldPowerState) {
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n"));
|
|
BatteryClassStatusNotify (compBatt->Class);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: recycling cancelled status irp\n"));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
BattPrint (BATT_ERROR, ("CompBatt: internal error - bad state\n"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set irp to issue query
|
|
//
|
|
|
|
#if DEBUG
|
|
if ((Batt->Wait.LowCapacity > 0xf0000000) && (Batt->Wait.LowCapacity != BATTERY_UNKNOWN_CAPACITY)) {
|
|
BattPrint (BATT_ERROR, ("CompBattMonitorIrpCompleteWorker: LowCapacity < 0, LowCapacity =%x\n",
|
|
Batt->Wait.LowCapacity));
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif
|
|
|
|
Batt->State = CB_ST_GET_STATUS;
|
|
Batt->Wait.Timeout = (ULONG) -1;
|
|
RtlCopyMemory (&Batt->IrpBuffer.Wait, &Batt->Wait, sizeof (Batt->Wait));
|
|
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_STATUS;
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(Batt->IrpBuffer.Wait);
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(Batt->IrpBuffer.Status);
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: waiting for status, Irp - %x\n", Irp));
|
|
BattPrint (BATT_NOTE, ("-------- PowerState = %x\n"
|
|
"-------- LowCapacity = %x\n"
|
|
"-------- HighCapacity = %x\n",
|
|
Batt->Wait.PowerState,
|
|
Batt->Wait.LowCapacity,
|
|
Batt->Wait.HighCapacity)
|
|
);
|
|
|
|
} else if (Irp->IoStatus.Status == STATUS_DEVICE_REMOVED) {
|
|
|
|
//
|
|
// If the Battery class driver returned STATUS_DEVICE_REMOVED, then the
|
|
// device has been removed, so we need to quit sending IRPs.
|
|
//
|
|
|
|
BattPrint (BATT_NOTE, ("Compbatt: MonitorIrpCompleteWorker detected device removal.\n"));
|
|
CompBattRemoveBattery (&Batt->BattName, compBatt);
|
|
IoFreeIrp (Irp);
|
|
|
|
return;
|
|
|
|
} else {
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: battery disappeared (status:%08x)\n",
|
|
Irp->IoStatus.Status));
|
|
|
|
//
|
|
// Invalidate the battery's tag, and the individual battery's cache, and
|
|
// recalculate the composite's tag
|
|
//
|
|
|
|
Batt->Info.Tag = BATTERY_TAG_INVALID;
|
|
Batt->Info.Valid = 0;
|
|
compBatt->Info.Valid = 0;
|
|
compBatt->Info.StatusTimeStamp = 0; // Invalidate cache
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n"));
|
|
BatteryClassStatusNotify (compBatt->Class);
|
|
|
|
Batt->State = CB_ST_GET_TAG;
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_TAG;
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ULONG);
|
|
Batt->IrpBuffer.Tag = (ULONG) -1;
|
|
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: getting tag (last error %x)\n",
|
|
Irp->IoStatus.Status));
|
|
|
|
}
|
|
|
|
|
|
IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
Irp->AssociatedIrp.SystemBuffer = &Batt->IrpBuffer;
|
|
Irp->PendingReturned = FALSE;
|
|
Irp->Cancel = FALSE;
|
|
|
|
IoSetCompletionRoutine (Irp, CompBattMonitorIrpComplete, NULL, TRUE, TRUE, TRUE);
|
|
status = IoCallDriver (Batt->DeviceObject, Irp);
|
|
BattPrint (BATT_NOTE, ("Compbatt: MonitorIrpCompleteWorker: CallDriver returned 0x%lx.\n", status));
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING MonitorIrpCompleteWorker\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
CompBattRecalculateTag (
|
|
IN PCOMPOSITE_BATTERY CompBatt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine checks to see if there is still a valid battery in the
|
|
composite's list. If so, the composite tag is bumped. This also
|
|
invalidates all but the composite's tag.
|
|
|
|
Arguments:
|
|
|
|
CompBatt - Composite device extension
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PCOMPOSITE_ENTRY batt;
|
|
PLIST_ENTRY entry;
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING CompBattRecalculateTag\n"));
|
|
|
|
|
|
//
|
|
// Run through the list of batteries looking for one that is still good
|
|
//
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
|
|
if (batt->Info.Valid & VALID_TAG) {
|
|
CompBatt->Info.Valid |= VALID_TAG;
|
|
CompBatt->Info.Tag = CompBatt->NextTag++;
|
|
break;
|
|
}
|
|
|
|
CompBatt->Info.Tag = BATTERY_TAG_INVALID;
|
|
}
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING CompBattRecalculateTag\n"));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
CompBattChargeDischarge (
|
|
IN PCOMPOSITE_BATTERY CompBatt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine calculates which battery should be charging/discharging
|
|
and attempts to make it so. Policy is summarized below:
|
|
|
|
CHARGING POLICY:
|
|
The most charged battery that is also less than 90% of maximum capacity
|
|
is charged first.
|
|
|
|
DISCHARGING POLICY:
|
|
The most discharged battery that is also more than 2% of empty is discharged
|
|
first, until it is empty.
|
|
|
|
Arguments:
|
|
|
|
CompBatt - Composite device extension
|
|
|
|
Return Value:
|
|
|
|
NONE. Nobody really cares if this works or not, since it won't work on all batteries.
|
|
|
|
--*/
|
|
{
|
|
PCOMPOSITE_ENTRY batt;
|
|
PLIST_ENTRY entry;
|
|
ULONG capacity;
|
|
ULONG percentCapacity;
|
|
ULONG targetCapacity;
|
|
PCOMPOSITE_ENTRY targetBattery;
|
|
BATTERY_SET_INFORMATION battSetInfo;
|
|
NTSTATUS status;
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: ENTERING CompBattChargeDischarge\n"));
|
|
|
|
targetBattery = NULL;
|
|
|
|
//
|
|
// Check if AC is present in the system.
|
|
//
|
|
|
|
if (CompBatt->Info.Status.PowerState & BATTERY_POWER_ON_LINE) {
|
|
|
|
//
|
|
// AC is present. Examine all batteries, looking for the most
|
|
// charged one that is less than 90% full.
|
|
//
|
|
|
|
targetCapacity = 0;
|
|
battSetInfo.InformationLevel = BatteryCharge;
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
|
|
if (batt->Info.Valid & VALID_TAG) {
|
|
|
|
//
|
|
// Get the battery max capacity and current % of capacity
|
|
//
|
|
|
|
capacity = batt->Info.Info.FullChargedCapacity;
|
|
if (capacity == 0) {
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
break;
|
|
}
|
|
|
|
percentCapacity = (batt->Info.Status.Capacity * 100) / capacity;
|
|
|
|
//
|
|
// Is this the most charged battery AND < 90% full?
|
|
//
|
|
|
|
if ((capacity > targetCapacity) && (percentCapacity < BATTERY_MAX_CHARGE_CAPACITY)) {
|
|
|
|
//
|
|
// Yes, this one is in the running for the one to charge
|
|
//
|
|
|
|
targetCapacity = capacity;
|
|
targetBattery = batt;
|
|
}
|
|
}
|
|
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
|
|
}
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattChargeDischarge: Setting battery %x to CHARGE (AC present)\n",
|
|
targetBattery));
|
|
|
|
} else {
|
|
|
|
//
|
|
// We are running on battery power. Examine all batteries, looking
|
|
// for the one with the least capacity that is greater than some small
|
|
// safety margin (say 2%).
|
|
//
|
|
|
|
targetCapacity = -1;
|
|
battSetInfo.InformationLevel = BatteryDischarge;
|
|
|
|
ExAcquireFastMutex (&CompBatt->ListMutex);
|
|
for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
|
|
|
|
batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
|
|
|
|
if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
|
|
continue;
|
|
}
|
|
|
|
if (batt->Info.Valid & VALID_TAG) {
|
|
|
|
//
|
|
// Get the battery max capacity and current % of capacity
|
|
//
|
|
|
|
capacity = batt->Info.Info.FullChargedCapacity;
|
|
if (capacity == 0) {
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
break;
|
|
}
|
|
|
|
percentCapacity = (batt->Info.Status.Capacity * 100) / capacity;
|
|
|
|
//
|
|
// Is this the least charged battery AND has a safety margin?
|
|
//
|
|
|
|
if ((capacity < targetCapacity) && (percentCapacity > BATTERY_MIN_SAFE_CAPACITY)) {
|
|
|
|
//
|
|
// Yes, this one is in the running for the one to discharge
|
|
//
|
|
|
|
targetCapacity = capacity;
|
|
targetBattery = batt;
|
|
}
|
|
}
|
|
|
|
CompbattReleaseDeleteLock(&batt->DeleteLock);
|
|
|
|
}
|
|
ExReleaseFastMutex (&CompBatt->ListMutex);
|
|
|
|
BattPrint (BATT_NOTE, ("CompBattChargeDischarge: Setting battery %x to DISCHARGE (no AC)\n",
|
|
targetBattery));
|
|
|
|
}
|
|
|
|
//
|
|
// If we have found a suitable battery, complete the setup and send off the Ioctl
|
|
//
|
|
|
|
if (targetBattery != NULL) {
|
|
|
|
battSetInfo.BatteryTag = targetBattery->Info.Tag;
|
|
|
|
//
|
|
// Make the Ioctl to the battery. This won't always be successful, since some
|
|
// batteries don't support it. For example, no control-method batteries support
|
|
// software charging decisions. Some smart batteries do, however.
|
|
//
|
|
|
|
status = BatteryIoctl (IOCTL_BATTERY_SET_INFORMATION,
|
|
batt->DeviceObject,
|
|
&battSetInfo,
|
|
sizeof (BATTERY_SET_INFORMATION),
|
|
NULL,
|
|
0,
|
|
FALSE);
|
|
|
|
}
|
|
|
|
|
|
BattPrint (BATT_TRACE, ("CompBatt: EXITING CompBattChargeDischarge\n"));
|
|
}
|