windows-nt/Source/XPSP1/NT/base/busdrv/acpi/battc/bsrv.c

2007 lines
61 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
bsrv.c
Abstract:
Service battery class device
Author:
Ken Reneris
Environment:
Notes:
Revision History:
--*/
#include "battcp.h"
VOID
BattCIoctl (
IN PBATT_INFO BattInfo,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
VOID
BattCCheckTagQueue (
IN PBATT_NP_INFO BattNPInfo,
IN PBATT_INFO BattInfo
);
VOID
BattCCheckStatusQueue (
IN PBATT_NP_INFO BattNPInfo,
IN PBATT_INFO BattInfo
);
VOID
BattCWmi (
IN PBATT_NP_INFO BattNPInfo,
IN PBATT_INFO BattInfo,
IN PBATT_WMI_REQUEST WmiRequest
);
VOID
BattCMiniportStatus (
IN PBATT_INFO BattInfo,
IN NTSTATUS Status
);
VOID
BattCCompleteIrpQueue (
IN PLIST_ENTRY Queue,
IN NTSTATUS Status
);
VOID
BattCCompleteWmiQueue (
IN PLIST_ENTRY Queue,
IN NTSTATUS Status
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,BattCCheckStatusQueue)
#pragma alloc_text(PAGE,BattCCheckTagQueue)
#pragma alloc_text(PAGE,BattCWorkerThread)
#pragma alloc_text(PAGE,BattCIoctl)
#endif
VOID
BattCWorkerDpc (
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
DPC used to get worker thread when status needs to be checked.
Arguments:
Dpc - the worker dpc
Return Value:
None.
--*/
{
PBATT_NP_INFO BattNPInfo;
BattNPInfo = (PBATT_NP_INFO) DeferredContext;
BattCQueueWorker (BattNPInfo, TRUE);
// Release Removal Lock
if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) {
KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE);
}
BattPrint ((BATT_LOCK), ("BattCWorkerDpc: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
}
VOID
BattCTagDpc (
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
DPC used to get worker thread when status needs to be checked.
Arguments:
Dpc - the worker dpc
Return Value:
None.
--*/
{
PBATT_NP_INFO BattNPInfo;
BattNPInfo = (PBATT_NP_INFO) DeferredContext;
InterlockedExchange(&BattNPInfo->CheckTag, 1);
BattCQueueWorker (BattNPInfo, FALSE);
// Release Removal Lock
if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) {
KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE);
}
BattPrint ((BATT_LOCK), ("BattCTagDpc: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
}
VOID
BattCCancelStatus (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Queued status IRP is being canceled
Arguments:
DeviceObject - Device object of the miniport. Not useful to the
class driver - ignored.
Irp - Irp being cancelled
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpNextSp;
PBATT_NP_INFO BattNPInfo;
//
// IRP is flagged as needing cancled, cause a check status which will
// complete any pending cancled irps
//
IrpNextSp = IoGetNextIrpStackLocation(Irp);
BattNPInfo = (PBATT_NP_INFO) IrpNextSp->Parameters.Others.Argument4;
BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCCancelStatus. Irp - %08x\n", BattNPInfo->DeviceNum, Irp));
BattCQueueWorker (BattNPInfo, TRUE);
//
// The cancel Spinlock must be released after attempting to queue the
// worker thread so that there is no timeing problems on remove.
//
IoReleaseCancelSpinLock(Irp->CancelIrql);
}
VOID
BattCCancelTag (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Queued tag IRP is being canceled
Arguments:
DeviceObject - Device object of the miniport. Not useful to the
class driver - ignored.
Irp - Irp being cancelled
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpNextSp;
PBATT_NP_INFO BattNPInfo;
//
// IRP is flagged as needing canceled. Cause a check tag which will
// complete any pending cancled irps
//
IrpNextSp = IoGetNextIrpStackLocation(Irp);
BattNPInfo = (PBATT_NP_INFO) IrpNextSp->Parameters.Others.Argument4;
BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCCancelTag. Irp - %08x\n", BattNPInfo->DeviceNum, Irp));
InterlockedExchange(&BattNPInfo->CheckTag, 1);
BattCQueueWorker (BattNPInfo, FALSE);
//
// The cancel Spinlock must be released after attempting to queue the
// worker thread so that there is no timeing problems on remove.
//
IoReleaseCancelSpinLock(Irp->CancelIrql);
}
VOID
BattCQueueWorker (
IN PBATT_NP_INFO BattNPInfo,
IN BOOLEAN CheckStatus
)
/*++
Routine Description:
Get worker thread to check the battery state (IoQueue). The
battery IOs are serialized here as only one worker thread is
used to process the battery IOs. If the worker thread is already
running, it is flagged to loop are re-check the state. If the
worker thread is not running, one is queued.
If CheckStatus is set, the worker thread is informed that the
batteries current status is read and the pending status queue
is checked.
Arguments:
BattNPInfo - Battery to check
CheckStatus - Whether or not the status also needs checked
Return Value:
None.
--*/
{
PBATT_INFO BattInfo = BattNPInfo->BattInfo;
//
// Add 1 to the WorkerActive value, if this is the first count
// queue a worker thread
//
BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCQueueWorker.\n", BattNPInfo->DeviceNum));
if (CheckStatus) {
InterlockedExchange(&BattNPInfo->CheckStatus, 1);
InterlockedExchange (&BattNPInfo->CheckTag, 1);
}
//
// Increment WorkerActive count. If the worker thread is already running,
// there is no need to requeue it.
//
if (InterlockedIncrement(&BattNPInfo->WorkerActive) == 1) {
// Removal lock.
if ((BattNPInfo->WantToRemove == TRUE) && (KeGetCurrentIrql() == PASSIVE_LEVEL)) {
// Check Irql to make sure this wasn't called by an ISR. If so,
// queue the worker rather than complete the requests in this thread.
//
// Empty IRP queues.
//
BattCCompleteIrpQueue(&(BattInfo->IoQueue), STATUS_DEVICE_REMOVED);
BattCCompleteIrpQueue(&(BattInfo->TagQueue), STATUS_DEVICE_REMOVED);
BattCCompleteIrpQueue(&(BattInfo->StatusQueue), STATUS_DEVICE_REMOVED);
BattCCompleteIrpQueue(&(BattInfo->WmiQueue), STATUS_DEVICE_REMOVED);
//
// Remove lock and trigger Remove function if necessary.
//
if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) {
KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE);
}
BattPrint ((BATT_LOCK), ("BattCQueueWorker: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
} else {
ExQueueWorkItem (&BattNPInfo->WorkerThread, DelayedWorkQueue);
}
}
}
VOID
BattCWorkerThread (
IN PVOID Context
)
/*++
Routine Description:
Battery IO worker thread entry point.
N.B. There is only one worker thread handling the battery at any one time
Arguments:
Context - BattInfo. Battery to check
Return Value:
None.
--*/
{
PBATT_INFO BattInfo;
PBATT_NP_INFO BattNPInfo;
PLIST_ENTRY Entry;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
ULONG i;
PAGED_CODE();
BattNPInfo = (PBATT_NP_INFO) Context;
BattInfo = BattNPInfo->BattInfo;
BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCWorkerThread entered.\n", BattNPInfo->DeviceNum));
//
// Loop while there is work to check
//
for (; ;) {
// Removal code. This makes sure that the structures aren't freed in the middle of
// processing. All Irp Queues will be emptied by BatteryClassUnload.
if (BattNPInfo->WantToRemove == TRUE) {
//
// Empty IRP queues.
//
BattCCompleteIrpQueue(&(BattInfo->IoQueue), STATUS_DEVICE_REMOVED);
BattCCompleteIrpQueue(&(BattInfo->TagQueue), STATUS_DEVICE_REMOVED);
BattCCompleteIrpQueue(&(BattInfo->StatusQueue), STATUS_DEVICE_REMOVED);
BattCCompleteIrpQueue(&(BattInfo->WmiQueue), STATUS_DEVICE_REMOVED);
//
// Signal BatteryClassUnload that it is safe to return.
//
if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) {
KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE);
}
BattPrint ((BATT_LOCK), ("BattCWorkerThread: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
return;
}
//
// Acquire queue locks
//
ExAcquireFastMutex (&BattNPInfo->Mutex);
//
// While there are IRPs in the IoQueue handle them
//
while (!IsListEmpty(&BattInfo->IoQueue)) {
//
// Remove entry from IoQueue and drop device lock
//
Entry = RemoveHeadList(&BattInfo->IoQueue);
ExReleaseFastMutex (&BattNPInfo->Mutex);
//
// Handle this entry
//
Irp = CONTAINING_RECORD (
Entry,
IRP,
Tail.Overlay.ListEntry
);
BattPrint (BATT_IOCTL, ("BattC (%d): WorkerThread, Got Irp - %x\n", BattNPInfo->DeviceNum, Irp));
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_BATTERY_QUERY_STATUS &&
IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof (BATTERY_WAIT_STATUS) &&
IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (BATTERY_STATUS)) {
BattPrint (BATT_IOCTL,
("BattC (%d): Received QueryStatus Irp - %x, timeout - %x\n",
BattNPInfo->DeviceNum,
Irp,
((PBATTERY_WAIT_STATUS)Irp->AssociatedIrp.SystemBuffer)->Timeout));
//
// Valid query status irp, put it on the StatusQueue and handle later
//
InterlockedExchange (&BattNPInfo->CheckStatus, 1);
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->Parameters.Others.Argument1 = (PVOID) 0;
IrpSp->Parameters.Others.Argument2 = (PVOID) 0;
IrpSp->Parameters.Others.Argument3 = NULL;
IrpSp->Parameters.Others.Argument4 = BattNPInfo;
//
// Set IRPs cancel routine
//
IoSetCancelRoutine (Irp, BattCCancelStatus);
//
// Queue it
//
InsertTailList (
&BattInfo->StatusQueue,
&Irp->Tail.Overlay.ListEntry
);
} else if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_BATTERY_QUERY_TAG &&
(IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof (ULONG) ||
IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0) &&
IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (ULONG)) {
BattPrint (BATT_IOCTL,
("BattC (%d): Received QueryTag with timeout %x\n",
BattNPInfo->DeviceNum,
*((PULONG) Irp->AssociatedIrp.SystemBuffer))
);
//
// Valid query tag irp, put it on the TagQueue and handle later
//
InterlockedExchange (&BattNPInfo->CheckTag, 1);
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->Parameters.Others.Argument1 = (PVOID) 0;
IrpSp->Parameters.Others.Argument2 = (PVOID) 0;
IrpSp->Parameters.Others.Argument3 = NULL;
IrpSp->Parameters.Others.Argument4 = BattNPInfo;
//
// Set IRPs cancel routine
//
IoSetCancelRoutine (Irp, BattCCancelTag);
InsertTailList (
&BattInfo->TagQueue,
&Irp->Tail.Overlay.ListEntry
);
} else {
//
// Handle IRP now
//
BattPrint (BATT_IOCTL, ("BattC (%d): Calling BattCIoctl with irp %x\n", BattNPInfo->DeviceNum, Irp));
BattCIoctl (BattInfo, Irp, IrpSp);
}
//
// Acquire IoQueue lock and check for anything else in the IoQueueu
//
ExAcquireFastMutex (&BattNPInfo->Mutex);
}
//
// Done with the IoQueue
//
ExReleaseFastMutex (&BattNPInfo->Mutex);
//
// Check pending status queue
//
if (BattNPInfo->CheckStatus) {
BattCCheckStatusQueue (BattNPInfo, BattInfo);
}
//
// Check pending tag queue
//
if (BattNPInfo->CheckTag) {
BattCCheckTagQueue (BattNPInfo, BattInfo);
}
//
// Acquire queue locks
//
ExAcquireFastMutex (&BattNPInfo->Mutex);
//
// While there are outstanding WMI requests handle them
//
while (!IsListEmpty(&BattInfo->WmiQueue)) {
PBATT_WMI_REQUEST WmiRequest;
//
// Remove entry from WmiQueue and drop device lock
//
Entry = RemoveHeadList(&BattInfo->WmiQueue);
ExReleaseFastMutex (&BattNPInfo->Mutex);
//
// Handle this entry
//
WmiRequest = CONTAINING_RECORD (
Entry,
BATT_WMI_REQUEST,
ListEntry
);
BattPrint (BATT_WMI, ("BattC (%d): WorkerThread, Got WMI Rewest - %x\n", BattNPInfo->DeviceNum, WmiRequest));
//
// Process the request here.
//
BattCWmi (BattNPInfo, BattInfo, WmiRequest);
//
// Acquire IoQueue lock and check for anything else in the IoQueueu
//
ExAcquireFastMutex (&BattNPInfo->Mutex);
}
//
// Done with the IoQueue
//
ExReleaseFastMutex (&BattNPInfo->Mutex);
//
// See if we need to recheck
//
i = InterlockedDecrement(&BattNPInfo->WorkerActive);
BattPrint (BATT_TRACE, ("BattC (%d): WorkerActive count=%x\n", BattNPInfo->DeviceNum, i));
if (i == 0) {
// done
BattPrint (BATT_TRACE, ("BattC (%d): WorkerActive count is zero!\n", BattNPInfo->DeviceNum));
break;
}
//
// No need to loop multiple times, if count is not one lower it
//
if (i != 1) {
BattPrint (BATT_TRACE, ("BattC (%d): WorkerActive set to 1\n", BattNPInfo->DeviceNum));
InterlockedExchange(&BattNPInfo->WorkerActive, 1);
}
}
BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCWorkerThread exiting.\n", BattNPInfo->DeviceNum));
}
VOID
BattCIoctl (
IN PBATT_INFO BattInfo,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Completes the battery IOCTL request.
N.B. must be invoked from the non-rentrant worker thread
Arguments:
BattInfo - Battery
Irp - IOCTL request
IrpSp - Current stack location
Return Value:
IRP has been completed
--*/
{
ULONG InputLen, OutputLen;
PVOID IOBuffer;
NTSTATUS Status;
PBATTERY_QUERY_INFORMATION QueryInfo;
PBATTERY_SET_INFORMATION SetInformation;
#if DEBUG
BATTERY_QUERY_INFORMATION_LEVEL inflevel;
#endif
PAGED_CODE();
BattPrint ((BATT_TRACE), ("BattC (%d): BattCIoctl called\n", BattInfo->BattNPInfo->DeviceNum));
IOBuffer = Irp->AssociatedIrp.SystemBuffer;
InputLen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
OutputLen = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// Dispatch IOCtl request to proper miniport function
//
Status = STATUS_INVALID_BUFFER_SIZE;
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_BATTERY_QUERY_TAG:
//
// Query tag only gets here if the input or output buffer lengths are
// wrong. Return STATUS_INVALID_BUFFER_SIZE
//
break;
case IOCTL_BATTERY_QUERY_INFORMATION:
if (InputLen != sizeof (BATTERY_QUERY_INFORMATION)) {
//
// Don't check size of the output buffer since it is variable size.
// This is checked in Mp.QueryInformation
//
// Return STATUS_INVALID_BUFFER_SIZE
//
break;
}
QueryInfo = (PBATTERY_QUERY_INFORMATION) IOBuffer;
#if DEBUG
inflevel = QueryInfo->InformationLevel;
#endif
Status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
QueryInfo->BatteryTag,
QueryInfo->InformationLevel,
QueryInfo->AtRate,
IOBuffer,
OutputLen,
&OutputLen
);
#if DEBUG
if (inflevel == BatteryInformation) {
BattInfo->FullChargedCap = ((PBATTERY_INFORMATION)IOBuffer)->FullChargedCapacity;
}
#endif
BattPrint ((BATT_MP_DATA), ("BattC (%d): Mp.QueryInformation status = %08x, Level = %d\n",
BattInfo->BattNPInfo->DeviceNum, Status, QueryInfo->InformationLevel));
break;
case IOCTL_BATTERY_QUERY_STATUS:
//
// Query status only gets here if the input or output buffer lengths are
// wrong. Return STATUS_INVALID_BUFFER_SIZE
//
break;
case IOCTL_BATTERY_SET_INFORMATION:
if ((InputLen != sizeof(BATTERY_SET_INFORMATION)) || (OutputLen != 0)) {
break;
}
SetInformation = (PBATTERY_SET_INFORMATION) IOBuffer;
if (BattInfo->Mp.SetInformation != NULL) {
Status = BattInfo->Mp.SetInformation (
BattInfo->Mp.Context,
SetInformation->BatteryTag,
SetInformation->InformationLevel,
SetInformation->Buffer
);
BattPrint ((BATT_MP_DATA), ("BattC (%d): Mp.SetInformation status = %08x, Level = %d\n",
BattInfo->BattNPInfo->DeviceNum, Status, SetInformation->InformationLevel));
} else {
Status = STATUS_NOT_SUPPORTED;
}
break;
default:
Status = STATUS_NOT_IMPLEMENTED;
break;
}
BattCMiniportStatus (BattInfo, Status);
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = OutputLen;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
}
VOID
BattCCheckStatusQueue (
IN PBATT_NP_INFO BattNPInfo,
IN PBATT_INFO BattInfo
)
/*++
Routine Description:
Gets the batteries current status, and checks the pending
status queue for possible IRP completion. Resets the miniport
notification settings if needed.
N.B. Must be invoked from the non-rentrant worker thread.
BattNPInfo->CheckStatus must be non-zero.
Arguments:
BattNPInfo - Battery
BattInfo - Battery
Return Value:
None
--*/
{
PLIST_ENTRY Entry;
PBATTERY_WAIT_STATUS BatteryWaitStatus;
PIRP Irp;
PIO_STACK_LOCATION IrpSp, IrpNextSp;
BATTERY_NOTIFY Notify;
LARGE_INTEGER NextTime;
LARGE_INTEGER CurrentTime;
LARGE_INTEGER li;
ULONG TimeIncrement;
BOOLEAN ReturnCurrentStatus;
NTSTATUS Status;
BOOLEAN StatusNotified;
BattPrint ((BATT_TRACE), ("BattC (%d): BattCCheckStatusQueue called\n", BattInfo->BattNPInfo->DeviceNum));
PAGED_CODE();
TimeIncrement = KeQueryTimeIncrement();
//
// Loop while status needs checked, check pending status IRPs
//
while (InterlockedExchange(&BattNPInfo->CheckStatus, 0)) {
Notify.PowerState = BattInfo->Status.PowerState;
Notify.LowCapacity = 0;
Notify.HighCapacity = (ULONG) -1;
//
// Set to recheck no later than MIN_STATUS_POLL_RATE (3 min) from now.
//
NextTime.QuadPart = MIN_STATUS_POLL_RATE;
//
// If the StatusQueue is empty, the status doesn't need to be read
// at this time. BattNPInfo->StatusNotified is not modified
// so the next time an IRP comes through, we'll re-read the status.
// The local value of StatusNotified needs to be set correctly to
// disable notifications if necessary.
//
if (IsListEmpty (&BattInfo->StatusQueue)) {
StatusNotified = (BOOLEAN)BattNPInfo->StatusNotified;
break;
}
StatusNotified = FALSE;
//
// Pickup status notified flag
//
if (BattNPInfo->StatusNotified) {
InterlockedExchange (&BattNPInfo->StatusNotified, 0);
StatusNotified = TRUE;
// Reset the invalid data retry count when we get a notification.
#if DEBUG
if (BattInfo->InvalidRetryCount != 0) {
BattPrint (BATT_DEBUG, ("BattC (%d) Reset InvalidRetryCount\n", BattNPInfo->DeviceNum));
}
#endif
BattInfo->InvalidRetryCount = 0;
}
KeQueryTickCount (&CurrentTime);
CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
if (StatusNotified ||
CurrentTime.QuadPart - BattInfo->StatusTime > STATUS_VALID_TIME) {
//
// Get the batteries current status
//
Status = BattInfo->Mp.QueryStatus (
BattInfo->Mp.Context,
BattInfo->Tag,
&BattInfo->Status
);
if (!NT_SUCCESS(Status)) {
//
// Battery status is not valid, complete all pending status irps
//
BattPrint ((BATT_MP_ERROR), ("BattC (%d) CheckStatus: Status read err = %x\n", BattNPInfo->DeviceNum, Status));
BattCCompleteIrpQueue (&(BattInfo->StatusQueue), Status);
break;
}
BattPrint ((BATT_MP_DATA), ("BattC (%d) MP.QueryStatus: st[%08X] Cap[%08X] V[%08x] R[%08x]\n",
BattNPInfo->DeviceNum,
BattInfo->Status.PowerState,
BattInfo->Status.Capacity,
BattInfo->Status.Voltage,
BattInfo->Status.Rate
));
Notify.PowerState = BattInfo->Status.PowerState;
//
// Get the current time to compute timeouts on status query requests
//
KeQueryTickCount (&CurrentTime);
CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
BattInfo->StatusTime = CurrentTime.QuadPart;
}
//
// Check each pending Status IRP
//
BattPrint ((BATT_IOCTL_QUEUE), ("BattC (%d) Processing StatusQueue\n", BattNPInfo->DeviceNum));
Entry = BattInfo->StatusQueue.Flink;
while (Entry != &BattInfo->StatusQueue) {
//
// Get IRP to check
//
Irp = CONTAINING_RECORD (
Entry,
IRP,
Tail.Overlay.ListEntry
);
IrpSp = IoGetCurrentIrpStackLocation(Irp);
IrpNextSp = IoGetNextIrpStackLocation(Irp);
BatteryWaitStatus = (PBATTERY_WAIT_STATUS) Irp->AssociatedIrp.SystemBuffer;
#if DEBUG
if (BattInfo->FullChargedCap == 0) {
BattInfo->FullChargedCap = 1000;
}
#endif
BattPrint ((BATT_IOCTL_QUEUE), ("BattC (%d) StatusQueue: 0x%08x=%d -- 0x%08x=%d time=%08x, st=%08x\n",
BattNPInfo->DeviceNum,
BatteryWaitStatus->HighCapacity, (ULONG) (((LONGLONG) BatteryWaitStatus->HighCapacity * 1000) / BattInfo->FullChargedCap),
BatteryWaitStatus->LowCapacity, (ULONG) (((LONGLONG) BatteryWaitStatus->LowCapacity * 1000) / BattInfo->FullChargedCap),
BatteryWaitStatus->Timeout,
BatteryWaitStatus->PowerState));
//
// Get next request
//
Entry = Entry->Flink;
//
// If status is in error, or tag no longer matches abort the
// request accordingly
//
if (BattInfo->Tag != BatteryWaitStatus->BatteryTag) {
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
}
//
// If IRP is flagged as cancelled, complete it
//
if (Irp->Cancel) {
Irp->IoStatus.Status = STATUS_CANCELLED;
}
//
// If request is still pending, check it
//
if (Irp->IoStatus.Status == STATUS_PENDING) {
ReturnCurrentStatus = FALSE;
if (BattInfo->Status.PowerState != BatteryWaitStatus->PowerState ||
BattInfo->Status.Capacity < BatteryWaitStatus->LowCapacity ||
BattInfo->Status.Capacity > BatteryWaitStatus->HighCapacity) {
BattPrint((BATT_IOCTL_DATA), ("BattC (%d) CheckStatusQueue, Returning Current Status, Asked For:\n"
"----------- Irp.PowerState = %x\n"
"----------- Irp.LowCapacity = %x\n"
"----------- Irp.HighCapacity = %x\n"
"----------- BattInfo.PowerState = %x\n"
"----------- BattInfo.Capacity = %x\n",
BattNPInfo->DeviceNum,
BatteryWaitStatus->PowerState,
BatteryWaitStatus->LowCapacity,
BatteryWaitStatus->HighCapacity,
BattInfo->Status.PowerState,
BattInfo->Status.Capacity)
);
//
// Complete this IRP with the current status
//
ReturnCurrentStatus = TRUE;
} else {
//
// Compute time when the request expires
//
BattPrint ((BATT_IOCTL_DATA), ("BattC (%d) CheckStatusQueue: Status Request %x Waiting For:\n"
"----------- Timeout = %x\n"
"----------- Irp.PowerState = %x\n"
"----------- Irp.LowCapacity = %x\n"
"----------- Irp.HighCapacity = %x\n",
BattNPInfo->DeviceNum,
Irp,
BatteryWaitStatus->Timeout,
BatteryWaitStatus->PowerState,
BatteryWaitStatus->LowCapacity,
BatteryWaitStatus->HighCapacity)
);
if (BatteryWaitStatus->Timeout &&
IrpNextSp->Parameters.Others.Argument1 == NULL &&
IrpNextSp->Parameters.Others.Argument2 == NULL) {
// initialize it
li.QuadPart = CurrentTime.QuadPart +
((ULONGLONG) BatteryWaitStatus->Timeout * NTMS);
IrpNextSp->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)li.LowPart);
IrpNextSp->Parameters.Others.Argument2 = (PVOID)((ULONG_PTR)li.HighPart);
}
li.LowPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument1);
li.HighPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument2);
li.QuadPart -= CurrentTime.QuadPart;
if (li.QuadPart <= 0) {
//
// Time's up, complete it
//
ReturnCurrentStatus = TRUE;
} else {
//
// If waiting forever, no need to set a timer
//
if (BatteryWaitStatus->Timeout != 0xFFFFFFFF) {
//
// Check if this will be the next timeout time -- we will use
// the minimum timeout of the pending requests.
//
if (li.QuadPart < NextTime.QuadPart) {
NextTime.QuadPart = li.QuadPart;
}
}
}
}
if (!ReturnCurrentStatus) {
//
// IRP is still pending, calculate LCD of all waiting IRPs
//
if (BatteryWaitStatus->LowCapacity > Notify.LowCapacity) {
Notify.LowCapacity = BatteryWaitStatus->LowCapacity;
}
if (BatteryWaitStatus->HighCapacity < Notify.HighCapacity) {
Notify.HighCapacity = BatteryWaitStatus->HighCapacity;
}
} else {
//
// Return current battery status
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(BattInfo->Status);
RtlCopyMemory (
Irp->AssociatedIrp.SystemBuffer,
&BattInfo->Status,
sizeof(BattInfo->Status)
);
}
}
//
// If this request is no longer pending, complete it
//
if (Irp->IoStatus.Status != STATUS_PENDING) {
BattPrint (BATT_IOCTL,
("BattC (%d): completing QueryStatus irp - %x, status - %x\n",
BattNPInfo->DeviceNum,
Irp,
Irp->IoStatus.Status));
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
IoSetCancelRoutine (Irp, NULL);
IoCompleteRequest (Irp, IO_NO_INCREMENT);
}
}
}
//
// Status check complete
//
if (IsListEmpty (&BattInfo->StatusQueue)) {
//
// Nothing pending, if being notified disable the notifications
//
if (StatusNotified) {
BattInfo->Mp.DisableStatusNotify (BattInfo->Mp.Context);
BattInfo->StatusTime = 0;
BattPrint ((BATT_MP_DATA), ("BattC (%d) CheckStatus: called Mp.DisableStatusNotify\n", BattNPInfo->DeviceNum));
}
} else {
//
// Set notification setting
//
Status = BattInfo->Mp.SetStatusNotify (
BattInfo->Mp.Context,
BattInfo->Tag,
&Notify
);
if (NT_SUCCESS(Status)) {
//
// New notification set, remember it
//
BattPrint (BATT_MP_DATA, ("BattC (%d) Mp.SetStatusNotify: Notify set for: State=%x, Low=%x, High=%x\n",
BattNPInfo->DeviceNum,
Notify.PowerState,
Notify.LowCapacity,
Notify.HighCapacity
));
} else {
//
// Could not set notification, handle error
//
BattPrint (BATT_MP_ERROR, ("BattC (%d) Mp.SetStatusNotify: failed (%x), will poll\n", BattNPInfo->DeviceNum, Status));
BattCMiniportStatus (BattInfo, Status);
//
// Compute poll time
//
li.QuadPart = MIN_STATUS_POLL_RATE;
if (BattInfo->Status.Capacity == BATTERY_UNKNOWN_CAPACITY) {
// Retry 10 times at a polling rate of 1 second.
// Then revert to the slow polling rate.
if (BattInfo->InvalidRetryCount < INVALID_DATA_MAX_RETRY) {
BattInfo->InvalidRetryCount++;
li.QuadPart = INVALID_DATA_POLL_RATE;
BattPrint (BATT_DEBUG, ("BattC (%d) InvalidRetryCount = %d\n",
BattNPInfo->DeviceNum, BattInfo->InvalidRetryCount));
} else {
BattPrint (BATT_DEBUG, ("BattC (%d) InvalidRetryCount = %d. Using slow polling rate.\n",
BattNPInfo->DeviceNum, BattInfo->InvalidRetryCount));
li.QuadPart = MIN_STATUS_POLL_RATE;
}
} else if ((BattInfo->Status.Rate != 0) && (BattInfo->Status.Rate != BATTERY_UNKNOWN_RATE)) {
if (BattInfo->Status.Rate > 0) {
li.QuadPart = Notify.HighCapacity - BattInfo->Status.Capacity;
} else if (BattInfo->Status.Rate < 0) {
li.QuadPart = Notify.LowCapacity - BattInfo->Status.Capacity;
}
// convert to 3/4 its target time
li.QuadPart = li.QuadPart * ((ULONGLONG) NTMIN * 45);
li.QuadPart = li.QuadPart / (LONGLONG)(BattInfo->Status.Rate);
//
// Bound it
//
if (li.QuadPart > MIN_STATUS_POLL_RATE) {
// poll at least this fast
li.QuadPart = MIN_STATUS_POLL_RATE;
} else if (li.QuadPart < MAX_STATUS_POLL_RATE) {
// but not faster then this
li.QuadPart = MAX_STATUS_POLL_RATE;
}
}
//
// If sooner then NextTime, adjust NextTime
//
if (li.QuadPart < NextTime.QuadPart) {
NextTime.QuadPart = li.QuadPart;
}
}
//
// If there's a NextTime, queue the timer to recheck
//
if (NextTime.QuadPart) {
NextTime.QuadPart = -NextTime.QuadPart;
//
// Acquire a remove lock.
//
InterlockedIncrement (&BattNPInfo->InUseCount);
BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Aqcuired remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
if (BattNPInfo->WantToRemove == TRUE) {
//
// If BatteryClassUnload is waiting to remove the device:
// Don't set the timer.
// Release the remove lock just acquired.
// No need to notify BatteryclassUnload because
// at this point there is at least one other lock held.
//
InterlockedDecrement(&BattNPInfo->InUseCount);
BattPrint (BATT_NOTE,
("BattC (%d) CheckStatus: Poll cancel because of device removal.\n",
BattNPInfo->DeviceNum));
BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
} else {
if (KeSetTimer (&BattNPInfo->WorkerTimer, NextTime, &BattNPInfo->WorkerDpc)) {
//
// If the timer was already set, we need to release a remove lock since
// there was already one aquired the last time this timer was set.
//
InterlockedDecrement(&BattNPInfo->InUseCount);
BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Released extra remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
}
#if DEBUG
NextTime.QuadPart = (-NextTime.QuadPart) / (ULONGLONG) NTSEC;
BattPrint (BATT_NOTE, ("BattC (%d) CheckStatus: Poll in %d seconds (%d minutes)\n",
BattNPInfo->DeviceNum, NextTime.LowPart, NextTime.LowPart/60));
#endif
}
} else {
//
// There should always be a NextTime.
//
ASSERT(FALSE);
}
} // if (IsListEmpty (&BattInfo->StatusQueue)) {...} else
}
VOID
BattCCheckTagQueue (
IN PBATT_NP_INFO BattNPInfo,
IN PBATT_INFO BattInfo
)
/*++
Routine Description:
Gets the batteries current tag, and checks the pending
tag queue for possible IRP completion. Resets the miniport
notification settings if needed.
N.B. must be invoked from the non-reentrant worker thread
Arguments:
BattNPInfo - Battery
BattInfo - Battery
Return Value:
None
--*/
{
PLIST_ENTRY Entry;
PIRP Irp;
PIO_STACK_LOCATION IrpSp, IrpNextSp;
LARGE_INTEGER NextTime;
LARGE_INTEGER CurrentTime;
LARGE_INTEGER li;
ULONG TimeIncrement;
BOOLEAN ReturnCurrentStatus;
NTSTATUS Status;
ULONG batteryTimeout;
BOOLEAN TagNotified;
ULONG tmpTag = BATTERY_TAG_INVALID;
BattPrint ((BATT_TRACE), ("BattC (%d): BattCCheckTagQueue called\n", BattInfo->BattNPInfo->DeviceNum));
PAGED_CODE();
TimeIncrement = KeQueryTimeIncrement();
//
// Loop while tag needs checked, check pending tag IRPs
//
while (InterlockedExchange(&BattNPInfo->CheckTag, 0)) {
NextTime.QuadPart = 0;
//
// If the Tag Queue is empty, done
// but we need to make sure that we leave TagNotified set to TRUE
// so the next time an IRP comes through,we'll re-read the tag.
//
if (IsListEmpty (&BattInfo->TagQueue)) {
break;
}
TagNotified = FALSE;
//
// Pickup tag notified flag
//
if (BattNPInfo->TagNotified) {
InterlockedExchange (&BattNPInfo->TagNotified, 0);
TagNotified = TRUE;
}
KeQueryTickCount (&CurrentTime);
CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
if (TagNotified ||
CurrentTime.QuadPart - BattInfo->TagTime > STATUS_VALID_TIME) {
//
// Get the battery's current tag
//
tmpTag = 0;
Status = BattInfo->Mp.QueryTag (
BattInfo->Mp.Context,
&tmpTag
);
if (!NT_SUCCESS(Status) && (Status != STATUS_NO_SUCH_DEVICE)) {
//
// Something went wrong, complete all pending tag irps
//
BattPrint (BATT_MP_ERROR, ("BattC (%d) CheckTag: Tag read err = %x\n", BattNPInfo->DeviceNum, Status));
BattCMiniportStatus (BattInfo, Status);
break;
}
BattPrint (BATT_MP_DATA, ("BattC (%d) MP.QueryTag: Status = %08x, Tag = %08x\n",
BattNPInfo->DeviceNum, Status, tmpTag));
if (Status == STATUS_NO_SUCH_DEVICE) {
//
// Get the current time to compute timeouts on tag query requests
//
KeQueryTickCount (&CurrentTime);
CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
BattInfo->TagTime = CurrentTime.QuadPart;
}
}
//
// Check each pending Tag IRP
//
Entry = BattInfo->TagQueue.Flink;
while (Entry != &BattInfo->TagQueue) {
//
// Get IRP to check
//
Irp = CONTAINING_RECORD (
Entry,
IRP,
Tail.Overlay.ListEntry
);
IrpSp = IoGetCurrentIrpStackLocation(Irp);
IrpNextSp = IoGetNextIrpStackLocation(Irp);
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0) {
//
// If no input was given, then use timeout of 0.
//
batteryTimeout = 0;
} else {
batteryTimeout = *((PULONG) Irp->AssociatedIrp.SystemBuffer);
}
//
// Get next request
//
Entry = Entry->Flink;
//
// If IRP is flagged as cancelled, complete it
//
if (Irp->Cancel) {
BattPrint (BATT_IOCTL, ("BattC (%d): QueryTag irp cancelled - %x\n", BattNPInfo->DeviceNum, Irp));
Irp->IoStatus.Status = STATUS_CANCELLED;
}
//
// If request is still pending, check it
//
if (Irp->IoStatus.Status == STATUS_PENDING) {
ReturnCurrentStatus = FALSE;
if (tmpTag != BATTERY_TAG_INVALID) {
//
// Complete this IRP with the current tag
//
ReturnCurrentStatus = TRUE;
Irp->IoStatus.Status = STATUS_SUCCESS;
} else {
//
// Compute time when the request expires, the battery tag
// is an input parameter that holds the timeout.
//
if (batteryTimeout &&
IrpNextSp->Parameters.Others.Argument1 == NULL &&
IrpNextSp->Parameters.Others.Argument2 == NULL) {
// initialize it
li.QuadPart = CurrentTime.QuadPart + ((ULONGLONG) batteryTimeout * NTMS);
IrpNextSp->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)li.LowPart);
IrpNextSp->Parameters.Others.Argument2 = (PVOID)((ULONG_PTR)li.HighPart);
}
li.LowPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument1);
li.HighPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument2);
li.QuadPart -= CurrentTime.QuadPart;
if (li.QuadPart <= 0) {
//
// Time's up, complete it
//
BattPrint ((BATT_NOTE | BATT_IOCTL), ("BattC (%d): QueryTag irp timeout - %x\n", BattNPInfo->DeviceNum, Irp));
ReturnCurrentStatus = TRUE;
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
} else {
//
// If waiting forever, no need to set a timer
//
if (batteryTimeout != 0xFFFFFFFF) {
//
// Check if this is the next timeout time
//
if (NextTime.QuadPart == 0 || li.QuadPart < NextTime.QuadPart) {
NextTime.QuadPart = li.QuadPart;
}
}
}
}
if (ReturnCurrentStatus) {
//
// Return current battery status
//
*((PULONG) Irp->AssociatedIrp.SystemBuffer) = tmpTag;
Irp->IoStatus.Information = sizeof(ULONG);
if (BattInfo->Tag != tmpTag) {
//
// This is a new battery tag, capture tag
//
BattInfo->Tag = tmpTag;
}
}
}
//
// If this request is no longer pending, complete it
//
if (Irp->IoStatus.Status != STATUS_PENDING) {
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
IoSetCancelRoutine (Irp, NULL);
BattPrint (
(BATT_IOCTL),
("BattC (%d): CheckTag completing request, IRP = %x, status = %x\n",
BattNPInfo->DeviceNum,
Irp,
Irp->IoStatus.Status)
);
IoCompleteRequest (Irp, IO_NO_INCREMENT);
}
}
}
//
// If there's a NextTime, queue the timer to recheck.
// This means there is a tag request with a timout other than 0 or -1.
//
if (NextTime.QuadPart) {
NextTime.QuadPart = -NextTime.QuadPart;
//
// Acquire a remove lock.
//
InterlockedIncrement (&BattNPInfo->InUseCount);
BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Aqcuired remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
if (BattNPInfo->WantToRemove == TRUE) {
//
// If BatteryClassUnload is waiting to remove the device:
// Don't set the timer.
// Release the remove lock just acquired.
// No need to notify BatteryclassUnload because
// at this point there is at least one other lock held.
//
InterlockedDecrement(&BattNPInfo->InUseCount);
BattPrint (BATT_NOTE,
("BattC (%d) CheckTag: Poll cancel because of device removal.\n",
BattNPInfo->DeviceNum));
BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
} else {
if (KeSetTimer (&BattNPInfo->TagTimer, NextTime, &BattNPInfo->TagDpc)){
//
// If the timer was already set, we need to release a remove lock since
// there was already one aquired the last time this timer was set.
//
InterlockedDecrement(&BattNPInfo->InUseCount);
BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Released extra remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
}
#if DEBUG
NextTime.QuadPart = NextTime.QuadPart / -NTSEC;
BattPrint (BATT_NOTE, ("BattC (%d) CheckTag: Poll in %x seconds\n", BattNPInfo->DeviceNum, NextTime.LowPart));
#endif
}
}
}
VOID
BattCWmi (
IN PBATT_NP_INFO BattNPInfo,
IN PBATT_INFO BattInfo,
IN PBATT_WMI_REQUEST WmiRequest
)
/*++
Routine Description:
Processes a single WMI request.
N.B. must be invoked from the non-reentrant worker thread
Arguments:
BattNPInfo - Battery
BattInfo - Battery
WmiRequest - Wmi Request to process
Return Value:
None
--*/
{
NTSTATUS status = STATUS_SUCCESS;
ULONG size = 0;
ULONG OutputLen;
BATTERY_INFORMATION batteryInformation;
PWCHAR tempString;
BattPrint((BATT_WMI), ("BattCWmi (%d): GuidIndex = 0x%x\n",
BattNPInfo->DeviceNum, WmiRequest->GuidIndex));
switch (WmiRequest->GuidIndex) {
case BattWmiStatusId:
size = sizeof (BATTERY_WMI_STATUS);
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Tag = BattInfo->Tag;
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->RemainingCapacity = BattInfo->Status.Capacity;
if (BattInfo->Status.Rate < 0) {
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->ChargeRate = 0;
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->DischargeRate = -BattInfo->Status.Rate;
} else {
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->ChargeRate = BattInfo->Status.Rate;
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->DischargeRate = 0;
}
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Voltage = BattInfo->Status.Voltage;
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->PowerOnline =
(BattInfo->Status.PowerState & BATTERY_POWER_ON_LINE) ? TRUE : FALSE;
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Charging =
(BattInfo->Status.PowerState & BATTERY_CHARGING) ? TRUE : FALSE;
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Discharging =
(BattInfo->Status.PowerState & BATTERY_DISCHARGING) ? TRUE : FALSE;
((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Critical =
(BattInfo->Status.PowerState & BATTERY_CRITICAL) ? TRUE : FALSE;
BattPrint((BATT_WMI), ("BattCWmi (%d): BatteryStatus\n",
BattNPInfo->DeviceNum));
break;
case BattWmiRuntimeId:
size = sizeof (BATTERY_WMI_RUNTIME);
((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->Tag = BattInfo->Tag;
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatteryEstimatedTime,
0,
&((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->EstimatedRuntime,
sizeof(ULONG),
&OutputLen
);
BattPrint((BATT_WMI), ("BattCWmi (%d): EstimateRuntime = %08x, Status = 0x%08x\n",
BattNPInfo->DeviceNum, &((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->EstimatedRuntime, status));
break;
case BattWmiTemperatureId:
size = sizeof (BATTERY_WMI_TEMPERATURE);
((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Tag = BattInfo->Tag;
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatteryTemperature,
0,
&((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Temperature,
sizeof(ULONG),
&OutputLen
);
BattPrint((BATT_WMI), ("BattCWmi (%d): Temperature = %08x, Status = 0x%08x\n",
BattNPInfo->DeviceNum, &((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Temperature, status));
break;
case BattWmiFullChargedCapacityId:
size = sizeof (BATTERY_WMI_FULL_CHARGED_CAPACITY);
((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->Tag = BattInfo->Tag;
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatteryInformation,
0,
&batteryInformation,
sizeof(BATTERY_INFORMATION),
&OutputLen
);
((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->FullChargedCapacity =
batteryInformation.FullChargedCapacity;
BattPrint((BATT_WMI), ("BattCWmi (%d): FullChargedCapacity = %08x, Status = 0x%08x\n",
BattNPInfo->DeviceNum, ((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->FullChargedCapacity, status));
break;
case BattWmiCycleCountId:
size = sizeof (BATTERY_WMI_CYCLE_COUNT);
((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->Tag = BattInfo->Tag;
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatteryInformation,
0,
&batteryInformation,
sizeof(BATTERY_INFORMATION),
&OutputLen
);
((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->CycleCount =
batteryInformation.CycleCount;
BattPrint((BATT_WMI), ("BattCWmi (%d): CycleCount = %08x, Status = 0x%08x\n",
BattNPInfo->DeviceNum, ((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->CycleCount, status));
break;
case BattWmiStaticDataId:
size = sizeof(BATTERY_WMI_STATIC_DATA)+4*MAX_BATTERY_STRING_SIZE*sizeof(WCHAR);
((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Tag = BattInfo->Tag;
// ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->ManufacturerDate[0] =
// ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Granularity =
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatteryInformation,
0,
&batteryInformation,
sizeof(BATTERY_INFORMATION),
&OutputLen
);
if (NT_SUCCESS(status)) {
((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Capabilities =
batteryInformation.Capabilities;
((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Technology =
batteryInformation.Technology;
((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Chemistry =
*(PULONG)batteryInformation.Chemistry;
((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DesignedCapacity =
batteryInformation.DesignedCapacity;
((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DefaultAlert1 =
batteryInformation.DefaultAlert1;
((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DefaultAlert2 =
batteryInformation.DefaultAlert2;
((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->CriticalBias =
batteryInformation.CriticalBias;
tempString = ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Strings;
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatteryDeviceName,
0,
&tempString[1],
MAX_BATTERY_STRING_SIZE,
&OutputLen
);
if (!NT_SUCCESS(status)) {
// Some batteries may not support some types of Information Queries
// Don't fail request, simply leave this one blank.
OutputLen = 0;
}
tempString[0] = (USHORT) OutputLen;
tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatteryManufactureName,
0,
&tempString[1],
MAX_BATTERY_STRING_SIZE,
&OutputLen
);
if (!NT_SUCCESS(status)) {
// Some batteries may not support some types of Information Queries
// Don't fail request, simply leave this one blank.
OutputLen = 0;
}
tempString[0] = (USHORT) OutputLen;
tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatterySerialNumber,
0,
&tempString[1],
MAX_BATTERY_STRING_SIZE,
&OutputLen
);
if (!NT_SUCCESS(status)) {
// Some batteries may not support some types of Information Queries
// Don't fail request, simply leave this one blank.
OutputLen = 0;
}
tempString[0] = (USHORT) OutputLen;
tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
status = BattInfo->Mp.QueryInformation (
BattInfo->Mp.Context,
BattInfo->Tag,
BatteryUniqueID,
0,
&tempString[1],
MAX_BATTERY_STRING_SIZE,
&OutputLen
);
if (!NT_SUCCESS(status)) {
// Some batteries may not support some types of Information Queries
// Don't fail request, simply leave this one blank.
OutputLen = 0;
status = STATUS_SUCCESS;
}
tempString[0] = (USHORT) OutputLen;
tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
size = (ULONG)(sizeof(BATTERY_WMI_STATIC_DATA)+(tempString - ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Strings));
}
break;
default:
status = STATUS_WMI_GUID_NOT_FOUND;
}
*WmiRequest->InstanceLengthArray = size;
status = WmiCompleteRequest(WmiRequest->DeviceObject,
WmiRequest->Irp,
status,
size,
IO_NO_INCREMENT);
}
VOID
BattCMiniportStatus (
IN PBATT_INFO BattInfo,
IN NTSTATUS Status
)
/*++
Routine Description:
Function to return status from miniport. If the battery tag has gone
invalid the pending statuses are aborted.
N.B. must be invoked from the non-rentrant worker thread
Arguments:
BattInfo - Battery
Status - Status from miniport.
Return Value:
None
--*/
{
if (NT_SUCCESS(Status)) {
return ;
}
switch (Status) {
#if DEBUG
case STATUS_SUCCESS:
case STATUS_NOT_IMPLEMENTED:
case STATUS_BUFFER_TOO_SMALL:
case STATUS_INVALID_BUFFER_SIZE:
case STATUS_NOT_SUPPORTED:
case STATUS_INVALID_PARAMETER:
case STATUS_OBJECT_NAME_NOT_FOUND:
case STATUS_INVALID_DEVICE_REQUEST:
// no action
break;
default:
BattPrint (BATT_ERROR, ("BattCMiniportStatus: unknown status from miniport: %x BattInfo %x\n",
Status, BattInfo));
break;
#endif
case STATUS_NO_SUCH_DEVICE:
//
// Our battery tag is wrong. Cancel any queued status irps
//
BattCCompleteIrpQueue (&(BattInfo->StatusQueue), Status);
break;
}
}
VOID
BattCCompleteIrpQueue (
IN PLIST_ENTRY Queue,
IN NTSTATUS Status
)
/*++
Routine Description:
Complete all pending Irps in the IoQueue, TagQueue, or StatusQueue.
N.B. must be invoked from the non-rentrant worker thread
Arguments:
BattInfo - Battery
Status - Error status to complete pending status request with
Return Value:
None
--*/
{
PLIST_ENTRY Entry;
PIRP Irp;
ASSERT (!NT_SUCCESS(Status));
BattPrint (BATT_TRACE, ("BattC: ENTERING BattCCompleteIrpQueue\n"));
while (!IsListEmpty(Queue)) {
Entry = RemoveHeadList (Queue);
Irp = CONTAINING_RECORD (
Entry,
IRP,
Tail.Overlay.ListEntry
);
//
// Use Cancel Spinlock to make sure that Completion routine isn't being called
//
IoAcquireCancelSpinLock (&Irp->CancelIrql);
IoSetCancelRoutine (Irp, NULL);
IoReleaseCancelSpinLock(Irp->CancelIrql);
BattPrint (BATT_NOTE, ("BattC: Completing IRP 0x%0lx at IRQL %d.\n", Irp, KeGetCurrentIrql()));
Irp->IoStatus.Status = Status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
}
BattPrint (BATT_TRACE, ("BattC: EXITING BattCCompleteIrpQueue\n"));
}
VOID
BattCCompleteWmiQueue (
IN PLIST_ENTRY Queue,
IN NTSTATUS Status
)
/*++
Routine Description:
Complete all pending Irps in the IoQueue, TagQueue, or StatusQueue.
N.B. must be invoked from the non-rentrant worker thread
Arguments:
BattInfo - Battery
Status - Error status to complete pending status request with
Return Value:
None
--*/
{
PLIST_ENTRY Entry;
PBATT_WMI_REQUEST WmiRequest;
ASSERT (!NT_SUCCESS(Status));
BattPrint (BATT_TRACE, ("BattC: ENTERING BattCCompleteWmiQueue\n"));
while (!IsListEmpty(Queue)) {
Entry = RemoveHeadList (Queue);
WmiRequest = CONTAINING_RECORD (
Entry,
BATT_WMI_REQUEST,
ListEntry
);
BattPrint (BATT_NOTE, ("BattC: Completing Wmi Request 0x%0lx at IRQL %d.\n", WmiRequest, KeGetCurrentIrql()));
*WmiRequest->InstanceLengthArray = 0;
WmiCompleteRequest(WmiRequest->DeviceObject,
WmiRequest->Irp,
Status,
0,
IO_NO_INCREMENT);
}
BattPrint (BATT_TRACE, ("BattC: EXITING BattCCompleteWmiQueue\n"));
}