windows-nt/Source/XPSP1/NT/base/ntos/po/misc.c
2020-09-26 16:20:57 +08:00

1167 lines
28 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
misc.c
Abstract:
This module implements miscellaneous power management functions
Author:
Ken Reneris (kenr) 19-July-1994
Revision History:
--*/
#include "pop.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,PoInitializeDeviceObject)
#pragma alloc_text(PAGELK,PoRunDownDeviceObject)
#pragma alloc_text(PAGE,PopCleanupPowerState)
#pragma alloc_text(PAGE,PopChangeCapability)
#pragma alloc_text(PAGE,PopExceptionFilter)
#pragma alloc_text(PAGELK,PopSystemStateString)
#pragma alloc_text(PAGE,PopOpenPowerKey)
#pragma alloc_text(PAGE,PopInitializePowerPolicySimulate)
#pragma alloc_text(PAGE,PopSaveHeuristics)
#pragma alloc_text(PAGE,PoInvalidateDevicePowerRelations)
#pragma alloc_text(PAGE,PoGetLightestSystemStateForEject)
#pragma alloc_text(PAGE,PoGetDevicePowerState)
#pragma alloc_text(PAGE, PopUnlockAfterSleepWorker)
#if DBG
#pragma alloc_text(PAGE, PopPowerActionString)
#pragma alloc_text(PAGE, PopAssertPolicyLockOwned)
#endif
#endif
//
// TCP/IP checksum that we use if it is available
//
ULONG
tcpxsum(
IN ULONG cksum,
IN PUCHAR buf,
IN ULONG_PTR len
);
VOID
PoInitializeDeviceObject (
IN PDEVOBJ_EXTENSION DeviceObjectExtension
)
{
//
// default to unspecified power states, not Inrush, Pageable.
//
DeviceObjectExtension->PowerFlags = 0L;
PopSetDoSystemPowerState(DeviceObjectExtension, PowerSystemUnspecified);
PopSetDoDevicePowerState(DeviceObjectExtension, PowerDeviceUnspecified);
DeviceObjectExtension->Dope = NULL;
}
VOID
PoRunDownDeviceObject (
IN PDEVICE_OBJECT DeviceObject
)
{
KIRQL OldIrql;
PDEVOBJ_EXTENSION doe;
PDEVICE_OBJECT_POWER_EXTENSION pdope;
ULONG D0Count;
doe = (PDEVOBJ_EXTENSION) DeviceObject->DeviceObjectExtension;
//
// force off any idle counter that may be active
//
PoRegisterDeviceForIdleDetection(
DeviceObject, 0, 0, PowerDeviceUnspecified
);
if (PopFindIrpByDeviceObject(DeviceObject, DevicePowerState) ||
PopFindIrpByDeviceObject(DeviceObject, SystemPowerState))
{
PopInternalAddToDumpFile( NULL, 0, DeviceObject, NULL, NULL, NULL );
KeBugCheckEx(
DRIVER_POWER_STATE_FAILURE,
DEVICE_DELETED_WITH_POWER_IRPS,
0x100,
(ULONG_PTR) DeviceObject,
0 );
}
//
// knock down notify structures attached to this DO.
//
PopRunDownSourceTargetList(DeviceObject);
//
// knock down the dope
//
pdope = doe->Dope;
if (pdope) {
ASSERT(ExPageLockHandle);
MmLockPagableSectionByHandle(ExPageLockHandle);
PopAcquireVolumeLock ();
PopLockDopeGlobal(&OldIrql);
if (pdope->Volume.Flink) {
RemoveEntryList (&pdope->Volume);
doe->Dope->Volume.Flink = NULL;
doe->Dope->Volume.Blink = NULL;
}
doe->Dope = NULL;
ExFreePool(pdope);
PopUnlockDopeGlobal(OldIrql);
PopReleaseVolumeLock ();
MmUnlockPagableImageSection (ExPageLockHandle);
}
}
VOID
PopCleanupPowerState (
IN OUT PUCHAR PowerState
)
/*++
Routine Description:
Used to cleanup the Thread->Tcb.PowerState or the Process->Pcb.PowerState
during thread or process rundown
Arguments:
PowerState - Which power state to cleanup
Return Value:
None
--*/
{
ULONG OldFlags;
//
// If power state is set, clean it up
//
if (*PowerState) {
PopAcquirePolicyLock ();
//
// Get current settings and clear them
//
OldFlags = *PowerState | ES_CONTINUOUS;
*PowerState = 0;
//
// Account for attribute settings which are being cleared
//
PopApplyAttributeState (ES_CONTINUOUS, OldFlags);
//
// Done
//
PopReleasePolicyLock (TRUE);
}
}
VOID
PoNotifySystemTimeSet (
VOID
)
/*++
Routine Description:
Called by KE after a new system time has been set. Enqueues
a notification to the proper system components that the time
has been changed.
Arguments:
None
Return Value:
None
--*/
{
KIRQL OldIrql;
if (PopEventCallout) {
KeRaiseIrql (DISPATCH_LEVEL, &OldIrql);
ExNotifyCallback (ExCbSetSystemTime, NULL, NULL);
PopGetPolicyWorker (PO_WORKER_TIME_CHANGE);
PopCheckForWork (TRUE);
KeLowerIrql (OldIrql);
}
}
VOID
PopChangeCapability (
IN PBOOLEAN PresentFlag,
IN BOOLEAN IsPresent
)
{
//
// If feature wasn't present before, it is now. Re-compute policies
// as system capabilities changed
//
if (*PresentFlag != IsPresent) {
*PresentFlag = IsPresent;
PopResetCurrentPolicies ();
PopSetNotificationWork (PO_NOTIFY_CAPABILITIES);
}
}
#if DBG
VOID
PopAssertPolicyLockOwned(
VOID
)
{
PAGED_CODE();
ASSERT (PopPolicyLockThread == KeGetCurrentThread());
}
#endif // DBG
VOID
FASTCALL
PopInternalAddToDumpFile (
IN OPTIONAL PVOID DataBlock,
IN OPTIONAL ULONG DataBlockSize,
IN OPTIONAL PDEVICE_OBJECT DeviceObject,
IN OPTIONAL PDRIVER_OBJECT DriverObject,
IN OPTIONAL PDEVOBJ_EXTENSION Doe,
IN OPTIONAL PDEVICE_OBJECT_POWER_EXTENSION Dope
)
/*++
Routine Description:
Called just before bugchecking. This function will ensure that
things we care about get into the dump file for later debugging.
It should be noted that many of the parameters can be derived
from each other. However, since we're about to bugcheck, we run
a risk of double-faulting while we're chasing pointers. Therefore,
we give the caller the option to override some of the pointers by
sending us a pointer directly.
Arguments:
DataBlock - Generic block of memory to place into the dump file.
DataBlockSize - Size of DataBlock (in bytes).
DeviceObject - DEVICE_OBJECT to place into dump file.
DriverObject - DRIVER_OBJECT to place into dump file.
N.B. This overrides a value we may find in DeviceObject->DriverObject
Doe - DEVOBJ_EXTENSION to place into dump file.
N.B. This overrides a value we may find in DeviceObject->DeviceObjectExtension
Dope - DEVICE_OBJECT_POWER_EXTENSION to place into the dump file.
N.B. This overrides a value we may find in Doe->Dope (or by induction, DeviceObject->DeviceObjectExtension->Dope
Return Value:
None
--*/
{
PDRIVER_OBJECT lPDriverObject = NULL;
PDEVOBJ_EXTENSION lPDoe = NULL;
PDEVICE_OBJECT_POWER_EXTENSION lPDope = NULL;
//
// Insert any parameters that were sent in.
//
if( DataBlock ) {
IoAddTriageDumpDataBlock(
PAGE_ALIGN(DataBlock),
(DataBlockSize ? BYTES_TO_PAGES(DataBlockSize) : PAGE_SIZE) );
}
if( DeviceObject ) {
IoAddTriageDumpDataBlock(DeviceObject, sizeof(DEVICE_OBJECT));
}
if( DriverObject ) {
lPDriverObject = DriverObject;
} else if( (DeviceObject) && (DeviceObject->DriverObject) ) {
lPDriverObject = DeviceObject->DriverObject;
}
if( lPDriverObject ) {
IoAddTriageDumpDataBlock(lPDriverObject, lPDriverObject->Size);
if( lPDriverObject->DriverName.Buffer ) {
IoAddTriageDumpDataBlock(lPDriverObject->DriverName.Buffer, lPDriverObject->DriverName.Length);
}
}
if( Doe ) {
lPDoe = Doe;
} else if( DeviceObject ) {
lPDoe = DeviceObject->DeviceObjectExtension;
}
if( lPDoe ) {
IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDoe), sizeof(DEVOBJ_EXTENSION));
if( lPDoe->DeviceNode ) {
IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDoe->DeviceNode), PAGE_SIZE);
}
if( lPDoe->AttachedTo ) {
IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDoe->AttachedTo), PAGE_SIZE);
}
if( lPDoe->Vpb ) {
IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDoe->Vpb), PAGE_SIZE);
}
}
if( Dope ) {
lPDope = Dope;
} else if( lPDoe ) {
lPDope = lPDoe->Dope;
}
if( lPDope ) {
IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDope), sizeof(DEVICE_OBJECT_POWER_EXTENSION));
}
//
// Globals that may be of interest.
//
IoAddTriageDumpDataBlock(PAGE_ALIGN(&PopHiberFile), sizeof(POP_HIBER_FILE));
IoAddTriageDumpDataBlock(PAGE_ALIGN(&PopAction), sizeof(POP_POWER_ACTION));
if(PopAction.DevState) {
IoAddTriageDumpDataBlock(PAGE_ALIGN(&(PopAction.DevState)), sizeof(POP_DEVICE_SYS_STATE));
}
if(PopAction.HiberContext) {
IoAddTriageDumpDataBlock(PAGE_ALIGN(&(PopAction.HiberContext)), sizeof(POP_HIBER_CONTEXT));
}
IoAddTriageDumpDataBlock(PAGE_ALIGN(&PopCB), sizeof(POP_COMPOSITE_BATTERY));
if(PopCB.StatusIrp) {
IoAddTriageDumpDataBlock(PAGE_ALIGN(&(PopCB.StatusIrp)), sizeof(IRP));
}
IoAddTriageDumpDataBlock(PAGE_ALIGN(PopAttributes), sizeof(POP_STATE_ATTRIBUTE) * POP_NUMBER_ATTRIBUTES);
}
VOID
FASTCALL
_PopInternalError (
IN ULONG BugCode
)
{
KeBugCheckEx (INTERNAL_POWER_ERROR, 0x2, BugCode, 0, 0);
}
EXCEPTION_DISPOSITION
PopExceptionFilter (
IN PEXCEPTION_POINTERS ExceptionInfo,
IN BOOLEAN AllowRaisedException
)
{
//
// If handler wants raised expceptions, check the exception code
//
if (AllowRaisedException) {
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case STATUS_INVALID_PARAMETER:
case STATUS_INVALID_PARAMETER_1:
case STATUS_INVALID_PARAMETER_2:
return EXCEPTION_EXECUTE_HANDLER;
}
}
//
// Not allowed
//
PoPrint (PO_ERROR, ("PoExceptionFilter: exr %x, cxr %x",
ExceptionInfo->ExceptionRecord,
ExceptionInfo->ContextRecord
));
PopInternalAddToDumpFile( ExceptionInfo->ExceptionRecord,
sizeof(EXCEPTION_RECORD),
NULL,
NULL,
NULL,
NULL );
PopInternalAddToDumpFile( ExceptionInfo->ContextRecord,
sizeof(CONTEXT),
NULL,
NULL,
NULL,
NULL );
KeBugCheckEx( INTERNAL_POWER_ERROR,
0x101,
POP_MISC,
(ULONG_PTR)ExceptionInfo,
0 );
}
PUCHAR
PopSystemStateString(
IN SYSTEM_POWER_STATE SystemState
)
// This function is not DBG because...
{
PUCHAR p;
switch (SystemState) {
case PowerSystemUnspecified: p = "Unspecified"; break;
case PowerSystemWorking: p = "Working"; break;
case PowerSystemSleeping1: p = "Sleeping1"; break;
case PowerSystemSleeping2: p = "Sleeping2"; break;
case PowerSystemSleeping3: p = "Sleeping3"; break;
case PowerSystemHibernate: p = "Hibernate"; break;
case PowerSystemShutdown: p = "Shutdown"; break;
default: p = "?";
}
return p;
}
#if DBG
PUCHAR
PopPowerActionString(
IN POWER_ACTION PowerAction
)
// This function is not DBG because...
{
PUCHAR p;
switch (PowerAction) {
case PowerActionNone: p = "None"; break;
case PowerActionSleep: p = "Sleep"; break;
case PowerActionHibernate: p = "Hibernate"; break;
case PowerActionShutdown: p = "Shutdown"; break;
case PowerActionShutdownReset: p = "ShutdownReset"; break;
case PowerActionShutdownOff: p = "ShutdownOff"; break;
case PowerActionWarmEject: p = "WarmEject"; break;
default: p = "?";
}
return p;
}
#endif
#if DBG
//
// PowerTrace variables
//
ULONG PoPowerTraceControl = 0L;
ULONG PoPowerTraceCount = 0L;
PUCHAR PoPowerTraceMinorCode[] = {
"wait", "seq", "set", "query"
};
PUCHAR PoPowerTracePoint[] = {
"calldrv", "present", "startnxt", "setstate", "complete"
};
PUCHAR PoPowerType[] = {
"sys", "dev"
};
VOID
PoPowerTracePrint(
ULONG TracePoint,
ULONG_PTR Caller,
ULONG_PTR CallerCaller,
ULONG_PTR DeviceObject,
ULONG_PTR Arg1,
ULONG_PTR Arg2
)
/*
Example:
PLOG,00015,startnxt,c@ffea1345,cc@ffea5643,do@80081234,irp@8100ff00,ios@8100ff10,query,sys,3
*/
{
PIO_STACK_LOCATION Isp;
PUCHAR tracename;
ULONG j;
ULONG tp;
PoPowerTraceCount++;
if (PoPowerTraceControl & TracePoint) {
tracename = NULL;
tp = TracePoint;
for (j = 0; j < 33; tp = tp >> 1, j = j+1)
{
if (tp & 1) {
tracename = PoPowerTracePoint[j];
j = 33;
}
}
DbgPrint("PLOG,%05ld,%8s,do@%08lx",
PoPowerTraceCount,tracename,DeviceObject
);
if ((TracePoint == POWERTRACE_CALL) ||
(TracePoint == POWERTRACE_PRESENT) ||
(TracePoint == POWERTRACE_STARTNEXT))
{
DbgPrint(",irp@%08lx,isp@%08lx",Arg1,Arg2);
Isp = (PIO_STACK_LOCATION)Arg2;
DbgPrint(",%5s", PoPowerTraceMinorCode[Isp->MinorFunction]);
if ((Isp->MinorFunction == IRP_MN_SET_POWER) ||
(Isp->MinorFunction == IRP_MN_QUERY_POWER))
{
DbgPrint(",%s,%d",
PoPowerType[Isp->Parameters.Power.Type],
((ULONG)Isp->Parameters.Power.State.DeviceState)-1 // hack - works for sys state too
);
}
} else if (TracePoint == POWERTRACE_SETSTATE) {
DbgPrint(",,,,%s,%d", PoPowerType[Arg1], Arg2-1);
} else if (TracePoint == POWERTRACE_COMPLETE) {
DbgPrint(",irp@%08lx,isp@%08lx",Arg1,Arg2);
}
DbgPrint("\n");
}
return;
}
#endif
ULONG PoSimpleCheck(IN ULONG PartialSum,
IN PVOID SourceVa,
IN ULONG_PTR Length)
{
// Just use the TCP/IP check sum
//
return tcpxsum(PartialSum, (PUCHAR)SourceVa, Length);
}
NTSTATUS
PopOpenPowerKey (
OUT PHANDLE Handle
)
/*++
Routine Description:
Open and return the handle to the power policy key in the registry
Arguments:
Handle - Handle to power policy key
Return Value:
Status
--*/
{
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE BaseHandle;
ULONG disposition;
//
// Open current control set
//
InitializeObjectAttributes(
&ObjectAttributes,
&CmRegistryMachineSystemCurrentControlSet,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
(PSECURITY_DESCRIPTOR) NULL
);
Status = ZwOpenKey (
&BaseHandle,
KEY_READ | KEY_WRITE,
&ObjectAttributes
);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Open power branch
//
RtlInitUnicodeString (&UnicodeString, PopRegKey);
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
BaseHandle,
(PSECURITY_DESCRIPTOR) NULL
);
Status = ZwCreateKey (
Handle,
KEY_READ | KEY_WRITE,
&ObjectAttributes,
0,
(PUNICODE_STRING) NULL,
REG_OPTION_NON_VOLATILE,
&disposition
);
ZwClose (BaseHandle);
return Status;
}
VOID
PopInitializePowerPolicySimulate(
VOID
)
/*++
Routine Description:
Reads PopSimulate out of the registry. Also applies any overrides that might
be required as a result of the installed system (hydra for example)
Arguments:
NONE.
Return Value:
Status
--*/
{
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE BaseHandle;
HANDLE Handle;
ULONG Length;
ULONG disposition;
struct {
KEY_VALUE_PARTIAL_INFORMATION Inf;
ULONG Data;
} PartialInformation;
PAGED_CODE();
//
// Open current control set
//
InitializeObjectAttributes(
&ObjectAttributes,
&CmRegistryMachineSystemCurrentControlSet,
OBJ_CASE_INSENSITIVE,
NULL,
(PSECURITY_DESCRIPTOR) NULL
);
Status = ZwOpenKey(
&BaseHandle,
KEY_READ,
&ObjectAttributes
);
if (!NT_SUCCESS(Status)) {
goto done;
}
// Get the right key
RtlInitUnicodeString (&UnicodeString, PopSimulateRegKey);
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
BaseHandle,
(PSECURITY_DESCRIPTOR) NULL
);
Status = ZwCreateKey(
&Handle,
KEY_READ,
&ObjectAttributes,
0,
(PUNICODE_STRING) NULL,
REG_OPTION_NON_VOLATILE,
&disposition
);
ZwClose(BaseHandle);
if(!NT_SUCCESS(Status)) {
goto done;
}
//
// Get the value of the simulation
//
RtlInitUnicodeString (&UnicodeString,PopSimulateRegName);
Status = ZwQueryValueKey(
Handle,
&UnicodeString,
KeyValuePartialInformation,
&PartialInformation,
sizeof (PartialInformation),
&Length
);
ZwClose (Handle);
if (!NT_SUCCESS(Status)) {
goto done;
}
//
// Check to make sure the retrieved data makes sense
//
if(PartialInformation.Inf.DataLength != sizeof(ULONG)) {
goto done;
}
//
// Initialize PopSimulate
//
PopSimulate = *((PULONG)(PartialInformation.Inf.Data));
done:
return;
}
VOID
PopSaveHeuristics (
VOID
)
/*++
Routine Description:
Open and return the handle to the power policy key in the registry
Arguments:
Handle - Handle to power policy key
Return Value:
Status
--*/
{
HANDLE handle;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
ASSERT_POLICY_LOCK_OWNED();
Status = PopOpenPowerKey (&handle);
if (NT_SUCCESS(Status)) {
PopHeuristics.Dirty = FALSE;
RtlInitUnicodeString (&UnicodeString, PopHeuristicsRegName);
Status = ZwSetValueKey (
handle,
&UnicodeString,
0L,
REG_BINARY,
&PopHeuristics,
sizeof (PopHeuristics)
);
ZwClose(handle);
}
}
VOID
PoInvalidateDevicePowerRelations(
PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine is called by IoInvalidateDeviceRelations when the
type of invalidation is for power relations.
It will will knock down the notify network around the supplied
device object.
Arguments:
DeviceObject - supplies the address of the device object whose
power relations are now invalid.
Return Value:
None.
--*/
{
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
PopRunDownSourceTargetList(DeviceObject);
return;
}
NTSTATUS
PoGetLightestSystemStateForEject(
IN BOOLEAN DockBeingEjected,
IN BOOLEAN HotEjectSupported,
IN BOOLEAN WarmEjectSupported,
OUT PSYSTEM_POWER_STATE LightestSleepState
)
/*++
Routine Description:
This routine is called by ntos\pnp\pnpevent.c to determine the lightest
sleep state an eject operation can be executed in. This function is to be
called after all appropriate batteries/power adapters have been *removed*,
but before the eject has occured.
Arguments:
DockBeingEjected - TRUE iff a dock is among the items that may be
ejected.
HotEjectSupported - TRUE if the device being ejected supports S0 VCR
style eject.
WarmEjectSupported - TRUE if the device being ejected supports S1-S4
style warm ejection.
LightestSleepState - Set to the lightest sleep state the device can be
ejected in. On error, this is set to
PowerSystemUnspecified.
N.B. If both HotEjectSupported and WarmEjectSupported are FALSE, it is
assumed this device is "user" ejectable in S0 (ie. Hot ejectable).
Return Value:
NTSTATUS - If there is insufficient power to do the indicated operation,
STATUS_INSUFFICIENT_POWER is returned.
--*/
{
SYSTEM_BATTERY_STATE systemBatteryInfo;
UNICODE_STRING unicodeString;
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS status;
HANDLE handle;
ULONG length;
ULONG currentPercentage;
ULONG disposition;
UCHAR ejectPartialInfo[SIZEOF_EJECT_PARTIAL_INFO];
PUNDOCK_POWER_RESTRICTIONS undockRestrictions;
PKEY_VALUE_PARTIAL_INFORMATION partialInfoHeader;
PAGED_CODE();
//
// Preinit sleep to failure.
//
*LightestSleepState = PowerSystemUnspecified;
//
// If neither, then it's a "user" assisted hot eject.
//
if ((!HotEjectSupported) && (!WarmEjectSupported)) {
HotEjectSupported = TRUE;
}
//
// If it's not a dock device being ejected, we assume no great changes
// in power will occur after the eject. Therefore our policy is simple,
// if we can't do hot eject, we'll try warm eject in the best possible
// sleep state.
//
if (!DockBeingEjected) {
if (HotEjectSupported) {
*LightestSleepState = PowerSystemWorking;
} else {
ASSERT(WarmEjectSupported);
*LightestSleepState = PowerSystemSleeping1;
}
return STATUS_SUCCESS;
}
//
// We are going to eject a dock, so we retrieve our undock power policy.
//
status = PopOpenPowerKey (&handle);
if (!NT_SUCCESS(status)) {
return status;
}
// Get the right key
RtlInitUnicodeString (&unicodeString, PopUndockPolicyRegName);
status = ZwQueryValueKey (
handle,
&unicodeString,
KeyValuePartialInformation,
&ejectPartialInfo,
sizeof (ejectPartialInfo),
&length
);
ZwClose (handle);
if ((!NT_SUCCESS(status)) && (status != STATUS_OBJECT_NAME_NOT_FOUND)) {
return status;
}
// Check to make sure the retrieved data makes sense
partialInfoHeader = (PKEY_VALUE_PARTIAL_INFORMATION) ejectPartialInfo;
undockRestrictions =
(PUNDOCK_POWER_RESTRICTIONS) (ejectPartialInfo + SIZEOF_PARTIAL_INFO_HEADER);
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
//
// These defaults match Win9x behavior. 0% for sleep undock means
// we always allow undock into Sx. This is bad for some laptops, but
// legal for those that have reserve power we don't see.
//
undockRestrictions->HotUndockMinimumCapacity = 10; // In percent
undockRestrictions->SleepUndockMinimumCapacity = 0; // In percent
} else if (partialInfoHeader->DataLength <
FIELD_OFFSET(UNDOCK_POWER_RESTRICTIONS, HotUndockMinimumCapacity)) {
return STATUS_REGISTRY_CORRUPT;
} else if (undockRestrictions->Version != 1) {
//
// We cannot interpret the information stored in the registry. Bail.
//
return STATUS_UNSUCCESSFUL;
} else if ((partialInfoHeader->DataLength < sizeof(UNDOCK_POWER_RESTRICTIONS)) ||
(undockRestrictions->Size != partialInfoHeader->DataLength)) {
//
// Malformed for version 1.
//
return STATUS_REGISTRY_CORRUPT;
}
//
// Retrieve all the fun battery info. Note that we do not examine the
// AC power adapter information as the best bus we have today (ACPI) doesn't
// let us know if an AC adapter is leaving when we undock (so we assume all
// will). If the vendor *did* put his adapter in the AML namespace, we would
// enter CRITICAL shutdown immediately upon change driver in our current
// design.
//
status = NtPowerInformation(
SystemBatteryState,
NULL,
0,
&systemBatteryInfo,
sizeof(systemBatteryInfo)
) ;
if (!NT_SUCCESS(status)) {
return status;
}
//
// Convert current capacity in milliwatt hours to percentage remaining. We
// should really make a decision based on the amount of time remaining under
// peak milliwatt usage, but we do not collect enough information for this
// today...
//
if (systemBatteryInfo.MaxCapacity == 0) {
currentPercentage = 0;
} else {
//
// Did we "wrap" around?
//
if ((systemBatteryInfo.RemainingCapacity * 100) <=
systemBatteryInfo.RemainingCapacity) {
currentPercentage = 0;
} else {
currentPercentage = (systemBatteryInfo.RemainingCapacity * 100)/
systemBatteryInfo.MaxCapacity;
}
}
//
// Pick the appropriate sleep state based on our imposed limits.
//
if ((currentPercentage >= undockRestrictions->HotUndockMinimumCapacity) &&
HotEjectSupported) {
*LightestSleepState = PowerSystemWorking;
} else if (WarmEjectSupported) {
if (currentPercentage >= undockRestrictions->SleepUndockMinimumCapacity) {
*LightestSleepState = PowerSystemSleeping1;
} else {
*LightestSleepState = PowerSystemHibernate;
}
} else {
status = STATUS_INSUFFICIENT_POWER;
}
return status;
}
VOID
PoGetDevicePowerState(
IN PDEVICE_OBJECT PhysicalDeviceObject,
OUT DEVICE_POWER_STATE *DevicePowerState
)
/*++
Routine Description:
This routine gets the power state of a given device. The object should be
the Physical Device Object for a *Started* WDM device stack.
Arguments:
PhysicalDeviceObject - Device object representing the bottom of a WDM
device stack.
DevicePowerState - Receives the power state of the given device.
Return Value:
None.
--*/
{
PDEVOBJ_EXTENSION doe;
DEVICE_POWER_STATE deviceState;
PAGED_CODE();
doe = PhysicalDeviceObject->DeviceObjectExtension;
deviceState = PopLockGetDoDevicePowerState(doe);
if (deviceState == PowerDeviceUnspecified) {
//
// The PDO isn't bothering to call PoSetPowerState. Since this API
// shouldn't be called on non-started devices, we will call it D0.
//
deviceState = PowerDeviceD0;
}
*DevicePowerState = deviceState;
}
VOID
PopUnlockAfterSleepWorker(
IN PVOID NotUsed
)
/*++
Routine Description:
This work item performs the unlocking of code and worker threads that
corresponds to the locking done at the beginning of NtSetSystemPowerState.
The unlocking is queued off to a delayed worker thread because it is likely
to block on disk I/O, which will force the resume to get stuck waiting for
the disks to spin up.
Arguments:
NotUsed - not used
Return Value:
None
--*/
{
MmUnlockPagableImageSection(ExPageLockHandle);
ExSwapinWorkerThreads(TRUE);
ExNotifyCallback (ExCbPowerState, (PVOID) PO_CB_SYSTEM_STATE_LOCK, (PVOID) 1);
//
// Signal that unlocking is done and it is safe to lock again.
//
KeSetEvent(&PopUnlockComplete, 0, FALSE);
}