1857 lines
61 KiB
C
1857 lines
61 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
CmBatt.c
|
||
|
||
Abstract:
|
||
|
||
Control Method Battery Miniport Driver
|
||
|
||
Author:
|
||
|
||
Ron Mosgrove (Intel)
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "CmBattp.h"
|
||
|
||
|
||
#if DEBUG
|
||
#if DBG
|
||
ULONG CmBattDebug = CMBATT_ERROR;
|
||
#else
|
||
// Turn off all debug info by default for free builds.
|
||
ULONG CmBattDebug = 0;
|
||
#endif //DBG
|
||
#endif //DEBUG
|
||
|
||
#ifndef _WIN32_WINNT
|
||
ULONG CmBattPrevPowerSource = 1;
|
||
#endif //_WIN32_WINNT
|
||
|
||
UNICODE_STRING GlobalRegistryPath;
|
||
|
||
PVOID CmBattPowerCallBackRegistration;
|
||
PCALLBACK_OBJECT CmBattPowerCallBackObject;
|
||
KDPC CmBattWakeDpcObject;
|
||
KTIMER CmBattWakeDpcTimerObject;
|
||
|
||
LARGE_INTEGER CmBattWakeDpcDelay = WAKE_DPC_DELAY;
|
||
|
||
//
|
||
// Prototypes
|
||
//
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
NTSTATUS
|
||
CmBattOpenClose(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
CmBattIoctl(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
CmBattUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
NTSTATUS
|
||
CmBattGetBatteryStatus(
|
||
PCM_BATT CmBatt,
|
||
IN ULONG BatteryTag
|
||
);
|
||
|
||
NTSTATUS
|
||
CmBattGetSetAlarm(
|
||
IN PCM_BATT CmBatt,
|
||
IN OUT PULONG AlarmPtr,
|
||
IN UCHAR OpType
|
||
);
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,DriverEntry)
|
||
#pragma alloc_text(PAGE,CmBattQueryTag)
|
||
#pragma alloc_text(PAGE,CmBattQueryInformation)
|
||
#pragma alloc_text(PAGE,CmBattQueryStatus)
|
||
#pragma alloc_text(PAGE,CmBattSetStatusNotify)
|
||
#pragma alloc_text(PAGE,CmBattDisableStatusNotify)
|
||
#pragma alloc_text(PAGE,CmBattUnload)
|
||
#pragma alloc_text(PAGE,CmBattOpenClose)
|
||
#pragma alloc_text(PAGE,CmBattIoctl)
|
||
#pragma alloc_text(PAGE,CmBattGetBatteryStatus)
|
||
#endif
|
||
|
||
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the ACPI Embedded Controller Driver
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to driver object created by system.
|
||
|
||
RegistryPath - Pointer to the Unicode name of the registry path
|
||
for this driver.
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status from the initialization operation.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
OBJECT_ATTRIBUTES objAttributes;
|
||
UNICODE_STRING callBackName;
|
||
|
||
|
||
//
|
||
// Save the RegistryPath.
|
||
//
|
||
|
||
GlobalRegistryPath.MaximumLength = RegistryPath->Length +
|
||
sizeof(UNICODE_NULL);
|
||
GlobalRegistryPath.Length = RegistryPath->Length;
|
||
GlobalRegistryPath.Buffer = ExAllocatePoolWithTag (
|
||
PagedPool,
|
||
GlobalRegistryPath.MaximumLength,
|
||
'MtaB');
|
||
|
||
if (!GlobalRegistryPath.Buffer) {
|
||
|
||
CmBattPrint ((CMBATT_ERROR),("CmBatt: Couldn't allocate pool for registry path."));
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
|
||
|
||
CmBattPrint (CMBATT_TRACE, ("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
|
||
DriverObject, RegistryPath->Buffer));
|
||
//
|
||
// Set up the device driver entry points.
|
||
//
|
||
DriverObject->DriverUnload = CmBattUnload;
|
||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
|
||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
|
||
DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
|
||
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
|
||
DriverObject->DriverExtension->AddDevice = CmBattAddDevice;
|
||
|
||
|
||
//
|
||
// Register a callback that tells us when the system is in the
|
||
// process of sleeping or waking.
|
||
//
|
||
RtlInitUnicodeString( &callBackName, L"\\Callback\\PowerState" );
|
||
InitializeObjectAttributes(
|
||
&objAttributes,
|
||
&callBackName,
|
||
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
|
||
NULL,
|
||
NULL
|
||
);
|
||
status = ExCreateCallback(
|
||
&CmBattPowerCallBackObject,
|
||
&objAttributes,
|
||
FALSE,
|
||
TRUE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
CmBattPowerCallBackRegistration = ExRegisterCallback(
|
||
CmBattPowerCallBackObject,
|
||
(PCALLBACK_FUNCTION) CmBattPowerCallBack,
|
||
DriverObject
|
||
);
|
||
if (CmBattPowerCallBackRegistration) {
|
||
KeInitializeDpc (&CmBattWakeDpcObject,
|
||
(PKDEFERRED_ROUTINE) CmBattWakeDpc,
|
||
DriverObject);
|
||
KeInitializeTimer (&CmBattWakeDpcTimerObject);
|
||
} else {
|
||
ObDereferenceObject (CmBattPowerCallBackObject);
|
||
CmBattPrint (CMBATT_ERROR, ("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n"));
|
||
}
|
||
} else {
|
||
CmBattPowerCallBackObject = NULL;
|
||
CmBattPrint (CMBATT_ERROR, ("CmBattRegisterPowerCallBack: failed status=0x%08x\n", status));
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
CmBattUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cleanup all devices and unload the driver
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Driver object for unload
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
|
||
CmBattPrint (CMBATT_TRACE, ("CmBattUnload: \n"));
|
||
|
||
if (CmBattPowerCallBackObject) {
|
||
ExUnregisterCallback (CmBattPowerCallBackRegistration);
|
||
ObDereferenceObject (CmBattPowerCallBackObject);
|
||
}
|
||
|
||
if (GlobalRegistryPath.Buffer) {
|
||
ExFreePool (GlobalRegistryPath.Buffer);
|
||
}
|
||
|
||
if (DriverObject->DeviceObject != NULL) {
|
||
CmBattPrint (CMBATT_ERROR, ("Unload called before all devices removed.\n"));
|
||
}
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CmBattOpenClose(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the routine called as a result of a Open or Close on the device
|
||
|
||
Arguments:
|
||
|
||
|
||
DeviceObject - Battery for request
|
||
Irp - IO request
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - no way to fail this puppy
|
||
If Device has received a query remove, this will fail.
|
||
STATUS_NO_SUCH_DEVICE
|
||
|
||
--*/
|
||
{
|
||
PCM_BATT CmBatt;
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION irpStack;
|
||
|
||
PAGED_CODE();
|
||
|
||
CmBattPrint (CMBATT_TRACE, ("CmBattOpenClose\n"));
|
||
|
||
CmBatt = (PCM_BATT) DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// A remove lock is not needed in this dispatch function because
|
||
// all data accessed is in the device extension. If any other functionality was
|
||
// added to this routine, a remove lock might be neccesary.
|
||
//
|
||
|
||
status = STATUS_SUCCESS; // Success by default.
|
||
|
||
ExAcquireFastMutex (&CmBatt->OpenCloseMutex);
|
||
if (CmBatt->OpenCount == (ULONG) -1) { // A query remove has come requested
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
CmBattPrint (CMBATT_PNP, ("CmBattOpenClose: Failed (UID = %x)(device being removed).\n", CmBatt->Info.Tag));
|
||
} else {
|
||
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
if (irpStack->MajorFunction == IRP_MJ_CREATE) {
|
||
CmBatt->OpenCount++;
|
||
CmBattPrint (CMBATT_PNP, ("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
|
||
CmBatt->DeviceNumber, CmBatt->OpenCount));
|
||
} else if (irpStack->MajorFunction == IRP_MJ_CLOSE) {
|
||
CmBatt->OpenCount--;
|
||
CmBattPrint (CMBATT_PNP, ("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
|
||
CmBatt->DeviceNumber, CmBatt->OpenCount));
|
||
}
|
||
}
|
||
ExReleaseFastMutex (&CmBatt->OpenCloseMutex);
|
||
|
||
//
|
||
// Complete Irp.
|
||
//
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CmBattIoctl(
|
||
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
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_NOT_SUPPORTED;
|
||
PCM_BATT CmBatt;
|
||
|
||
|
||
#if DIRECT_ACCESS
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
#endif //DIRECT_ACCESS
|
||
|
||
PAGED_CODE();
|
||
|
||
CmBattPrint (CMBATT_TRACE, ("CmBattIoctl\n"));
|
||
|
||
CmBatt = (PCM_BATT) DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Aquire remove lock
|
||
//
|
||
|
||
InterlockedIncrement (&CmBatt->InUseCount);
|
||
if (CmBatt->WantToRemove == TRUE) {
|
||
if (0 == InterlockedDecrement(&CmBatt->InUseCount)) {
|
||
KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE);
|
||
}
|
||
Status = STATUS_DEVICE_REMOVED;
|
||
Irp->IoStatus.Status = Status;
|
||
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
||
return Status;
|
||
}
|
||
|
||
if (CmBatt->Type == CM_BATTERY_TYPE) {
|
||
Status = BatteryClassIoctl (CmBatt->Class, Irp);
|
||
|
||
#if DIRECT_ACCESS
|
||
if (Status == STATUS_NOT_SUPPORTED) {
|
||
|
||
//
|
||
// Is it a Direct Access IOCTL?
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
CmBattPrint((CMBATT_BIOS),
|
||
("CmBattIoctl: Received Direct Access IOCTL %x\n",
|
||
IrpSp->Parameters.DeviceIoControl.IoControlCode));
|
||
|
||
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
|
||
case IOCTL_CMBATT_UID:
|
||
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (ULONG)) {
|
||
Status = CmBattGetUniqueId (CmBatt->Pdo, Irp->AssociatedIrp.SystemBuffer);
|
||
if (NT_SUCCESS(Status)) {
|
||
Irp->IoStatus.Information = sizeof (ULONG);
|
||
} else {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_BUFFER_SIZE;
|
||
};
|
||
break;
|
||
case IOCTL_CMBATT_STA:
|
||
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (ULONG)) {
|
||
Status = CmBattGetStaData (CmBatt->Pdo, Irp->AssociatedIrp.SystemBuffer);
|
||
if (NT_SUCCESS(Status)) {
|
||
Irp->IoStatus.Information = sizeof (ULONG);
|
||
} else {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_BUFFER_SIZE;
|
||
};
|
||
break;
|
||
case IOCTL_CMBATT_PSR:
|
||
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (ULONG)) {
|
||
if (AcAdapterPdo != NULL) {
|
||
Status = CmBattGetPsrData (AcAdapterPdo, Irp->AssociatedIrp.SystemBuffer);
|
||
} else {
|
||
Status = STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
if (NT_SUCCESS(Status)) {
|
||
Irp->IoStatus.Information = sizeof (ULONG);
|
||
} else {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_BUFFER_SIZE;
|
||
};
|
||
break;
|
||
case IOCTL_CMBATT_BTP:
|
||
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof (ULONG)) {
|
||
Status = CmBattSetTripPpoint (CmBatt, *((PULONG) (Irp->AssociatedIrp.SystemBuffer)));
|
||
Irp->IoStatus.Information = 0;
|
||
} else {
|
||
Status = STATUS_INVALID_BUFFER_SIZE;
|
||
};
|
||
break;
|
||
case IOCTL_CMBATT_BIF:
|
||
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (CM_BIF_BAT_INFO)) {
|
||
Status = CmBattGetBifData (CmBatt, Irp->AssociatedIrp.SystemBuffer);
|
||
if (NT_SUCCESS(Status)) {
|
||
Irp->IoStatus.Information = sizeof (CM_BIF_BAT_INFO);
|
||
} else {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_BUFFER_SIZE;
|
||
};
|
||
break;
|
||
case IOCTL_CMBATT_BST:
|
||
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (CM_BST_BAT_INFO)) {
|
||
Status = CmBattGetBstData (CmBatt, Irp->AssociatedIrp.SystemBuffer);
|
||
if (NT_SUCCESS(Status)) {
|
||
Irp->IoStatus.Information = sizeof (CM_BST_BAT_INFO);
|
||
} else {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_BUFFER_SIZE;
|
||
};
|
||
break;
|
||
|
||
default:
|
||
CmBattPrint((CMBATT_ERROR),
|
||
("CmBattIoctl: Unknown IOCTL %x\n",
|
||
IrpSp->Parameters.DeviceIoControl.IoControlCode));
|
||
|
||
}
|
||
|
||
if (Status != STATUS_NOT_SUPPORTED) {
|
||
|
||
//
|
||
// We just handled this IOCTL. Complete it.
|
||
//
|
||
|
||
Irp->IoStatus.Status = Status;
|
||
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
||
}
|
||
}
|
||
#endif //DIRECT_ACCESS
|
||
}
|
||
|
||
if (Status == STATUS_NOT_SUPPORTED) {
|
||
|
||
//
|
||
// Not for the battery. Pass it down the stack.
|
||
//
|
||
|
||
IoSkipCurrentIrpStackLocation (Irp);
|
||
Status = IoCallDriver (CmBatt->LowerDeviceObject, Irp);
|
||
|
||
}
|
||
|
||
//
|
||
// Release Removal Lock
|
||
//
|
||
if (0 == InterlockedDecrement(&CmBatt->InUseCount)) {
|
||
KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CmBattQueryTag (
|
||
IN PVOID Context,
|
||
OUT PULONG TagPtr
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called by the class driver to retrieve the batteries current tag value
|
||
|
||
The battery class driver will serialize all requests it issues to
|
||
the miniport for a given battery.
|
||
|
||
Arguments:
|
||
|
||
Context - Miniport context value for battery
|
||
TagPtr - Pointer to return current tag
|
||
|
||
Return Value:
|
||
|
||
Success if there is a battery currently installed, else no such device.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PCM_BATT CmBatt = (PCM_BATT) Context;
|
||
ULONG BatteryStatus;
|
||
|
||
|
||
PAGED_CODE();
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_MINI),
|
||
("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
|
||
*TagPtr, CmBatt, CmBatt->DeviceNumber));
|
||
|
||
//
|
||
// Check if battery is still there
|
||
//
|
||
CmBatt->ReCheckSta = FALSE;
|
||
Status = CmBattGetStaData (CmBatt->Pdo, &BatteryStatus);
|
||
|
||
if (NT_SUCCESS (Status)) {
|
||
if (BatteryStatus & STA_DEVICE_PRESENT) {
|
||
|
||
//
|
||
// If the tag isn't assigned, assign a new one
|
||
//
|
||
|
||
if (CmBatt->Info.Tag == BATTERY_TAG_INVALID) {
|
||
|
||
//
|
||
// See if there is a battery out there.
|
||
//
|
||
|
||
CmBatt->TagCount += 1;
|
||
if (CmBatt->TagCount == BATTERY_TAG_INVALID) {
|
||
CmBatt->TagCount += 1;
|
||
}
|
||
|
||
CmBatt->Info.Tag = CmBatt->TagCount;
|
||
|
||
RtlZeroMemory (&CmBatt->Alarm, sizeof(BAT_ALARM_INFO));
|
||
CmBatt->Alarm.Setting = CM_ALARM_INVALID;
|
||
CmBattPrint (CMBATT_TRACE, ("CmBattQueryTag - New Tag: (%d)\n", CmBatt->Info.Tag));
|
||
InterlockedExchange (&CmBatt->CacheState, 0);
|
||
CmBatt->DischargeTime = KeQueryInterruptTime();
|
||
}
|
||
|
||
} else {
|
||
|
||
CmBatt->Info.Tag = BATTERY_TAG_INVALID;
|
||
Status = STATUS_NO_SUCH_DEVICE;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
|
||
CmBattPrint ((CMBATT_MINI),
|
||
("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n",
|
||
CmBatt->Info.Tag, Status));
|
||
|
||
*TagPtr = CmBatt->Info.Tag;
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CmBattQueryInformation (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
IN BATTERY_QUERY_INFORMATION_LEVEL Level,
|
||
IN LONG AtRate OPTIONAL,
|
||
OUT PVOID Buffer,
|
||
IN ULONG BufferLength,
|
||
OUT PULONG ReturnedLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called by the class driver to retrieve battery information
|
||
|
||
The battery class driver will serialize all requests it issues to
|
||
the miniport for a given battery.
|
||
|
||
We return invalid parameter when we can't handle a request for a
|
||
specific level of information. This is defined in the battery class spec.
|
||
|
||
Arguments:
|
||
|
||
Context - Miniport context value for battery
|
||
BatteryTag - Tag of current battery
|
||
Level - type of information required
|
||
AtRate - Used only when Level==BatteryEstimatedTime
|
||
Buffer - Location for the information
|
||
BufferLength - Length in bytes of the buffer
|
||
ReturnedLength - Length in bytes of the returned data
|
||
|
||
Return Value:
|
||
|
||
Success if there is a battery currently installed, else no such device.
|
||
|
||
--*/
|
||
{
|
||
PCM_BATT CmBatt = (PCM_BATT) Context;
|
||
ULONG ResultData;
|
||
NTSTATUS Status;
|
||
PVOID ReturnBuffer;
|
||
ULONG ReturnBufferLength;
|
||
WCHAR scratchBuffer[CM_MAX_STRING_LENGTH];
|
||
WCHAR buffer2[CM_MAX_STRING_LENGTH];
|
||
UNICODE_STRING tmpUnicodeString;
|
||
UNICODE_STRING unicodeString;
|
||
ANSI_STRING ansiString;
|
||
|
||
BATTERY_REMAINING_SCALE ScalePtr[2];
|
||
|
||
|
||
PAGED_CODE();
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_MINI),
|
||
("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
|
||
BatteryTag, CmBatt->DeviceNumber, Level));
|
||
|
||
//
|
||
// Be sure there's a battery out there
|
||
// This also checks BatteryTag
|
||
//
|
||
|
||
Status = CmBattVerifyStaticInfo (CmBatt, BatteryTag);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
ResultData = 0;
|
||
ReturnBuffer = NULL;
|
||
ReturnBufferLength = 0;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Get the info requested
|
||
//
|
||
|
||
switch (Level) {
|
||
case BatteryInformation:
|
||
//
|
||
// This data structure is populated by CmBattVerifyStaticInfo
|
||
//
|
||
ReturnBuffer = (PVOID) &CmBatt->Info.ApiInfo;
|
||
ReturnBufferLength = sizeof (CmBatt->Info.ApiInfo);
|
||
break;
|
||
|
||
case BatteryGranularityInformation:
|
||
//
|
||
// Get the granularity from the static info structure
|
||
// This data structure is populated by CmBattVerifyStaticInfo
|
||
//
|
||
{
|
||
ScalePtr[0].Granularity = CmBatt->Info.ApiGranularity_1;
|
||
ScalePtr[0].Capacity = CmBatt->Info.ApiInfo.DefaultAlert1;
|
||
ScalePtr[1].Granularity = CmBatt->Info.ApiGranularity_2;
|
||
ScalePtr[1].Capacity = CmBatt->Info.ApiInfo.DesignedCapacity;
|
||
|
||
ReturnBuffer = ScalePtr;
|
||
ReturnBufferLength = 2 * sizeof (BATTERY_REMAINING_SCALE);
|
||
}
|
||
break;
|
||
|
||
case BatteryTemperature:
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
|
||
case BatteryEstimatedTime:
|
||
|
||
//
|
||
// Return unknown time if battery has been discharging less than 15 seconds
|
||
//
|
||
if (KeQueryInterruptTime() > (CmBatt->DischargeTime + CM_ESTIMATED_TIME_DELAY)) {
|
||
|
||
//
|
||
// The BatteryEstimatedTime for the control method batteries is defined
|
||
// by the following formula:
|
||
//
|
||
// EstimatedTime [min] = RemainingCapacity [mAh|mWh] * 60 [min/hr] * 60 [sec/min]
|
||
// ----------------------------------
|
||
// PresentRate [mA|mW]
|
||
//
|
||
|
||
//
|
||
// Rerun _BST since we don't have a timeout on this data.
|
||
// Also Calculate API status values from CM values
|
||
//
|
||
|
||
CmBattGetBatteryStatus (CmBatt, CmBatt->Info.Tag);
|
||
|
||
//
|
||
// If AtRate is zero, we need to use the present rate
|
||
//
|
||
|
||
if (AtRate == 0) {
|
||
AtRate = CmBatt->Info.ApiStatus.Rate;
|
||
}
|
||
|
||
if (AtRate >= 0) {
|
||
AtRate = BATTERY_UNKNOWN_RATE;
|
||
}
|
||
if ((AtRate != BATTERY_UNKNOWN_RATE) &&
|
||
(CmBatt->Info.ApiStatus.Capacity != BATTERY_UNKNOWN_CAPACITY)) {
|
||
|
||
// Calculate estimated time.
|
||
#if DEBUG
|
||
// Make sure we don't overflow...
|
||
if (CmBatt->Info.ApiStatus.Capacity > (0xffffffff/3600)) {
|
||
CmBattPrint (CMBATT_ERROR_ONLY, ("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n"));
|
||
}
|
||
#endif //DEBUG
|
||
ResultData = (ULONG) (CmBatt->Info.ApiStatus.Capacity * 3600) / (-AtRate);
|
||
|
||
} else {
|
||
//
|
||
// We don't know have enough information to calculate the value.
|
||
// Return BATTERY_UNKNONW_TIME.
|
||
//
|
||
// If this battery is incapable of returning estimated time, return with
|
||
// STATUS_INVALID_DEVICE_REQUEST
|
||
//
|
||
|
||
#if DEBUG
|
||
if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_DISCHARGING) {
|
||
CmBattPrint (CMBATT_WARN,
|
||
("CmBattQueryInformation: Can't calculate EstimatedTime.\n"));
|
||
}
|
||
#endif //DEBUG
|
||
|
||
if (CmBatt->Info.ApiStatus.Rate == BATTERY_UNKNOWN_RATE &&
|
||
(CmBatt->Info.Status.BatteryState & CM_BST_STATE_DISCHARGING)) {
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
CmBattPrint (CMBATT_WARN,
|
||
("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n"));
|
||
}
|
||
if (CmBatt->Info.ApiStatus.Capacity == BATTERY_UNKNOWN_CAPACITY) {
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
CmBattPrint (CMBATT_WARN,
|
||
("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n"));
|
||
}
|
||
|
||
ResultData = BATTERY_UNKNOWN_TIME;
|
||
}
|
||
} else { // if (KeQueryInterruptTime() > CmBatt->DischargeTime + CM_ESTIMATED_TIME_DELAY)
|
||
|
||
//
|
||
// Return unknown time if battery has been discharging less than 15 seconds
|
||
//
|
||
ResultData = BATTERY_UNKNOWN_TIME;
|
||
}
|
||
|
||
ReturnBuffer = &ResultData;
|
||
ReturnBufferLength = sizeof(ResultData);
|
||
break;
|
||
|
||
case BatteryDeviceName:
|
||
//
|
||
// Model Number must be returned as a wide string
|
||
//
|
||
unicodeString.Buffer = scratchBuffer;
|
||
unicodeString.MaximumLength = CM_MAX_STRING_LENGTH;
|
||
|
||
RtlInitAnsiString (&ansiString, CmBatt->Info.ModelNum);
|
||
Status = RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
|
||
|
||
ReturnBuffer = unicodeString.Buffer;
|
||
ReturnBufferLength = unicodeString.Length;
|
||
break;
|
||
|
||
case BatteryManufactureDate:
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
|
||
case BatteryManufactureName:
|
||
//
|
||
// Oem Info must be returned as wide string
|
||
//
|
||
unicodeString.Buffer = scratchBuffer;
|
||
unicodeString.MaximumLength = CM_MAX_STRING_LENGTH;
|
||
|
||
RtlInitAnsiString (&ansiString, CmBatt->Info.OEMInfo);
|
||
Status = RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
|
||
|
||
ReturnBuffer = unicodeString.Buffer;
|
||
ReturnBufferLength = unicodeString.Length;
|
||
break;
|
||
|
||
case BatteryUniqueID:
|
||
//
|
||
// Concatenate the serial #, OEM info, and Model #
|
||
//
|
||
|
||
unicodeString.Buffer = scratchBuffer;
|
||
unicodeString.MaximumLength = CM_MAX_STRING_LENGTH;
|
||
|
||
tmpUnicodeString.Buffer = buffer2;
|
||
tmpUnicodeString.MaximumLength = CM_MAX_STRING_LENGTH;
|
||
|
||
RtlInitAnsiString (&ansiString, CmBatt->Info.SerialNum);
|
||
RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
|
||
|
||
if (CmBatt->Info.OEMInfo[0]) {
|
||
RtlInitAnsiString (&ansiString, CmBatt->Info.OEMInfo);
|
||
RtlAnsiStringToUnicodeString (&tmpUnicodeString, &ansiString, FALSE);
|
||
RtlAppendUnicodeStringToString (&unicodeString, &tmpUnicodeString);
|
||
}
|
||
|
||
RtlInitAnsiString (&ansiString, CmBatt->Info.ModelNum);
|
||
RtlAnsiStringToUnicodeString (&tmpUnicodeString, &ansiString, FALSE);
|
||
RtlAppendUnicodeStringToString (&unicodeString, &tmpUnicodeString);
|
||
|
||
ReturnBuffer = unicodeString.Buffer;
|
||
ReturnBufferLength = unicodeString.Length;
|
||
break;
|
||
|
||
default:
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Done, return buffer if needed
|
||
//
|
||
|
||
*ReturnedLength = ReturnBufferLength;
|
||
if (BufferLength < ReturnBufferLength) {
|
||
Status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
if (NT_SUCCESS(Status) && ReturnBuffer) {
|
||
RtlCopyMemory (Buffer, ReturnBuffer, ReturnBufferLength); // Copy what's needed
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CmBattQueryStatus (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
OUT PBATTERY_STATUS BatteryStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called by the class driver to retrieve the batteries current status
|
||
|
||
The battery class driver will serialize all requests it issues to
|
||
the miniport for a given 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.
|
||
|
||
--*/
|
||
{
|
||
PCM_BATT CmBatt = (PCM_BATT) Context;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_MINI), ("CmBattQueryStatus - Tag (%d) Device %x\n",
|
||
BatteryTag, CmBatt->DeviceNumber));
|
||
|
||
|
||
Status = CmBattGetBatteryStatus (CmBatt, BatteryTag);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
RtlCopyMemory (BatteryStatus, &CmBatt->Info.ApiStatus, sizeof(BATTERY_STATUS));
|
||
}
|
||
CmBattPrint ((CMBATT_MINI), ("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
|
||
BatteryStatus->PowerState, BatteryStatus->Capacity, BatteryStatus->Voltage, BatteryStatus->Rate));
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CmBattSetStatusNotify (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
IN PBATTERY_NOTIFY Notify
|
||
)
|
||
/*++
|
||
|
||
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.
|
||
|
||
The class driver will always set the notification level it needs
|
||
after each call to BatteryClassStatusNotify.
|
||
|
||
The battery class driver will serialize all requests it issues to
|
||
the miniport for a given battery.
|
||
|
||
Arguments:
|
||
|
||
Context - Miniport context value for battery
|
||
BatteryTag - Tag of current battery
|
||
BatteryNotify - The notification setting
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
PCM_BATT CmBatt;
|
||
NTSTATUS Status;
|
||
ULONG Target;
|
||
LONG ActualAlarm; // Value after adjusting for limit conditions.
|
||
CM_BST_BAT_INFO bstData;
|
||
|
||
PAGED_CODE();
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_MINI), ("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
|
||
BatteryTag, Notify->LowCapacity));
|
||
|
||
Status = STATUS_SUCCESS;
|
||
CmBatt = (PCM_BATT) Context;
|
||
|
||
Status = CmBattVerifyStaticInfo (CmBatt, BatteryTag);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// If _BTP doesn't exist, don't call it again.
|
||
//
|
||
|
||
if (!CmBatt->Info.BtpExists) {
|
||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
|
||
if ((Notify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
|
||
(Notify->LowCapacity == BATTERY_UNKNOWN_CAPACITY)) {
|
||
CmBattPrint (CMBATT_WARN, ("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n"));
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_CHARGING) {
|
||
Target = Notify->HighCapacity;
|
||
} else if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_DISCHARGING) {
|
||
Target = Notify->LowCapacity;
|
||
} else {
|
||
// No trip point needs to be set, the battery will trip as soon as it starts
|
||
// charging or discharging.
|
||
//return STATUS_SUCCESS;
|
||
// but it doesn't hurt to set the trip point just in case the battery
|
||
// system screws up and doesn't send the notification when the status changed.
|
||
Target = Notify->LowCapacity;
|
||
}
|
||
|
||
ActualAlarm = Target;
|
||
|
||
//
|
||
// If the battery operates on mA we need to convert the trip point from mW
|
||
// to mA. The formula for doing this is:
|
||
//
|
||
// mA = mW / V or mA = (mW / mV) * 1000
|
||
//
|
||
|
||
if (CmBatt->Info.StaticData.PowerUnit & CM_BIF_UNITS_AMPS) {
|
||
if ((CmBatt->Info.StaticData.DesignVoltage == CM_UNKNOWN_VALUE) ||
|
||
(CmBatt->Info.StaticData.DesignVoltage == 0)) {
|
||
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
|
||
CmBatt->Info.StaticData.DesignVoltage));
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
//
|
||
// Calculate optimized Ah target
|
||
//
|
||
if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_CHARGING) {
|
||
|
||
//
|
||
// (ActualAlarm * 1000 + 500) / DesignVoltage + 1 will generate
|
||
// the correct battery trip point, except in cases when
|
||
// (ActualAlarm * 1000)+ 500) is evenly divisible by the
|
||
// DesignVoltage. In that case, it will be 1 mAh higher than
|
||
// it should be.
|
||
//
|
||
// This is in the form of a single expression rather than an
|
||
// "if" statement to encourage the compiler to use the remainder
|
||
// from the original div operation rather than performing div
|
||
// twice
|
||
//
|
||
|
||
ActualAlarm = (ActualAlarm * 1000 + 500) / CmBatt->Info.StaticData.DesignVoltage +
|
||
( ((ActualAlarm * 1000 + 500) % CmBatt->Info.StaticData.DesignVoltage == 0)? 0 : 1 );
|
||
|
||
} else {
|
||
|
||
//
|
||
// (ActualAlarm * 1000 - 500) / DesignVoltage will generate
|
||
// the correct battery trip point, except in cases when
|
||
// (ActualAlarm * 1000)+ 500) is evenly divisible by the
|
||
// DesignVoltage. In that case, it will be 1 mAh higher than
|
||
// it should be
|
||
//
|
||
|
||
ActualAlarm = (ActualAlarm * 1000 - 500) / CmBatt->Info.StaticData.DesignVoltage -
|
||
( ((ActualAlarm * 1000 + 500) % CmBatt->Info.StaticData.DesignVoltage == 0)? 1 : 0);
|
||
|
||
}
|
||
|
||
} else {
|
||
// Increment or decrement the alarm value by 1 since the input to this
|
||
// function is < or >, but _BTP is <= or >=
|
||
if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_CHARGING) {
|
||
ActualAlarm++;
|
||
} else {
|
||
if (ActualAlarm > 0) {
|
||
ActualAlarm--;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ActualAlarm == CmBatt->Alarm.Setting) {
|
||
//
|
||
// Don't need to reset the alarm to the same value.
|
||
//
|
||
|
||
CmBattPrint(CMBATT_LOW,
|
||
("CmBattSetStatusNotify: Keeping original setting: %X\n",
|
||
CmBatt->Alarm.Setting
|
||
));
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Save current setting, so we won't waste time setting it twice.
|
||
//
|
||
CmBatt->Alarm.Setting = ActualAlarm;
|
||
|
||
//
|
||
// Set the alarm
|
||
//
|
||
Status = CmBattSetTripPpoint (CmBatt, ActualAlarm);
|
||
|
||
if ((ActualAlarm == 0) && (Target != 0)) {
|
||
// If the driver really wanted to be notified when the capacity
|
||
// reached 0, return STATUS_NOT_SUPPORTED because seting _BTP to zero
|
||
// disables notification. The battery class will perform polling since
|
||
// STATUS_NOT_SUPPORTED was returned.
|
||
|
||
Status = STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
//
|
||
// Something failed in the Trip point call, get out
|
||
//
|
||
CmBattPrint (CMBATT_ERROR, ("CmBattSetStatusNotify: SetTripPoint failed - %x\n",
|
||
Status));
|
||
CmBatt->Alarm.Setting = CM_ALARM_INVALID;
|
||
return Status;
|
||
}
|
||
|
||
// Make sure that the trip point hasn't been passed already.
|
||
Status = CmBattGetBstData (CmBatt, &bstData);
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
//
|
||
// Something failed in the Trip point call, get out
|
||
//
|
||
CmBattPrint (CMBATT_ERROR, ("CmBattSetStatusNotify: GetBstData - %x\n",
|
||
Status));
|
||
} else {
|
||
if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_CHARGING) {
|
||
if (bstData.RemainingCapacity >= (ULONG)ActualAlarm) {
|
||
CmBattPrint (CMBATT_WARN, ("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
|
||
ActualAlarm, bstData.RemainingCapacity));
|
||
CmBattNotifyHandler (CmBatt, BATTERY_STATUS_CHANGE);
|
||
}
|
||
} else {
|
||
if ((bstData.RemainingCapacity <= (ULONG)ActualAlarm) && (Target != 0)) {
|
||
CmBattPrint (CMBATT_WARN, ("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
|
||
ActualAlarm, bstData.RemainingCapacity));
|
||
CmBattNotifyHandler (CmBatt, BATTERY_STATUS_CHANGE);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
CmBattPrint(CMBATT_LOW,
|
||
("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
|
||
Target,
|
||
CmBatt->Info.ApiStatus.Capacity
|
||
));
|
||
|
||
CmBattPrint ((CMBATT_MINI),
|
||
("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %x\n",
|
||
Notify->PowerState, Notify->LowCapacity, Notify->HighCapacity));
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CmBattDisableStatusNotify (
|
||
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 CmBattSetStatusNotify.
|
||
|
||
The battery class driver will serialize all requests it issues to
|
||
the miniport for a given battery.
|
||
|
||
Arguments:
|
||
|
||
Context - Miniport context value for battery
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
PCM_BATT CmBatt;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_MINI), ("CmBattDisableStatusNotify\n"));
|
||
|
||
CmBatt = (PCM_BATT) Context;
|
||
|
||
//
|
||
// If _BTP doesn't exist, don't call it again.
|
||
//
|
||
|
||
if (!CmBatt->Info.BtpExists) {
|
||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
|
||
if (CmBatt->Alarm.Setting != CM_BATT_CLEAR_TRIP_POINT) {
|
||
|
||
CmBatt->Alarm.Setting = CM_BATT_CLEAR_TRIP_POINT;
|
||
|
||
//
|
||
// Clear the trip point.
|
||
//
|
||
|
||
Status = CmBattSetTripPpoint (CmBatt, CM_BATT_CLEAR_TRIP_POINT);
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
CmBattPrint ((CMBATT_MINI),
|
||
("CmBattDisableStatusNotify: SetTripPoint failed - %x\n",
|
||
Status));
|
||
CmBatt->Alarm.Setting = CM_ALARM_INVALID;
|
||
}
|
||
} else {
|
||
//
|
||
// Don't need to disable alarm is it's already been disabled.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CmBattGetBatteryStatus (
|
||
PCM_BATT CmBatt,
|
||
IN ULONG BatteryTag
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called to setup the status data required by the IOCTL API defined for
|
||
the battery class. This is the data defined in the BATTERY_STATUS
|
||
structure.
|
||
|
||
Arguments:
|
||
|
||
CmBatt - The extension for this device.
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PBATTERY_STATUS ApiStatus;
|
||
PCM_BST_BAT_INFO CmBattStatus;
|
||
ULONG AcStatus = 0;
|
||
ULONG LastPowerState;
|
||
|
||
|
||
PAGED_CODE();
|
||
|
||
CmBattPrint (CMBATT_TRACE, ("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n",
|
||
CmBatt, BatteryTag));
|
||
|
||
|
||
Status = CmBattVerifyStaticInfo (CmBatt, BatteryTag);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
if (CmBatt->Sleeping) {
|
||
//
|
||
// Return cached data, and ensure that this gets requeried when we are fully awake.
|
||
//
|
||
CmBattNotifyHandler (CmBatt, BATTERY_STATUS_CHANGE);
|
||
return Status;
|
||
}
|
||
|
||
CmBattStatus = &CmBatt->Info.Status;
|
||
Status = CmBattGetBstData(CmBatt, CmBattStatus);
|
||
if (!NT_SUCCESS(Status)) {
|
||
InterlockedExchange (&CmBatt->CacheState, 0);
|
||
return Status;
|
||
}
|
||
|
||
ApiStatus = &CmBatt->Info.ApiStatus;
|
||
LastPowerState = ApiStatus->PowerState;
|
||
RtlZeroMemory (ApiStatus, sizeof(BATTERY_STATUS));
|
||
|
||
//
|
||
// Decode the state bits
|
||
//
|
||
#if DEBUG
|
||
if (((CmBattStatus->BatteryState & CM_BST_STATE_DISCHARGING) &&
|
||
(CmBattStatus->BatteryState & CM_BST_STATE_CHARGING) )) {
|
||
CmBattPrint ((CMBATT_ERROR),
|
||
("************************ ACPI BIOS BUG ********************\n"
|
||
"* CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
|
||
"* One battery cannot be charging and discharging at the same time.\n",
|
||
CmBattStatus->BatteryState));
|
||
}
|
||
// ASSERT(!((CmBattStatus->BatteryState & CM_BST_STATE_DISCHARGING) &&
|
||
// (CmBattStatus->BatteryState & CM_BST_STATE_CHARGING) ));
|
||
|
||
#endif
|
||
|
||
if (CmBattStatus->BatteryState & CM_BST_STATE_DISCHARGING) {
|
||
ApiStatus->PowerState |= BATTERY_DISCHARGING;
|
||
if (!(LastPowerState & BATTERY_DISCHARGING)) {
|
||
//
|
||
// Keep track of when battery started discharging.
|
||
//
|
||
CmBatt->DischargeTime = KeQueryInterruptTime();
|
||
}
|
||
} else if (CmBattStatus->BatteryState & CM_BST_STATE_CHARGING) {
|
||
ApiStatus->PowerState |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
|
||
}
|
||
|
||
if (CmBattStatus->BatteryState & CM_BST_STATE_CRITICAL)
|
||
ApiStatus->PowerState |= BATTERY_CRITICAL;
|
||
|
||
ApiStatus->Voltage = CmBattStatus->PresentVoltage;
|
||
|
||
//
|
||
// Run the _PSR method on the AC adapter to get the current power status.
|
||
// Otherwise, we don't know if it is connected, unless the battery reports charging.
|
||
// This isn't enough information for the upper software to work properly, so
|
||
// just find out for sure.
|
||
//
|
||
if (AcAdapterPdo != NULL) {
|
||
|
||
CmBattGetPsrData (AcAdapterPdo, &AcStatus);
|
||
|
||
} else {
|
||
// If the AcAdapterPdo is NULL, then we need to assume the AC status from
|
||
// the battery charging status.
|
||
if (CmBattStatus->BatteryState & CM_BST_STATE_CHARGING) {
|
||
AcStatus = 1;
|
||
} else {
|
||
AcStatus = 0;
|
||
}
|
||
}
|
||
|
||
if (AcStatus == 0x01) {
|
||
ApiStatus->PowerState |= BATTERY_POWER_ON_LINE;
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA),
|
||
("CmBattGetBatteryStatus: AC adapter is connected\n"));
|
||
} else {
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA),
|
||
("CmBattGetBatteryStatus: AC adapter is NOT connected\n"));
|
||
}
|
||
|
||
// The following is an awful hack put into the win98 version that really
|
||
// shouldn't be there. The purpose of this is reduce the delay in notification
|
||
// when AC status changes, but this doesn't help the problem of delays when
|
||
// other events such as battery insertion or removal happen. In addition it
|
||
// violates the priciple of WDM drivers being binary compatible, and this fix
|
||
// does nothing for any other battery driver that may later be added by a third
|
||
// party. This should be handled by the OS maintianing an outstanding long term
|
||
// status or tag request to the composite battery at all times. That would
|
||
// involve starting the Irps then recycleing it in the completion routine doing
|
||
// what this hack does if there was a change to report.
|
||
|
||
#ifndef _WIN32_WINNT
|
||
|
||
// JASONCL: check for a power source change and notify vpowerd if there has been one.
|
||
|
||
if ( ((AcStatus & 0x01) && (CmBattPrevPowerSource == 0)) ||
|
||
(!(AcStatus & 0x01) && (CmBattPrevPowerSource == 1)) ) {
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA),
|
||
("CmBattGetBatteryStatus: Detected Power Source Change\n"));
|
||
|
||
CmBattPrevPowerSource = AcStatus & 0x01;
|
||
|
||
CmBattNotifyVPOWERDOfPowerChange (1);
|
||
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Decode the power/current
|
||
//
|
||
if (CmBatt->Info.StaticData.PowerUnit == CM_BIF_UNITS_AMPS) {
|
||
//
|
||
// This battery expresses power in terms of amps. The system expects
|
||
// it to be Watts, so we have to do a conversion. The Conversion is:
|
||
//
|
||
// mW = mA * Volts or mW = mA * mV / 1000
|
||
//
|
||
|
||
// Using DesignVoltage for conversions since presentvoltage
|
||
// may vary over time, giving inconsistent results.
|
||
|
||
if ((CmBatt->Info.StaticData.DesignVoltage != CM_UNKNOWN_VALUE) &&
|
||
(CmBatt->Info.StaticData.DesignVoltage != 0)) {
|
||
if (CmBattStatus->RemainingCapacity != CM_UNKNOWN_VALUE) {
|
||
|
||
ApiStatus->Capacity = (CmBattStatus->RemainingCapacity *
|
||
CmBatt->Info.StaticData.DesignVoltage +
|
||
500) / 1000;
|
||
} else {
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("---------------------- RemainingCapacity = CM_UNKNOWN_VALUE\n"));
|
||
|
||
ApiStatus->Capacity = BATTERY_UNKNOWN_CAPACITY;
|
||
}
|
||
|
||
if (CmBattStatus->PresentRate != CM_UNKNOWN_VALUE) {
|
||
|
||
if (CmBattStatus->PresentRate > ((MAXULONG - 500)/ CmBatt->Info.StaticData.DesignVoltage)) { CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetBatteryStatus - Can't calculate Rate \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("---------------------- Overflow: PresentRate = 0x%08x\n", CmBattStatus->PresentRate));
|
||
|
||
ApiStatus->Rate = BATTERY_UNKNOWN_RATE;
|
||
}
|
||
|
||
ApiStatus->Rate = (CmBattStatus->PresentRate *
|
||
CmBatt->Info.StaticData.DesignVoltage +
|
||
500) / 1000;
|
||
} else {
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetBatteryStatus - Can't calculate Rate \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("---------------------- Present Rate = CM_UNKNOWN_VALUE\n"));
|
||
|
||
ApiStatus->Rate = BATTERY_UNKNOWN_RATE;
|
||
}
|
||
|
||
} else {
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("---------------------- DesignVoltage = 0x%08x\n",
|
||
CmBatt->Info.StaticData.DesignVoltage));
|
||
ApiStatus->Capacity = BATTERY_UNKNOWN_CAPACITY;
|
||
|
||
ApiStatus->Rate = BATTERY_UNKNOWN_RATE;
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// This battery expresses power in terms of Watts
|
||
//
|
||
|
||
ApiStatus->Capacity = CmBattStatus->RemainingCapacity;
|
||
ApiStatus->Rate = CmBattStatus->PresentRate;
|
||
if (CmBattStatus->PresentRate > CM_MAX_VALUE) {
|
||
ApiStatus->Rate = BATTERY_UNKNOWN_RATE;
|
||
if (CmBattStatus->PresentRate != CM_UNKNOWN_VALUE) {
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("---------------------- PresentRate = 0x%08x\n", CmBattStatus->PresentRate));
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the rate is "unkown" set it to zero
|
||
//
|
||
if (ApiStatus->Rate == BATTERY_UNKNOWN_RATE) {
|
||
|
||
//
|
||
// This is only allowed when -c-h-a-r-g-i-n-g- not discharging.
|
||
// Batteries are allowed to return UNKNOWN_RATE when AC is online
|
||
// but they aren't being charged.
|
||
//
|
||
if (CmBattStatus->BatteryState & CM_BST_STATE_DISCHARGING) {
|
||
|
||
CmBattPrint(
|
||
CMBATT_ERROR,
|
||
("CmBattGetBatteryStatus: battery rate is unkown when battery "
|
||
"is not charging!\n")
|
||
);
|
||
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// The OS expects the PresentRate to be a signed value, with positive values
|
||
// indicating a charge and negative values indicating a discharge. Since the
|
||
// control methods only return unsigned values we need to do the conversion here.
|
||
//
|
||
|
||
if (ApiStatus->PowerState & BATTERY_DISCHARGING) {
|
||
ApiStatus->Rate = 0 - ApiStatus->Rate;
|
||
|
||
} else if (!(ApiStatus->PowerState & BATTERY_CHARGING) && (ApiStatus->Rate != 0)) {
|
||
CmBattPrint ((CMBATT_BIOS), ("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n", ApiStatus->Rate));
|
||
ApiStatus->Rate = 0;
|
||
} else {
|
||
// Rate already equals 0. Battery is not Charging or discharging.
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
CmBattVerifyStaticInfo (
|
||
IN PCM_BATT CmBatt,
|
||
IN ULONG BatteryTag
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
In order to detect battery changes, we'll check to see if any part of the data
|
||
returned by the cm is different from what we had read in the past.
|
||
|
||
Arguments:
|
||
|
||
CmBatt - Battery to read
|
||
BatteryTag - Tag of battery as expected by the caller
|
||
|
||
Return Value:
|
||
|
||
Returns a boolean to indicate to the caller that IO was performed.
|
||
This allows the caller to iterate on changes it may be making until
|
||
the battery state is correct.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
CM_BIF_BAT_INFO NewInfo;
|
||
ULONG StaResult;
|
||
PBATTERY_INFORMATION ApiData = &CmBatt->Info.ApiInfo;
|
||
PCM_BIF_BAT_INFO BIFData = &CmBatt->Info.StaticData;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
CmBattPrint (CMBATT_TRACE, ("CmBattVerifyStaticInfo - CmBatt (%08x) Tag (%d) Device %d\n",
|
||
CmBatt, BatteryTag, CmBatt->DeviceNumber));
|
||
|
||
Status = STATUS_SUCCESS;
|
||
if ((CmBatt->Info.Tag == BATTERY_TAG_INVALID) || (BatteryTag != CmBatt->Info.Tag)) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
if ((CmBatt->CacheState == 2) && (!CmBatt->ReCheckSta)) {
|
||
return Status;
|
||
}
|
||
|
||
if (CmBatt->Sleeping) {
|
||
//
|
||
// Return cached data, and ensure that this gets requeried when we are fully awake.
|
||
//
|
||
CmBattNotifyHandler (CmBatt, BATTERY_STATUS_CHANGE);
|
||
return Status;
|
||
}
|
||
|
||
// Check to make sure that the battery does exist
|
||
// before continuing
|
||
if (CmBatt->ReCheckSta) {
|
||
CmBatt->ReCheckSta = FALSE;
|
||
Status = CmBattGetStaData (CmBatt->Pdo, &StaResult);
|
||
if (NT_SUCCESS (Status)) {
|
||
if (!(StaResult & STA_DEVICE_PRESENT)) {
|
||
CmBatt->Info.Tag = BATTERY_TAG_INVALID;
|
||
Status = STATUS_NO_SUCH_DEVICE;
|
||
return Status;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// The first time through the loop, CacheState will be 1
|
||
// If a notification occurs, this will be reset to 0, and the loop will run again.
|
||
// If no notification occurs, it will increment to 2, the "Valid" value.
|
||
//
|
||
|
||
while (NT_SUCCESS(Status) && (InterlockedIncrement (&CmBatt->CacheState) == 1)) {
|
||
|
||
//
|
||
// Go get fresh data
|
||
// Issue the Control method
|
||
//
|
||
|
||
if (CmBatt->ReCheckSta) {
|
||
CmBatt->ReCheckSta = FALSE;
|
||
Status = CmBattGetStaData (CmBatt->Pdo, &StaResult);
|
||
|
||
if (NT_SUCCESS (Status)) {
|
||
if (!(StaResult & STA_DEVICE_PRESENT)) {
|
||
CmBatt->Info.Tag = BATTERY_TAG_INVALID;
|
||
Status = STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS (Status)) {
|
||
Status = CmBattGetBifData(CmBatt, &NewInfo);
|
||
}
|
||
|
||
if (NT_SUCCESS (Status)) {
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
|
||
("CmBattGetStaticInfo: _BIF Returned: PowerUnit=%x DesignCapacity=%x LastFull=%x\n",
|
||
NewInfo.PowerUnit, NewInfo.DesignCapacity, NewInfo.LastFullChargeCapacity ));
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
|
||
(" ---------------- Technology=%x Voltage=%x DesignWarning=%x\n",
|
||
NewInfo.BatteryTechnology, NewInfo.DesignVoltage,
|
||
NewInfo.DesignCapacityOfWarning ));
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
|
||
(" ---------------- DesignLow=%x Gran1=%x Gran2=%x\n",
|
||
NewInfo.DesignCapacityOfLow, NewInfo.BatteryCapacityGran_1,
|
||
NewInfo.BatteryCapacityGran_2 ));
|
||
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
|
||
(" ---------------- ModelNumber=%s \n",
|
||
NewInfo.ModelNumber));
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
|
||
(" ---------------- SerialNumber=%s \n",
|
||
NewInfo.SerialNumber));
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
|
||
(" ---------------- BatteryType=%s \n",
|
||
NewInfo.BatteryType));
|
||
CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
|
||
(" ---------------- OEMInformation=%s \n",
|
||
NewInfo.OEMInformation));
|
||
|
||
//
|
||
// Update static area with the new data
|
||
//
|
||
|
||
if ((CmBatt->Info.Tag == CmBatt->Info.StaticDataTag) &&
|
||
(CmBatt->Info.StaticDataTag != BATTERY_TAG_INVALID)) {
|
||
if (RtlCompareMemory (&NewInfo, BIFData, sizeof(NewInfo)) == sizeof(NewInfo)) {
|
||
//
|
||
// Nothing has changed. Don't need to update anything.
|
||
//
|
||
continue;
|
||
} else {
|
||
//
|
||
// Something has changed. The tag should have been invalidated.
|
||
//
|
||
CmBattPrint ((CMBATT_BIOS | CMBATT_ERROR),
|
||
("CmBattVerifyStaticInfo: Static data changed without recieving notify 0x81.\n"));
|
||
|
||
CmBatt->Info.Tag = BATTERY_TAG_INVALID;
|
||
Status = STATUS_NO_SUCH_DEVICE;
|
||
CmBatt->Info.StaticDataTag = BATTERY_TAG_INVALID;
|
||
|
||
}
|
||
|
||
}
|
||
CmBatt->Info.StaticDataTag = CmBatt->Info.Tag;
|
||
|
||
RtlCopyMemory (BIFData, &NewInfo, sizeof(CM_BIF_BAT_INFO));
|
||
|
||
RtlZeroMemory (ApiData, sizeof(BATTERY_INFORMATION));
|
||
ApiData->Capabilities = BATTERY_SYSTEM_BATTERY;
|
||
ApiData->Technology = (UCHAR) BIFData->BatteryTechnology;
|
||
|
||
//
|
||
// Use first four chars of BatteryType as Chemistry string
|
||
//
|
||
ApiData->Chemistry[0] = BIFData->BatteryType[0];
|
||
ApiData->Chemistry[1] = BIFData->BatteryType[1];
|
||
ApiData->Chemistry[2] = BIFData->BatteryType[2];
|
||
ApiData->Chemistry[3] = BIFData->BatteryType[3];
|
||
|
||
ApiData->CriticalBias = 0;
|
||
ApiData->CycleCount = 0;
|
||
|
||
if (BIFData->PowerUnit & CM_BIF_UNITS_AMPS) {
|
||
|
||
//
|
||
// This battery reports in mA we need to convert all the capacities to
|
||
// mW because this is what the OS expects. The algorithm for doing this
|
||
// is:
|
||
//
|
||
// mW = mA * Volts or mW = mA * mV / 1000
|
||
//
|
||
|
||
if (BIFData->DesignVoltage != CM_UNKNOWN_VALUE) {
|
||
|
||
//
|
||
// Convert the DesignCapacity
|
||
//
|
||
|
||
if (BIFData->DesignCapacity != CM_UNKNOWN_VALUE) {
|
||
ApiData->DesignedCapacity = (BIFData->DesignCapacity *
|
||
BIFData->DesignVoltage +
|
||
500) /
|
||
1000;
|
||
} else {
|
||
ApiData->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
|
||
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetStaticInfo - Can't calculate DesignCapacity \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("-------------------- DesignCapacity = CM_UNKNOWN_VALUE\n"));
|
||
}
|
||
|
||
|
||
//
|
||
// Convert the LastFullChargeCapacity
|
||
//
|
||
|
||
if (BIFData->LastFullChargeCapacity != CM_UNKNOWN_VALUE) {
|
||
ApiData->FullChargedCapacity = (BIFData->LastFullChargeCapacity *
|
||
BIFData->DesignVoltage +
|
||
500) /
|
||
1000;
|
||
} else {
|
||
ApiData->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
|
||
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetStaticInfo - Can't calculate LastFullChargeCapacity \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("-------------------- LastFullChargeCapacity = CM_UNKNOWN_VALUE\n"));
|
||
}
|
||
|
||
|
||
//
|
||
// Convert the DesignCapacityOfWarning
|
||
//
|
||
|
||
if (BIFData->DesignCapacityOfWarning != CM_UNKNOWN_VALUE) {
|
||
ApiData->DefaultAlert2 = (BIFData->DesignCapacityOfWarning *
|
||
BIFData->DesignVoltage +
|
||
500) /
|
||
1000;
|
||
} else {
|
||
ApiData->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
|
||
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetStaticInfo - Can't calculate DesignCapacityOfWarning \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("-------------------- DesignCapacityOfWarning = CM_UNKNOWN_VALUE\n"));
|
||
}
|
||
|
||
|
||
//
|
||
// Convert the DesignCapacityOfLow
|
||
//
|
||
|
||
if (BIFData->DesignCapacityOfLow != CM_UNKNOWN_VALUE) {
|
||
ApiData->DefaultAlert1 = (BIFData->DesignCapacityOfLow *
|
||
BIFData->DesignVoltage +
|
||
500) /
|
||
1000;
|
||
} else {
|
||
ApiData->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
|
||
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetStaticInfo - Can't calculate DesignCapacityOfLow \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("-------------------- DesignCapacityOfLow = CM_UNKNOWN_VALUE\n"));
|
||
}
|
||
|
||
|
||
//
|
||
// Convert the BatteryCapacityGran_1
|
||
//
|
||
|
||
if (BIFData->BatteryCapacityGran_1 != CM_UNKNOWN_VALUE) {
|
||
CmBatt->Info.ApiGranularity_1 = (BIFData->BatteryCapacityGran_1 *
|
||
BIFData->DesignVoltage +
|
||
500) /
|
||
1000;
|
||
} else {
|
||
CmBatt->Info.ApiGranularity_1 = BATTERY_UNKNOWN_CAPACITY;
|
||
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetStaticInfo - Can't calculate BatteryCapacityGran_1 \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("-------------------- BatteryCapacityGran_1 = CM_UNKNOWN_VALUE\n"));
|
||
}
|
||
|
||
|
||
//
|
||
// Convert the BatteryCapacityGran_2
|
||
//
|
||
|
||
if (BIFData->BatteryCapacityGran_2 != CM_UNKNOWN_VALUE) {
|
||
CmBatt->Info.ApiGranularity_2 = (BIFData->BatteryCapacityGran_2 *
|
||
BIFData->DesignVoltage +
|
||
500) /
|
||
1000;
|
||
} else {
|
||
CmBatt->Info.ApiGranularity_2 = BATTERY_UNKNOWN_CAPACITY;
|
||
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetStaticInfo - Can't calculate BatteryCapacityGran_2 \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("-------------------- BatteryCapacityGran_2 = CM_UNKNOWN_VALUE\n"));
|
||
}
|
||
} else {
|
||
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("CmBattGetStaticInfo - Can't calculate Capacities \n"));
|
||
CmBattPrint (CMBATT_ERROR_ONLY,
|
||
("-------------------- DesignVoltage = CM_UNKNOWN_VALUE\n"));
|
||
|
||
ApiData->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
|
||
ApiData->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
|
||
ApiData->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
|
||
ApiData->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
|
||
CmBatt->Info.ApiGranularity_1 = BATTERY_UNKNOWN_CAPACITY;
|
||
CmBatt->Info.ApiGranularity_2 = BATTERY_UNKNOWN_CAPACITY;
|
||
}
|
||
} else {
|
||
ApiData->DesignedCapacity = BIFData->DesignCapacity;
|
||
ApiData->FullChargedCapacity = BIFData->LastFullChargeCapacity;
|
||
ApiData->DefaultAlert1 = BIFData->DesignCapacityOfLow;
|
||
ApiData->DefaultAlert2 = BIFData->DesignCapacityOfWarning;
|
||
CmBatt->Info.ApiGranularity_1 = BIFData->BatteryCapacityGran_1;
|
||
CmBatt->Info.ApiGranularity_2 = BIFData->BatteryCapacityGran_2;
|
||
|
||
}
|
||
|
||
CmBatt->Info.ModelNum = (PUCHAR) &BIFData->ModelNumber;
|
||
CmBatt->Info.ModelNumLen = (ULONG) strlen (CmBatt->Info.ModelNum);
|
||
|
||
CmBatt->Info.SerialNum = (PUCHAR) &BIFData->SerialNumber;
|
||
CmBatt->Info.SerialNumLen = (ULONG) strlen (CmBatt->Info.SerialNum);
|
||
|
||
CmBatt->Info.OEMInfo = (PUCHAR) &BIFData->OEMInformation;
|
||
CmBatt->Info.OEMInfoLen = (ULONG) strlen (CmBatt->Info.OEMInfo);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if ((CmBatt->Info.Tag) == BATTERY_TAG_INVALID || (BatteryTag != CmBatt->Info.Tag)) {
|
||
// If the tag has been invalidated since we started, fail the request.
|
||
Status = STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
// If somthing failed, make sure the cache is marked as invalid.
|
||
InterlockedExchange (&CmBatt->CacheState, 0);
|
||
}
|
||
|
||
CmBattPrint (CMBATT_TRACE ,("CmBattGetStaticInfo: Exit\n"));
|
||
return Status;
|
||
}
|