969 lines
28 KiB
C
969 lines
28 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1990 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
pbatt.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module interfaces the policy manager to the composite device
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Ken Reneris (kenr) 17-Jan-1997
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
#include "pop.h"
|
||
|
|
||
|
|
||
|
//
|
||
|
// Internal prototypes
|
||
|
//
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PopRecalculateCBTriggerLevels (
|
||
|
ULONG Flags
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
PopComputeCBTime (
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text(PAGE, PopRecalculateCBTriggerLevels)
|
||
|
#pragma alloc_text(PAGE, PopCompositeBatteryDeviceHandler)
|
||
|
#pragma alloc_text(PAGE, PopComputeCBTime)
|
||
|
#pragma alloc_text(PAGE, PopResetCBTriggers)
|
||
|
#pragma alloc_text(PAGE, PopCurrentPowerState)
|
||
|
#endif
|
||
|
|
||
|
VOID
|
||
|
PopCompositeBatteryUpdateThrottleLimit(
|
||
|
IN ULONG CurrentCapacity
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine is called to update the ThrottleLimit in each of the
|
||
|
processor's PRCB to reflect the maximum given the change in current
|
||
|
capacity.
|
||
|
|
||
|
This function was broken out because it cannot be paged code
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CurrentCapacity - PercentageCapacity remaining...
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KAFFINITY currentAffinity;
|
||
|
KAFFINITY processors;
|
||
|
PPROCESSOR_PERF_STATE perfStates;
|
||
|
PPROCESSOR_POWER_STATE pState;
|
||
|
ULONG perfStatesCount;
|
||
|
ULONG i;
|
||
|
KIRQL oldIrql;
|
||
|
#if DBG
|
||
|
ULONGLONG currentTime;
|
||
|
UCHAR t[40];
|
||
|
|
||
|
currentTime = KeQueryInterruptTime();
|
||
|
PopTimeString(t, currentTime);
|
||
|
#endif
|
||
|
|
||
|
currentAffinity = 1;
|
||
|
processors = KeActiveProcessors;
|
||
|
while (processors) {
|
||
|
|
||
|
if (!(processors & currentAffinity)) {
|
||
|
|
||
|
currentAffinity <<= 1;
|
||
|
continue;
|
||
|
|
||
|
}
|
||
|
|
||
|
KeSetSystemAffinityThread( currentAffinity );
|
||
|
processors &= ~currentAffinity;
|
||
|
currentAffinity <<= 1;
|
||
|
|
||
|
//
|
||
|
// We need to be running at DISPATCH_LEVEL to access the
|
||
|
// structures referenced within the pState...
|
||
|
//
|
||
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
|
pState = &(KeGetCurrentPrcb()->PowerState);
|
||
|
|
||
|
//
|
||
|
// Does this processor support throttling?
|
||
|
//
|
||
|
if ((pState->Flags & PSTATE_SUPPORTS_THROTTLE) == 0) {
|
||
|
|
||
|
//
|
||
|
// No, then we don't care about it...
|
||
|
//
|
||
|
KeLowerIrql( oldIrql );
|
||
|
continue;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Look at the power structure and get the array of
|
||
|
// perf states supported. Note that we change the
|
||
|
// perfStatesCount by subtracting one so that we don't
|
||
|
// have to worry about overrruning the array during
|
||
|
// the for loop
|
||
|
//
|
||
|
pState = &(KeGetCurrentPrcb()->PowerState);
|
||
|
perfStates = pState->PerfStates;
|
||
|
perfStatesCount = (pState->PerfStatesCount - 1);
|
||
|
|
||
|
//
|
||
|
// See which throttle point is best for this power
|
||
|
// capacity. Note that we have pre-calculated which
|
||
|
// capacity matches which state, so its only a matter
|
||
|
// of walking the array...
|
||
|
//
|
||
|
for (i = pState->KneeThrottleIndex; i < perfStatesCount; i++) {
|
||
|
|
||
|
if (perfStates[i].MinCapacity <= CurrentCapacity) {
|
||
|
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the throttle limit index
|
||
|
//
|
||
|
if (pState->ThrottleLimitIndex != i) {
|
||
|
|
||
|
pState->ThrottleLimitIndex = (UCHAR) i;
|
||
|
#if DBG
|
||
|
PoPrint(
|
||
|
PO_THROTTLE,
|
||
|
("PopApplyThermalThrottle - %s - New Limit (%d) Index (%d)\n",
|
||
|
t,perfStates[i].PercentFrequency,i)
|
||
|
);
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Force a throttle update
|
||
|
//
|
||
|
PopUpdateProcessorThrottle();
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Revert back to our previous IRQL
|
||
|
//
|
||
|
KeLowerIrql( oldIrql );
|
||
|
|
||
|
} // while
|
||
|
|
||
|
//
|
||
|
// Revert to the affinity of the original thread
|
||
|
//
|
||
|
KeRevertToUserAffinityThread();
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PopCompositeBatteryDeviceHandler (
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp,
|
||
|
IN PVOID Context
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is the irp handler function to handle the completion
|
||
|
if the composite battery irp. When there is a composite battery
|
||
|
present one IRP is always outstanding to the device. On completion
|
||
|
this IRP is recycled to the next request.
|
||
|
|
||
|
N.B. PopPolicyLock must be held.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceObject - DeviceObject of the battery device
|
||
|
|
||
|
Irp - Irp which has completed
|
||
|
|
||
|
Context - n/a
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PIO_STACK_LOCATION IrpSp, IrpPrevSp;
|
||
|
PVOID InputBuffer, OutputBuffer;
|
||
|
ULONG InputBufferLength, OutputBufferLength;
|
||
|
ULONG IoctlCode;
|
||
|
PSYSTEM_POWER_POLICY Policy;
|
||
|
PPROCESSOR_POWER_POLICY ProcessorPolicy;
|
||
|
ULONG i, j;
|
||
|
NTSTATUS Status;
|
||
|
ULONG currentCapacity;
|
||
|
#if DBG
|
||
|
ULONGLONG currentTime;
|
||
|
UCHAR t[40];
|
||
|
|
||
|
currentTime = KeQueryInterruptTime();
|
||
|
PopTimeString(t, currentTime);
|
||
|
#endif
|
||
|
|
||
|
ASSERT_POLICY_LOCK_OWNED();
|
||
|
ASSERT (Irp == PopCB.StatusIrp);
|
||
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
|
|
||
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
||
|
|
||
|
//
|
||
|
// Handle the completed request
|
||
|
//
|
||
|
|
||
|
switch (PopCB.State) {
|
||
|
case PO_CB_READ_TAG:
|
||
|
case PO_CB_WAIT_TAG:
|
||
|
// tag is now valid, read info
|
||
|
PoPrint(PO_BATT, ("PopCB: New battery tag\n"));
|
||
|
|
||
|
//
|
||
|
// Reset the triggers.
|
||
|
//
|
||
|
// We reset PO_TRG_SET, so the level will be recalculated, but on
|
||
|
// battery tag change we don't want to make the actions happen again.
|
||
|
// If the trigger has not yet been set off, it will still be armed.
|
||
|
//
|
||
|
|
||
|
PopResetCBTriggers (PO_TRG_SET);
|
||
|
PopCB.State = PO_CB_READ_INFO;
|
||
|
PopCB.Tag = PopCB.u.Tag;
|
||
|
|
||
|
// Ensure that the power state passed in is bad, so QUERY_STATUS
|
||
|
// will return immediately.
|
||
|
PopCB.Status.PowerState = (ULONG) -1;
|
||
|
break;
|
||
|
|
||
|
case PO_CB_READ_INFO:
|
||
|
// info is read directly into buffer
|
||
|
PoPrint(PO_BATT, ("PopCB: info read\n"));
|
||
|
PopCB.State = PO_CB_READ_STATUS;
|
||
|
RtlCopyMemory (&PopCB.Info, &PopCB.u.Info, sizeof(PopCB.Info));
|
||
|
break;
|
||
|
|
||
|
case PO_CB_READ_STATUS:
|
||
|
//
|
||
|
// Status has been read, check it
|
||
|
//
|
||
|
|
||
|
PoPrint(PO_BATT, ("PopCB: Status PwrState %x, Cap %x, Volt %x, Cur %x\n",
|
||
|
PopCB.u.Status.PowerState,
|
||
|
PopCB.u.Status.Capacity,
|
||
|
PopCB.u.Status.Voltage,
|
||
|
PopCB.u.Status.Current
|
||
|
));
|
||
|
|
||
|
PopCB.StatusTime = KeQueryInterruptTime();
|
||
|
|
||
|
//
|
||
|
// Check if the current policy should be ac or dc
|
||
|
//
|
||
|
|
||
|
if (PopCB.u.Status.PowerState & BATTERY_POWER_ON_LINE) {
|
||
|
ProcessorPolicy = &PopAcProcessorPolicy;
|
||
|
Policy = &PopAcPolicy;
|
||
|
} else {
|
||
|
ProcessorPolicy = &PopDcProcessorPolicy;
|
||
|
Policy = &PopDcPolicy;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Did the policy change?
|
||
|
//
|
||
|
|
||
|
if (PopPolicy != Policy || PopProcessorPolicy != ProcessorPolicy) {
|
||
|
|
||
|
//
|
||
|
// Change the active policy and reset the battery triggers
|
||
|
//
|
||
|
PopProcessorPolicy = ProcessorPolicy;
|
||
|
PopPolicy = Policy;
|
||
|
|
||
|
//
|
||
|
// Reset triggers.
|
||
|
//
|
||
|
// In this case we re-arm both the user and system triggers.
|
||
|
// The system trigger will be disarmed when we recalculate
|
||
|
// trigger levels if the capacity was already below that level.
|
||
|
//
|
||
|
PopResetCBTriggers (PO_TRG_SET | PO_TRG_USER | PO_TRG_SYSTEM);
|
||
|
PopSetNotificationWork (
|
||
|
PO_NOTIFY_ACDC_CALLBACK |
|
||
|
PO_NOTIFY_POLICY |
|
||
|
PO_NOTIFY_PROCESSOR_POLICY
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Recompute thermal throttle and cooling mode
|
||
|
//
|
||
|
// Note that PopApplyThermalThrottle will take care of any dynamic
|
||
|
// throttling that might need to happen due to the AC/DC transition.
|
||
|
//
|
||
|
PopApplyThermalThrottle ();
|
||
|
PopIdleUpdateIdleHandlers();
|
||
|
|
||
|
//
|
||
|
// Recompute system idle values
|
||
|
//
|
||
|
PopInitSIdle ();
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Did battery cross resolution setting?
|
||
|
// Correction... Has it changed at all. If so, all apps should be updated,
|
||
|
// even if it hasn't crossed a resolution setting. Otherwise, if one app
|
||
|
// queries the current status, it could be displaying a different value than
|
||
|
// the battery meter.
|
||
|
//
|
||
|
|
||
|
if ((PopCB.u.Status.Capacity != PopCB.Status.Capacity) ||
|
||
|
PopCB.Status.PowerState != PopCB.u.Status.PowerState) {
|
||
|
PopSetNotificationWork (PO_NOTIFY_BATTERY_STATUS);
|
||
|
PopCB.State = PO_CB_READ_EST_TIME;
|
||
|
}
|
||
|
|
||
|
PopRecalculateCBTriggerLevels (PO_TRG_SYSTEM);
|
||
|
|
||
|
//
|
||
|
// Update current battery status
|
||
|
//
|
||
|
|
||
|
memcpy (&PopCB.Status, &PopCB.u.Status, sizeof (PopCB.Status));
|
||
|
|
||
|
//
|
||
|
// Check for discharging and if any discharge policies have tripped
|
||
|
//
|
||
|
|
||
|
if (Policy == &PopDcPolicy) {
|
||
|
for (i=0; i < PO_NUM_POWER_LEVELS; i++) {
|
||
|
if (PopCB.Status.Capacity <= PopCB.Trigger[i].Battery.Level) {
|
||
|
|
||
|
//
|
||
|
// Fire this power action
|
||
|
//
|
||
|
PopSetPowerAction(
|
||
|
&PopCB.Trigger[i],
|
||
|
PO_NOTIFY_BATTERY_STATUS,
|
||
|
&Policy->DischargePolicy[i].PowerPolicy,
|
||
|
Policy->DischargePolicy[i].MinSystemState,
|
||
|
SubstituteLightestOverallDownwardBounded
|
||
|
);
|
||
|
|
||
|
PopCB.State = PO_CB_READ_EST_TIME;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Clear the trigger for this event
|
||
|
//
|
||
|
|
||
|
PopCB.Trigger[i].Flags &= ~(PO_TRG_USER|PO_TRG_SYSTEM);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Figure out what our current capacity is...
|
||
|
//
|
||
|
if (PopCB.Info.FullChargedCapacity) {
|
||
|
|
||
|
currentCapacity = PopCB.Status.Capacity * 100 /
|
||
|
PopCB.Info.FullChargedCapacity;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Assume that the battery is fully charged...
|
||
|
// This will cause us to reset the throttle limiter
|
||
|
//
|
||
|
currentCapacity = 100;
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Assume that the battery is fully charged...
|
||
|
// This will cause us to reset the throttle limiter
|
||
|
//
|
||
|
currentCapacity = 100;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This is kind of silly code to put in here, but since
|
||
|
// want to minize our synchronization elsewhere, we have
|
||
|
// to examine every processor's powerstate and update
|
||
|
// the throttlelimitindex on each. This may be actually
|
||
|
// a smarth thing to do if not all processors support
|
||
|
// the same set of states
|
||
|
//
|
||
|
PopCompositeBatteryUpdateThrottleLimit( currentCapacity );
|
||
|
|
||
|
//
|
||
|
// If there's a thread waiting or if we notified user (since
|
||
|
// the response to the notify will be to read the power status) for
|
||
|
// power state, read the est time now else read new status
|
||
|
//
|
||
|
|
||
|
if (PopCB.ThreadWaiting) {
|
||
|
PopCB.State = PO_CB_READ_EST_TIME;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case PO_CB_READ_EST_TIME:
|
||
|
//
|
||
|
// Estimated time is read after sucessful status
|
||
|
// read and (currently) only when there's a thread
|
||
|
// waiting for the system power state
|
||
|
//
|
||
|
|
||
|
PoPrint(PO_BATT, ("PopCB: EstTime read\n"));
|
||
|
PopCB.EstTime = PopCB.u.EstTime;
|
||
|
|
||
|
PopCB.EstTimeTime = KeQueryInterruptTime();
|
||
|
PopComputeCBTime();
|
||
|
|
||
|
//
|
||
|
// Signal waiting threads
|
||
|
//
|
||
|
|
||
|
PopCB.ThreadWaiting = FALSE;
|
||
|
KeSetEvent (&PopCB.Event, 0, FALSE);
|
||
|
|
||
|
//
|
||
|
// Go back are read status
|
||
|
//
|
||
|
|
||
|
PopCB.State = PO_CB_READ_STATUS;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
PopInternalAddToDumpFile( Irp, sizeof(IRP), DeviceObject, NULL, NULL, NULL );
|
||
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
||
|
0x300,
|
||
|
POP_BATT,
|
||
|
(ULONG_PTR)DeviceObject,
|
||
|
(ULONG_PTR)Irp );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// some sort of error, if the request was canceld re-issue
|
||
|
// it else backup to reinitialize
|
||
|
//
|
||
|
|
||
|
if (Irp->IoStatus.Status != STATUS_CANCELLED) {
|
||
|
|
||
|
//
|
||
|
// This occurs under two circumstances. It is either the first time
|
||
|
// through, or a battery was removed so the Irp failed dur to tag change.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// If this is already a read-tag request then there's no battery present
|
||
|
//
|
||
|
|
||
|
PopCB.State = PopCB.State == PO_CB_READ_TAG ? PO_CB_WAIT_TAG : PO_CB_READ_TAG;
|
||
|
PoPrint(PO_BATT, ("PopCB: error %x - new state %d\n",
|
||
|
Irp->IoStatus.Status,
|
||
|
PopCB.State
|
||
|
));
|
||
|
} else {
|
||
|
PoPrint(PO_BATT, ("PopCB: irp cancelled\n"));
|
||
|
PopRecalculateCBTriggerLevels (PO_TRG_SYSTEM | PO_TRG_USER);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If new state is none, then there's no battery
|
||
|
//
|
||
|
|
||
|
if (PopCB.State != PO_CB_NONE) {
|
||
|
|
||
|
//
|
||
|
// Issue new request based on current state
|
||
|
//
|
||
|
|
||
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
||
|
IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
||
|
IoctlCode = IOCTL_BATTERY_QUERY_INFORMATION;
|
||
|
PopCB.u.QueryInfo.BatteryTag = PopCB.Tag;
|
||
|
InputBuffer = &PopCB.u.QueryInfo;
|
||
|
InputBufferLength = sizeof(PopCB.u.QueryInfo);
|
||
|
|
||
|
switch (PopCB.State) {
|
||
|
case PO_CB_READ_TAG:
|
||
|
PoPrint(PO_BATT, ("PopCB: query tag\n"));
|
||
|
IoctlCode = IOCTL_BATTERY_QUERY_TAG;
|
||
|
PopCB.u.Tag = (ULONG) 0;
|
||
|
InputBufferLength = sizeof(ULONG);
|
||
|
OutputBufferLength = sizeof(PopCB.Tag);
|
||
|
break;
|
||
|
|
||
|
case PO_CB_WAIT_TAG:
|
||
|
PoPrint(PO_BATT, ("PopCB: query tag\n"));
|
||
|
|
||
|
//
|
||
|
// Battery is gone. Wait for it to appear
|
||
|
//
|
||
|
|
||
|
IoctlCode = IOCTL_BATTERY_QUERY_TAG;
|
||
|
PopCB.u.Tag = (ULONG) -1;
|
||
|
InputBufferLength = sizeof(ULONG);
|
||
|
OutputBufferLength = sizeof(PopCB.Tag);
|
||
|
|
||
|
//
|
||
|
// Notify battery status change, and wake any threads
|
||
|
//
|
||
|
|
||
|
PopSetNotificationWork (PO_NOTIFY_BATTERY_STATUS);
|
||
|
|
||
|
if (PopCB.ThreadWaiting) {
|
||
|
PopCB.ThreadWaiting = FALSE;
|
||
|
KeSetEvent (&PopCB.Event, 0, FALSE);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case PO_CB_READ_INFO:
|
||
|
PoPrint(PO_BATT, ("PopCB: query info\n"));
|
||
|
PopCB.u.QueryInfo.InformationLevel = BatteryInformation;
|
||
|
OutputBufferLength = sizeof(PopCB.Info);
|
||
|
break;
|
||
|
|
||
|
case PO_CB_READ_STATUS:
|
||
|
//
|
||
|
// Calculate next wait
|
||
|
//
|
||
|
|
||
|
PopCB.u.Wait.BatteryTag = PopCB.Tag;
|
||
|
PopCB.u.Wait.PowerState = PopCB.Status.PowerState;
|
||
|
PopCB.u.Wait.Timeout = (ULONG) -1;
|
||
|
if (PopCB.ThreadWaiting) {
|
||
|
PopCB.u.Wait.Timeout = 0;
|
||
|
}
|
||
|
|
||
|
i = (PopCB.Info.FullChargedCapacity *
|
||
|
PopPolicy->BroadcastCapacityResolution) / 100;
|
||
|
if (!i) {
|
||
|
i = 1;
|
||
|
}
|
||
|
|
||
|
if (PopCB.Status.Capacity > i) {
|
||
|
PopCB.u.Wait.LowCapacity = PopCB.Status.Capacity - i;
|
||
|
} else {
|
||
|
PopCB.u.Wait.LowCapacity = 0;
|
||
|
}
|
||
|
|
||
|
PopCB.u.Wait.HighCapacity = PopCB.Status.Capacity + i;
|
||
|
if (PopCB.u.Wait.HighCapacity < i) {
|
||
|
// avoid rare case of overflow
|
||
|
PopCB.u.Wait.HighCapacity = (ULONG) -1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check limits against power policies
|
||
|
//
|
||
|
|
||
|
for (i=0; i < PO_NUM_POWER_LEVELS; i++) {
|
||
|
if (PopCB.Trigger[i].Flags & PO_TRG_SET) {
|
||
|
|
||
|
if (PopCB.Trigger[i].Battery.Level < PopCB.Status.Capacity &&
|
||
|
PopCB.Trigger[i].Battery.Level > PopCB.u.Wait.LowCapacity) {
|
||
|
|
||
|
PopCB.u.Wait.LowCapacity = PopCB.Trigger[i].Battery.Level;
|
||
|
}
|
||
|
|
||
|
if (PopCB.Trigger[i].Battery.Level > PopCB.Status.Capacity &&
|
||
|
PopCB.Trigger[i].Battery.Level < PopCB.u.Wait.HighCapacity) {
|
||
|
|
||
|
PopCB.u.Wait.HighCapacity = PopCB.Trigger[i].Battery.Level;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IoctlCode = IOCTL_BATTERY_QUERY_STATUS;
|
||
|
InputBuffer = &PopCB.u.Wait;
|
||
|
InputBufferLength = sizeof(PopCB.u.Wait);
|
||
|
OutputBufferLength = sizeof(PopCB.Status);
|
||
|
PoPrint(PO_BATT, ("PopCB: timeout %x, pwrstate %x, low %x - high %x\n",
|
||
|
PopCB.u.Wait.Timeout,
|
||
|
PopCB.u.Wait.PowerState,
|
||
|
PopCB.u.Wait.LowCapacity,
|
||
|
PopCB.u.Wait.HighCapacity
|
||
|
));
|
||
|
|
||
|
break;
|
||
|
|
||
|
case PO_CB_READ_EST_TIME:
|
||
|
PoPrint(PO_BATT, ("PopCB: query est time\n"));
|
||
|
PopCB.u.QueryInfo.InformationLevel = BatteryEstimatedTime;
|
||
|
PopCB.u.QueryInfo.AtRate = 0;
|
||
|
OutputBufferLength = sizeof(PopCB.EstTime);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
PopInternalAddToDumpFile( IrpSp, sizeof(IO_STACK_LOCATION), DeviceObject, NULL, NULL, NULL );
|
||
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
||
|
0x301,
|
||
|
POP_BATT,
|
||
|
(ULONG_PTR)DeviceObject,
|
||
|
(ULONG_PTR)IrpSp );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Submit IRP
|
||
|
//
|
||
|
|
||
|
IrpSp->Parameters.DeviceIoControl.IoControlCode = IoctlCode;
|
||
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
|
||
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
|
||
|
Irp->AssociatedIrp.SystemBuffer = &PopCB.u;
|
||
|
Irp->UserBuffer = &PopCB.u;
|
||
|
Irp->PendingReturned = FALSE;
|
||
|
Irp->Cancel = FALSE;
|
||
|
IoSetCompletionRoutine (Irp, PopCompletePolicyIrp, NULL, TRUE, TRUE, TRUE);
|
||
|
IoCallDriver (DeviceObject, Irp);
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// Battery has disappeared (state is PO_CB_NONE)
|
||
|
//
|
||
|
|
||
|
PoPrint(PO_BATT, ("PopCB: Battery removed\n"));
|
||
|
PopSetNotificationWork (PO_NOTIFY_BATTERY_STATUS);
|
||
|
|
||
|
//
|
||
|
// Set policy to AC
|
||
|
//
|
||
|
|
||
|
if (PopPolicy != &PopAcPolicy) {
|
||
|
PopPolicy = &PopAcPolicy;
|
||
|
PopProcessorPolicy = &PopAcProcessorPolicy;
|
||
|
PopSetNotificationWork(
|
||
|
PO_NOTIFY_ACDC_CALLBACK |
|
||
|
PO_NOTIFY_POLICY |
|
||
|
PO_NOTIFY_PROCESSOR_POLICY
|
||
|
);
|
||
|
PopApplyThermalThrottle();
|
||
|
PopIdleUpdateIdleHandlers();
|
||
|
PopInitSIdle ();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Wake any threads
|
||
|
//
|
||
|
|
||
|
if (PopCB.ThreadWaiting) {
|
||
|
PopCB.ThreadWaiting = FALSE;
|
||
|
KeSetEvent (&PopCB.Event, 0, FALSE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Cleanup
|
||
|
//
|
||
|
|
||
|
IoFreeIrp (Irp);
|
||
|
PopCB.StatusIrp = NULL;
|
||
|
ObDereferenceObject (DeviceObject);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PopRecalculateCBTriggerLevels (
|
||
|
ULONG Flags
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is invoked to set the trigger battery levels based on the power
|
||
|
policy. This will be invoked whenever the power policy is changed, or whenever
|
||
|
there is a battery status change that could affect these settings.
|
||
|
|
||
|
N.B. PopPolicyLock must be held.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Flags- The flags to set if the level has already been passed:
|
||
|
example: When user changes alarm leve, we don't want clear
|
||
|
PO_TRG_USER|PO_TRG_SYSTEM. If the recalculation was caused by a change
|
||
|
(startup, or AC unplug), we just want to set PO_TRG_SYSTEM because we
|
||
|
still want the user notification.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PSYSTEM_POWER_LEVEL DPolicy;
|
||
|
ULONG i;
|
||
|
|
||
|
//
|
||
|
// Calculate any level settings
|
||
|
//
|
||
|
|
||
|
for (i=0; i < PO_NUM_POWER_LEVELS; i++) {
|
||
|
DPolicy = &PopPolicy->DischargePolicy[i];
|
||
|
|
||
|
//
|
||
|
// If this setting not calculated handle it
|
||
|
//
|
||
|
|
||
|
if (!(PopCB.Trigger[i].Flags & PO_TRG_SET) && DPolicy->Enable) {
|
||
|
|
||
|
//
|
||
|
// Compute battery capacity setting for percentage
|
||
|
//
|
||
|
|
||
|
PopCB.Trigger[i].Flags |= PO_TRG_SET;
|
||
|
PopCB.Trigger[i].Battery.Level =
|
||
|
PopCB.Info.FullChargedCapacity * DPolicy->BatteryLevel / 100 +
|
||
|
PopCB.Info.FullChargedCapacity / 200;
|
||
|
|
||
|
//
|
||
|
// Make sure setting is not below the lowest default
|
||
|
//
|
||
|
|
||
|
if (PopCB.Trigger[i].Battery.Level < PopCB.Info.DefaultAlert1) {
|
||
|
PopCB.Trigger[i].Battery.Level = PopCB.Info.DefaultAlert1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Skip system action if battery capacity was already below level.
|
||
|
// This will occur on startup, when a battery is changed,
|
||
|
// and when AC comes or goes.
|
||
|
//
|
||
|
|
||
|
if (PopCB.Status.Capacity < PopCB.Trigger[i].Battery.Level) {
|
||
|
PopCB.Trigger[i].Flags |= Flags;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PopComputeCBTime (
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is invoked after the battery status & estimated time
|
||
|
have been read from the battery. This function can apply heuristics
|
||
|
or other knowedge to improve the extimated time.
|
||
|
|
||
|
N.B. PopPolicyLock must be held.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
// for now just use the batteries value
|
||
|
PopCB.AdjustedEstTime = PopCB.EstTime;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PopResetCBTriggers (
|
||
|
IN UCHAR Flags
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function clears the requested bits from the batteries trigger flags.
|
||
|
|
||
|
N.B. PopPolicyLock must be held.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Flags - Bits to clear
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Status
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG i;
|
||
|
|
||
|
ASSERT_POLICY_LOCK_OWNED();
|
||
|
|
||
|
//
|
||
|
// Clear flag bits
|
||
|
//
|
||
|
|
||
|
Flags = ~Flags;
|
||
|
for (i=0; i < PO_NUM_POWER_LEVELS; i++) {
|
||
|
PopCB.Trigger[i].Flags &= Flags;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reread battery status
|
||
|
//
|
||
|
|
||
|
if (PopCB.StatusIrp) {
|
||
|
IoCancelIrp (PopCB.StatusIrp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
PopCurrentPowerState (
|
||
|
OUT PSYSTEM_BATTERY_STATE PowerState
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function returns the current system power state. If needed,
|
||
|
this function will cause the composite battery irp to get the
|
||
|
current battery status.
|
||
|
|
||
|
N.B. PopPolicyLock must be held.
|
||
|
N.B. The function may drop the PopPolicyLock
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
PowerState - The current power state
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Status
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONGLONG CurrentTime;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
|
||
|
ASSERT_POLICY_LOCK_OWNED();
|
||
|
|
||
|
Status = STATUS_SUCCESS;
|
||
|
RtlZeroMemory (PowerState, sizeof(SYSTEM_BATTERY_STATE));
|
||
|
|
||
|
//
|
||
|
// Wait for valid state in PopCB
|
||
|
//
|
||
|
|
||
|
do {
|
||
|
|
||
|
//
|
||
|
// If there's not a composite battery, then return
|
||
|
//
|
||
|
|
||
|
if (PopCB.State == PO_CB_NONE || PopCB.State == PO_CB_WAIT_TAG) {
|
||
|
PowerState->AcOnLine = PopPolicy == &PopAcPolicy;
|
||
|
|
||
|
// Indicate no battery found...
|
||
|
PERFINFO_POWER_BATTERY_LIFE_INFO(-1, 0);
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If device state not being read, we need to wait
|
||
|
//
|
||
|
|
||
|
if (PopCB.State == PO_CB_READ_STATUS) {
|
||
|
//
|
||
|
// If last EstTime was calculated within PO_MAX_CB_CACHE_TIME,
|
||
|
// use the current data. (note this implies status was sucessfully
|
||
|
// read just before time was calcualted)
|
||
|
//
|
||
|
|
||
|
CurrentTime = KeQueryInterruptTime();
|
||
|
if (CurrentTime - PopCB.EstTimeTime < PO_MAX_CB_CACHE_TIME) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Need new status. If no other threads are waiting for
|
||
|
// system power state, then setup for wait
|
||
|
//
|
||
|
|
||
|
if (!PopCB.ThreadWaiting) {
|
||
|
PopCB.ThreadWaiting = TRUE;
|
||
|
KeResetEvent (&PopCB.Event);
|
||
|
|
||
|
//
|
||
|
// If read status is in progress, cancel it so we
|
||
|
// can read status now
|
||
|
//
|
||
|
|
||
|
if (PopCB.State == PO_CB_READ_STATUS) {
|
||
|
IoCancelIrp (PopCB.StatusIrp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Wait for status update
|
||
|
//
|
||
|
|
||
|
PopReleasePolicyLock (FALSE);
|
||
|
Status = KeWaitForSingleObject (&PopCB.Event, Executive, KernelMode, TRUE, NULL);
|
||
|
PopAcquirePolicyLock ();
|
||
|
} while (NT_SUCCESS(Status));
|
||
|
|
||
|
//
|
||
|
// Generate power state
|
||
|
//
|
||
|
|
||
|
PowerState->AcOnLine = (PopCB.Status.PowerState & BATTERY_POWER_ON_LINE) ? TRUE : FALSE;
|
||
|
PowerState->BatteryPresent = TRUE;
|
||
|
PowerState->Charging = (PopCB.Status.PowerState & BATTERY_CHARGING) ? TRUE : FALSE;
|
||
|
PowerState->Discharging = (PopCB.Status.PowerState & BATTERY_DISCHARGING) ? TRUE : FALSE;
|
||
|
PowerState->MaxCapacity = PopCB.Info.FullChargedCapacity;
|
||
|
PowerState->RemainingCapacity = PopCB.Status.Capacity;
|
||
|
PowerState->Rate = PopCB.Status.Current;
|
||
|
PowerState->EstimatedTime = PopCB.AdjustedEstTime;
|
||
|
PowerState->DefaultAlert1 = PopCB.Info.DefaultAlert1;
|
||
|
PowerState->DefaultAlert2 = PopCB.Info.DefaultAlert2;
|
||
|
|
||
|
PERFINFO_POWER_BATTERY_LIFE_INFO(PowerState->RemainingCapacity, PowerState->Rate);
|
||
|
|
||
|
return Status;
|
||
|
}
|