1565 lines
40 KiB
C
1565 lines
40 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ntapi.c
|
||
|
||
Abstract:
|
||
|
||
NT api level routines for the po component reside in this file
|
||
|
||
Author:
|
||
|
||
Bryan Willman (bryanwi) 14-Nov-1996
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
#include "pop.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtSetThreadExecutionState)
|
||
#pragma alloc_text(PAGE, NtRequestWakeupLatency)
|
||
#pragma alloc_text(PAGE, NtInitiatePowerAction)
|
||
#pragma alloc_text(PAGE, NtGetDevicePowerState)
|
||
#pragma alloc_text(PAGE, NtCancelDeviceWakeupRequest)
|
||
#pragma alloc_text(PAGE, NtIsSystemResumeAutomatic)
|
||
#pragma alloc_text(PAGE, NtRequestDeviceWakeup)
|
||
#pragma alloc_text(PAGELK, NtSetSystemPowerState)
|
||
#endif
|
||
|
||
extern POBJECT_TYPE IoFileObjectType;
|
||
|
||
WORK_QUEUE_ITEM PopShutdownWorkItem;
|
||
WORK_QUEUE_ITEM PopUnlockAfterSleepWorkItem;
|
||
KEVENT PopUnlockComplete;
|
||
extern ERESOURCE ExpTimeRefreshLock;
|
||
|
||
NTSYSAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
NtSetThreadExecutionState(
|
||
IN EXECUTION_STATE NewFlags, // ES_xxx flags
|
||
OUT EXECUTION_STATE *PreviousFlags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Implements Win32 API functionality. Tracks thread execution state
|
||
attributes. Keeps global count of all such attributes set.
|
||
|
||
Arguments:
|
||
|
||
NewFlags - Attributes to set or pulse
|
||
|
||
PreviousFlags - Threads 'set' attributes before applying NewFlags
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
ULONG OldFlags;
|
||
PKTHREAD Thread;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
Thread = KeGetCurrentThread();
|
||
Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Verify no reserved bits set
|
||
//
|
||
|
||
if (NewFlags & ~(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_CONTINUOUS)) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
try {
|
||
//
|
||
// Verify callers params
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
ProbeForWriteUlong (PreviousFlags);
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Get current flags
|
||
//
|
||
|
||
OldFlags = Thread->PowerState | ES_CONTINUOUS;
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
PopAcquirePolicyLock ();
|
||
|
||
//
|
||
// If the continous bit is set, modify current thread flags
|
||
//
|
||
|
||
if (NewFlags & ES_CONTINUOUS) {
|
||
Thread->PowerState = (UCHAR) NewFlags;
|
||
PopApplyAttributeState (NewFlags, OldFlags);
|
||
} else {
|
||
PopApplyAttributeState (NewFlags, 0);
|
||
}
|
||
|
||
//
|
||
// Release the lock here, but don't steal the poor caller's thread to
|
||
// do the work. Otherwise we can get in weird message loop deadlocks as
|
||
// this thread is waiting for the USER32 thread, which is broadcasting a
|
||
// system message to this thread's window.
|
||
//
|
||
PopReleasePolicyLock (FALSE);
|
||
PopCheckForWork(TRUE);
|
||
|
||
//
|
||
// Return the results
|
||
//
|
||
|
||
try {
|
||
*PreviousFlags = OldFlags;
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSYSAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
NtRequestWakeupLatency(
|
||
IN LATENCY_TIME latency // LT_xxx flags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Tracks process wakeup latecy attribute. Keeps global count
|
||
of all such attribute settings.
|
||
|
||
Arguments:
|
||
|
||
latency - Current latency setting for process
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
PEPROCESS Process;
|
||
ULONG OldFlags, NewFlags;
|
||
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Verify latency is known
|
||
//
|
||
|
||
switch (latency) {
|
||
case LT_DONT_CARE:
|
||
NewFlags = ES_CONTINUOUS;
|
||
break;
|
||
|
||
case LT_LOWEST_LATENCY:
|
||
NewFlags = ES_CONTINUOUS | POP_LOW_LATENCY;
|
||
break;
|
||
|
||
default:
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
Process = PsGetCurrentProcess();
|
||
PopAcquirePolicyLock ();
|
||
|
||
//
|
||
// Get changes
|
||
//
|
||
|
||
OldFlags = Process->Pcb.PowerState | ES_CONTINUOUS;
|
||
|
||
//
|
||
// Udpate latency flag in process field
|
||
//
|
||
|
||
Process->Pcb.PowerState = (UCHAR) NewFlags;
|
||
|
||
//
|
||
// Handle flags
|
||
//
|
||
|
||
PopApplyAttributeState (NewFlags, OldFlags);
|
||
|
||
//
|
||
// Done
|
||
//
|
||
|
||
PopReleasePolicyLock (TRUE);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSYSAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
NtInitiatePowerAction(
|
||
IN POWER_ACTION SystemAction,
|
||
IN SYSTEM_POWER_STATE LightestSystemState,
|
||
IN ULONG Flags, // POWER_ACTION_xxx flags
|
||
IN BOOLEAN Asynchronous
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Implements functionality for Win32 APIs to initiate a power
|
||
action. Causes s/w initiated trigger of requested action.
|
||
|
||
Arguments:
|
||
|
||
SystemAction - The action to initiate
|
||
|
||
LightestSystemState - If a sleep action, the minimum state which must be
|
||
entered
|
||
|
||
Flags - Attributes of action
|
||
|
||
Asynchronous - Function should initiate action and return, or should wait
|
||
for the action to complete before returning
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
KPROCESSOR_MODE PreviousMode;
|
||
POWER_ACTION_POLICY Policy;
|
||
POP_ACTION_TRIGGER Trigger;
|
||
PPOP_TRIGGER_WAIT Wait;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Verify callers access
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (!SeSinglePrivilegeCheck( SeShutdownPrivilege, PreviousMode )) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
|
||
if (SystemAction == PowerActionWarmEject) {
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
return STATUS_INVALID_PARAMETER_1;
|
||
}
|
||
}
|
||
|
||
if (Flags & POWER_ACTION_LIGHTEST_FIRST) {
|
||
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
}
|
||
|
||
//
|
||
// Build a policy & trigger to cause the action
|
||
//
|
||
|
||
RtlZeroMemory (&Policy, sizeof(Policy));
|
||
RtlZeroMemory (&Trigger, sizeof(Trigger));
|
||
|
||
Policy.Action = SystemAction;
|
||
Policy.Flags = Flags;
|
||
Trigger.Type = PolicyInitiatePowerActionAPI;
|
||
Trigger.Flags = PO_TRG_SET;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// If this is a synchronous power action request attach trigger
|
||
// wait structure to action
|
||
//
|
||
|
||
Wait = NULL;
|
||
if (!Asynchronous) {
|
||
Wait = ExAllocatePoolWithTag (
|
||
NonPagedPool,
|
||
sizeof (POP_TRIGGER_WAIT),
|
||
POP_PACW_TAG
|
||
);
|
||
if (!Wait) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory (Wait, sizeof(POP_TRIGGER_WAIT));
|
||
Wait->Status = STATUS_SUCCESS;
|
||
Wait->Trigger = &Trigger;
|
||
KeInitializeEvent (&Wait->Event, NotificationEvent, FALSE);
|
||
Trigger.Flags |= PO_TRG_SYNC;
|
||
Trigger.Wait = Wait;
|
||
}
|
||
|
||
//
|
||
// Acquire lock and fire it
|
||
//
|
||
|
||
PopAcquirePolicyLock ();
|
||
|
||
try {
|
||
|
||
PopSetPowerAction(
|
||
&Trigger,
|
||
0,
|
||
&Policy,
|
||
LightestSystemState,
|
||
SubstituteLightestOverallDownwardBounded
|
||
);
|
||
|
||
} except (PopExceptionFilter(GetExceptionInformation(), TRUE)) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
PopReleasePolicyLock (TRUE);
|
||
|
||
//
|
||
// If queued, wait for it to complete
|
||
//
|
||
|
||
if (Wait) {
|
||
|
||
|
||
if (Wait->Link.Flink) {
|
||
|
||
ASSERT(NT_SUCCESS(Status));
|
||
Status = KeWaitForSingleObject (&Wait->Event, Suspended, KernelMode, TRUE, NULL);
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = Wait->Status;
|
||
}
|
||
|
||
//
|
||
// Remove wait block from the queue
|
||
//
|
||
|
||
PopAcquirePolicyLock ();
|
||
RemoveEntryList (&Wait->Link);
|
||
PopReleasePolicyLock (FALSE);
|
||
} else {
|
||
//
|
||
// The wait block was not queued, it must have either failed or succeeded
|
||
// immediately.
|
||
//
|
||
Status = Wait->Status;
|
||
}
|
||
|
||
ExFreePool (Wait);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSYSAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
NtSetSystemPowerState (
|
||
IN POWER_ACTION SystemAction,
|
||
IN SYSTEM_POWER_STATE LightestSystemState,
|
||
IN ULONG Flags // POWER_ACTION_xxx flags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
N.B. This function is only called by Winlogon.
|
||
|
||
Winlogon calls this function in response to the policy manager calling
|
||
PopStateCallout once user mode operations have completed.
|
||
|
||
Arguments:
|
||
|
||
SystemAction - The current system action being processed.
|
||
|
||
LightestSystemState - The min system state for the action.
|
||
|
||
Flags - The attribute flags for the action.
|
||
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status, Status2;
|
||
POWER_ACTION_POLICY Action;
|
||
BOOLEAN QueryDevices;
|
||
BOOLEAN TimerRefreshLockOwned;
|
||
BOOLEAN BootStatusUpdated;
|
||
BOOLEAN VolumesFlushed;
|
||
BOOLEAN PolicyLockOwned;
|
||
BOOLEAN OptionsExhausted;
|
||
PVOID WakeTimerObject;
|
||
PVOID S4DozeObject;
|
||
HANDLE S4DozeTimer;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
TIMER_BASIC_INFORMATION TimerInformation;
|
||
POP_ACTION_TRIGGER Trigger;
|
||
SYSTEM_POWER_STATE DeepestSystemState;
|
||
ULONGLONG WakeTime;
|
||
ULONGLONG SleepTime;
|
||
TIME_FIELDS WakeTimeFields;
|
||
LARGE_INTEGER DueTime;
|
||
POP_SUBSTITUTION_POLICY SubstitutionPolicy;
|
||
NT_PRODUCT_TYPE NtProductType;
|
||
PIO_ERROR_LOG_PACKET ErrLog;
|
||
BOOLEAN WroteErrLog=FALSE;
|
||
|
||
//
|
||
// Verify callers access
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
if (!SeSinglePrivilegeCheck( SeShutdownPrivilege, PreviousMode )) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
|
||
//
|
||
// Turn into kernel mode operation
|
||
//
|
||
|
||
return ZwSetSystemPowerState (SystemAction, LightestSystemState, Flags);
|
||
}
|
||
|
||
//
|
||
// disable registry's lazzy flusher
|
||
//
|
||
CmSetLazyFlushState(FALSE);
|
||
|
||
//
|
||
// Setup
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
TimerRefreshLockOwned = FALSE;
|
||
BootStatusUpdated = FALSE;
|
||
VolumesFlushed = FALSE;
|
||
S4DozeObject = NULL;
|
||
WakeTimerObject = NULL;
|
||
WakeTime = 0;
|
||
|
||
RtlZeroMemory (&Action, sizeof(Action));
|
||
Action.Action = SystemAction;
|
||
Action.Flags = Flags;
|
||
|
||
RtlZeroMemory (&Trigger, sizeof(Trigger));
|
||
Trigger.Type = PolicySetPowerStateAPI;
|
||
Trigger.Flags = PO_TRG_SET;
|
||
|
||
//
|
||
// Lock any code dealing with shutdown or sleep
|
||
//
|
||
// PopUnlockComplete event is used to make sure that any previous unlock
|
||
// has completed before we try and lock everything again.
|
||
//
|
||
|
||
ASSERT(ExPageLockHandle);
|
||
KeWaitForSingleObject(&PopUnlockComplete, WrExecutive, KernelMode, FALSE, NULL);
|
||
MmLockPagableSectionByHandle(ExPageLockHandle);
|
||
ExNotifyCallback (ExCbPowerState, (PVOID) PO_CB_SYSTEM_STATE_LOCK, (PVOID) 0);
|
||
ExSwapinWorkerThreads(FALSE);
|
||
|
||
//
|
||
// Acquire policy manager lock
|
||
//
|
||
|
||
PopAcquirePolicyLock ();
|
||
PolicyLockOwned = TRUE;
|
||
|
||
//
|
||
// If we're not in the callout state, don't re-enter.
|
||
// The caller (paction.c) will handle the collision.
|
||
//
|
||
|
||
if (PopAction.State != PO_ACT_IDLE && PopAction.State != PO_ACT_CALLOUT) {
|
||
PoPrint (PO_PACT, ("NtSetSystemPowerState: already committed\n"));
|
||
PopReleasePolicyLock (FALSE);
|
||
MmUnlockPagableImageSection (ExPageLockHandle);
|
||
ExSwapinWorkerThreads(TRUE);
|
||
KeSetEvent(&PopUnlockComplete, 0, FALSE);
|
||
|
||
//
|
||
// try to catch weird case where we exit this routine with the
|
||
// time refresh lock held.
|
||
//
|
||
ASSERT(!ExIsResourceAcquiredExclusive(&ExpTimeRefreshLock));
|
||
return STATUS_ALREADY_COMMITTED;
|
||
}
|
||
|
||
if (PopAction.State == PO_ACT_IDLE) {
|
||
//
|
||
// If there is no other request, we want to clean up PopAction before we start,
|
||
// PopSetPowerAction() will not do this after we set State=PO_ACT_SET_SYSTEM_STATE.
|
||
//
|
||
PopResetActionDefaults();
|
||
}
|
||
//
|
||
// Update to action state to setting the system state
|
||
//
|
||
|
||
PopAction.State = PO_ACT_SET_SYSTEM_STATE;
|
||
|
||
//
|
||
// Set status to cancelled to start off as if this is a new request
|
||
//
|
||
|
||
Status = STATUS_CANCELLED;
|
||
|
||
try {
|
||
|
||
//
|
||
// Verify params and promote the current action.
|
||
//
|
||
PopSetPowerAction(
|
||
&Trigger,
|
||
0,
|
||
&Action,
|
||
LightestSystemState,
|
||
SubstituteLightestOverallDownwardBounded
|
||
);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
ASSERT (!NT_SUCCESS(Status));
|
||
}
|
||
|
||
//
|
||
// Lagecy hal support. If the original action was PowerDown
|
||
// change the action to be power down (as presumbly even if
|
||
// there's no handler HalReturnToFirmware will know what to do)
|
||
//
|
||
|
||
if (SystemAction == PowerActionShutdownOff) {
|
||
PopAction.Action = PowerActionShutdownOff;
|
||
}
|
||
|
||
//
|
||
// Allocate the DevState here. From this point out we must be careful
|
||
// that we never release the policy lock with State == PO_ACT_SET_SYSTEM_STATE
|
||
// and PopAction.DevState not valid. Otherwise there is a race condition
|
||
// with PopRestartSetSystemState.
|
||
//
|
||
PopAllocateDevState();
|
||
if (PopAction.DevState == NULL) {
|
||
PopAction.State = PO_ACT_IDLE;
|
||
PopReleasePolicyLock(FALSE);
|
||
MmUnlockPagableImageSection( ExPageLockHandle );
|
||
ExSwapinWorkerThreads(TRUE);
|
||
KeSetEvent(&PopUnlockComplete, 0, FALSE);
|
||
//
|
||
// try to catch weird case where we exit this routine with the
|
||
// time refresh lock held.
|
||
//
|
||
ASSERT(!ExIsResourceAcquiredExclusive(&ExpTimeRefreshLock));
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// At this point in the cycle, its not possible to abort the operation
|
||
// so this is a good time to ensure that the CPU is back running as close
|
||
// to 100% as we can make it.
|
||
//
|
||
PopSetPerfFlag( PSTATE_DISABLE_THROTTLE_NTAPI, FALSE );
|
||
PopUpdateAllThrottles();
|
||
|
||
//
|
||
// While there's some action pending handle it.
|
||
//
|
||
// N.B. We will never get here if no sleep states are supported, as
|
||
// NtInitiatePowerAction will fail (PopVerifyPowerActionPolicy will return
|
||
// Disabled == TRUE). Therefore we won't accidentally querying for S0. Note
|
||
// that all the policy limitations were also verified at some point too.
|
||
//
|
||
|
||
for (; ;) {
|
||
//
|
||
// N.B. The system must be in the working state to be here
|
||
//
|
||
|
||
if (!PolicyLockOwned) {
|
||
PopAcquirePolicyLock ();
|
||
PolicyLockOwned = TRUE;
|
||
}
|
||
|
||
//
|
||
// If there's nothing to do, stop
|
||
//
|
||
|
||
if (PopAction.Action == PowerActionNone) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Hibernate actions are converted to sleep actions before here.
|
||
//
|
||
|
||
ASSERT (PopAction.Action != PowerActionHibernate);
|
||
|
||
//
|
||
// We're handling it - clear update flags
|
||
//
|
||
|
||
PopAction.Updates &= ~(PO_PM_USER | PO_PM_REISSUE | PO_PM_SETSTATE);
|
||
|
||
//
|
||
// If the last operation was cancelled, update state for the
|
||
// new operation
|
||
//
|
||
|
||
if (Status == STATUS_CANCELLED) {
|
||
|
||
//
|
||
// If Re-issue is set we may need to abort back to PopSetPowerAction
|
||
// to let apps know of the promotion
|
||
//
|
||
|
||
if (PopAction.Updates & PO_PM_REISSUE) {
|
||
|
||
//
|
||
// Only abort if apps notificiation is allowed
|
||
//
|
||
|
||
if (!(PopAction.Flags & (POWER_ACTION_CRITICAL)) &&
|
||
(PopAction.Flags & (POWER_ACTION_QUERY_ALLOWED |
|
||
POWER_ACTION_UI_ALLOWED))
|
||
) {
|
||
|
||
// abort with STATUS_CANCELLED to PopSetPowerAction
|
||
PopGetPolicyWorker (PO_WORKER_ACTION_NORMAL);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get limits and start (over) with the first sleep state to try.
|
||
//
|
||
PopActionRetrieveInitialState(
|
||
&PopAction.LightestState,
|
||
&DeepestSystemState,
|
||
&PopAction.SystemState,
|
||
&QueryDevices
|
||
);
|
||
|
||
ASSERT (PopAction.SystemState != PowerActionNone);
|
||
|
||
if ((PopAction.Action == PowerActionShutdown) ||
|
||
(PopAction.Action == PowerActionShutdownReset) ||
|
||
(PopAction.Action == PowerActionShutdownOff)) {
|
||
|
||
//
|
||
// This is a shutdown.
|
||
//
|
||
PopAction.Shutdown = TRUE;
|
||
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Quick debug check. Our first sleep state must always be valid, ie
|
||
// validation doesn't change it.
|
||
//
|
||
#if DBG
|
||
if (QueryDevices && (PopAction.SystemState < PowerSystemShutdown)) {
|
||
|
||
SYSTEM_POWER_STATE TempSystemState;
|
||
|
||
TempSystemState = PopAction.SystemState;
|
||
PopVerifySystemPowerState(&TempSystemState, SubstituteLightestOverallDownwardBounded);
|
||
|
||
if ((TempSystemState != PopAction.SystemState) ||
|
||
(TempSystemState == PowerSystemWorking)) {
|
||
|
||
PopInternalError (POP_INFO);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If not success, abort SetSystemPowerState operation
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Only need the lock while updating PopAction.Action + Updates, and
|
||
// can not hold the lock while sending irps to device drivers
|
||
//
|
||
|
||
PopReleasePolicyLock(FALSE);
|
||
PolicyLockOwned = FALSE;
|
||
|
||
//
|
||
// Fish PopSimulate out of the registry so that it can
|
||
// modify some of our sleep/hiber behavior.
|
||
//
|
||
|
||
PopInitializePowerPolicySimulate();
|
||
|
||
//
|
||
// Dump any previous device state error
|
||
//
|
||
|
||
PopReportDevState (FALSE);
|
||
|
||
//
|
||
// What would be our next state to try?
|
||
//
|
||
PopAction.NextSystemState = PopAction.SystemState;
|
||
if (PopAction.Flags & POWER_ACTION_LIGHTEST_FIRST) {
|
||
|
||
//
|
||
// We started light, now we deepen our sleep state.
|
||
//
|
||
SubstitutionPolicy = SubstituteDeepenSleep;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We started deep, now we're lightening up.
|
||
//
|
||
SubstitutionPolicy = SubstituteLightenSleep;
|
||
}
|
||
|
||
PopAdvanceSystemPowerState(&PopAction.NextSystemState,
|
||
SubstitutionPolicy,
|
||
PopAction.LightestState,
|
||
DeepestSystemState);
|
||
|
||
//
|
||
// If allowed, query devices
|
||
//
|
||
|
||
PopAction.IrpMinor = IRP_MN_QUERY_POWER;
|
||
if (QueryDevices) {
|
||
|
||
//
|
||
// Issue query to devices
|
||
//
|
||
|
||
Status = PopSetDevicesSystemState (FALSE);
|
||
|
||
//
|
||
// If the last operation was a failure, but wasn't a total abort
|
||
// continue with next best state
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status) && Status != STATUS_CANCELLED) {
|
||
|
||
//
|
||
// Try next sleep state
|
||
//
|
||
PopAction.SystemState = PopAction.NextSystemState;
|
||
|
||
//
|
||
// If we're already exhausted all possible states, check
|
||
// if we need to continue regardless of the device failures.
|
||
//
|
||
|
||
if (PopAction.SystemState == PowerSystemWorking) {
|
||
|
||
if (PopAction.Flags & POWER_ACTION_CRITICAL) {
|
||
|
||
//
|
||
// It's critical. Stop querying and since the devices
|
||
// aren't particularly happy with any of the possible
|
||
// states, might as well use the max state
|
||
//
|
||
|
||
ASSERT( PopAction.Action != PowerActionWarmEject );
|
||
ASSERT( !(PopAction.Flags & POWER_ACTION_LIGHTEST_FIRST) );
|
||
|
||
QueryDevices = FALSE;
|
||
PopAction.SystemState = DeepestSystemState;
|
||
PopAction.Flags &= ~POWER_ACTION_LIGHTEST_FIRST;
|
||
|
||
} else {
|
||
|
||
//
|
||
// The query failure is final. Don't retry
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Try new settings
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If some error, start over
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Flush out any D irps on the queue. There shouldn't be any, but by
|
||
// setting LastCall == TRUE this also resets PopCallSystemState so that
|
||
// any D irps which occur as a side-effect of flushing the volumes get
|
||
// processed correctly.
|
||
//
|
||
PopSystemIrpDispatchWorker(TRUE);
|
||
|
||
//
|
||
// If this is a server and we are going into hibernation, write an entry
|
||
// into the eventlog. This allows for easy tracking of system downtime
|
||
// by searching the eventlog for hibernate/resume events.
|
||
//
|
||
if (RtlGetNtProductType(&NtProductType) &&
|
||
(NtProductType != NtProductWinNt) &&
|
||
(PopAction.SystemState == PowerSystemHibernate)) {
|
||
|
||
ErrLog = IoAllocateGenericErrorLogEntry(sizeof(IO_ERROR_LOG_PACKET));
|
||
if (ErrLog) {
|
||
|
||
//
|
||
// Fill it in and write it out
|
||
//
|
||
ErrLog->FinalStatus = STATUS_HIBERNATED;
|
||
ErrLog->ErrorCode = STATUS_HIBERNATED;
|
||
IoWriteErrorLogEntry(ErrLog);
|
||
WroteErrLog = TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Get hibernation context
|
||
//
|
||
|
||
|
||
Status = PopAllocateHiberContext ();
|
||
if (!NT_SUCCESS(Status) || (PopAction.Updates & (PO_PM_REISSUE | PO_PM_SETSTATE))) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// If boot status hasn't already been updated then do so now.
|
||
//
|
||
|
||
if(!BootStatusUpdated) {
|
||
|
||
if(PopAction.Shutdown) {
|
||
|
||
NTSTATUS bsdStatus;
|
||
HANDLE bsdHandle;
|
||
|
||
bsdStatus = RtlLockBootStatusData(&bsdHandle);
|
||
|
||
if(NT_SUCCESS(bsdStatus)) {
|
||
|
||
BOOLEAN t = TRUE;
|
||
|
||
RtlGetSetBootStatusData(bsdHandle,
|
||
FALSE,
|
||
RtlBsdItemBootShutdown,
|
||
&t,
|
||
sizeof(t),
|
||
NULL);
|
||
|
||
RtlUnlockBootStatusData(bsdHandle);
|
||
}
|
||
}
|
||
|
||
BootStatusUpdated = TRUE;
|
||
}
|
||
|
||
//
|
||
// If not already flushed, flush the volumes
|
||
//
|
||
|
||
if (!VolumesFlushed) {
|
||
VolumesFlushed = TRUE;
|
||
PopFlushVolumes ();
|
||
}
|
||
|
||
//
|
||
// Enter the SystemState
|
||
//
|
||
|
||
PopAction.IrpMinor = IRP_MN_SET_POWER;
|
||
if (PopAction.Shutdown) {
|
||
|
||
//
|
||
// Force reacquisition of the dev list. We will be telling Pnp
|
||
// to unload all possible devices, and therefore Pnp needs us to
|
||
// release the Pnp Engine Lock.
|
||
//
|
||
IoFreePoDeviceNotifyList(&PopAction.DevState->Order);
|
||
PopAction.DevState->GetNewDeviceList = TRUE;
|
||
|
||
//
|
||
// We shut down via a system worker thread so that the
|
||
// current active process will exit cleanly.
|
||
//
|
||
|
||
if (PsGetCurrentProcess() != PsInitialSystemProcess) {
|
||
|
||
ExInitializeWorkItem(&PopShutdownWorkItem,
|
||
&PopGracefulShutdown,
|
||
NULL);
|
||
|
||
ExQueueWorkItem(&PopShutdownWorkItem,
|
||
PO_SHUTDOWN_QUEUE);
|
||
|
||
// Clean up in prep for wait...
|
||
ASSERT(!PolicyLockOwned);
|
||
|
||
//
|
||
// If we acquired the timer refresh lock (can happen if we promoted to shutdown)
|
||
// then we need to release it so that suspend actually suspends.
|
||
//
|
||
if (TimerRefreshLockOwned) {
|
||
ExReleaseTimeRefreshLock();
|
||
}
|
||
|
||
// And sleep until we're terminated.
|
||
|
||
// Note that we do NOT clean up the dev state -- it's now
|
||
// owned by the shutdown worker thread.
|
||
|
||
// Note that we also do not unlock the pagable image
|
||
// section referred to by ExPageLockHandle -- this keeps
|
||
// all of our shutdown code in memory.
|
||
|
||
KeSuspendThread(KeGetCurrentThread());
|
||
|
||
return STATUS_SYSTEM_SHUTDOWN;
|
||
} else {
|
||
PopGracefulShutdown (NULL);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the timer refresh lock to hold off automated time of day
|
||
// adjustments. On wake the time will be explicitly reset from Cmos
|
||
//
|
||
|
||
if (!TimerRefreshLockOwned) {
|
||
TimerRefreshLockOwned = TRUE;
|
||
ExAcquireTimeRefreshLock(TRUE);
|
||
}
|
||
|
||
// This is where PopAllocateHiberContext used to be before bug #212420
|
||
|
||
//
|
||
// If there's a Doze to S4 timeout set, and this wasn't an S4 action
|
||
// and the system can support and S4 state, set a timer for the doze time
|
||
//
|
||
// N.B. this must be set before the paging devices are turned off
|
||
//
|
||
|
||
if (S4DozeObject) {
|
||
S4DozeObject = NULL;
|
||
NtClose (S4DozeTimer);
|
||
}
|
||
|
||
if (PopPolicy->DozeS4Timeout &&
|
||
!S4DozeObject &&
|
||
PopAction.SystemState != PowerSystemHibernate &&
|
||
SystemAction != PowerActionHibernate &&
|
||
PopCapabilities.SystemS4 &&
|
||
PopCapabilities.SystemS5 &&
|
||
PopCapabilities.HiberFilePresent) {
|
||
|
||
//
|
||
// Create a timer to wake the machine up when we need to hibernate
|
||
//
|
||
|
||
InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL);
|
||
|
||
Status2 = NtCreateTimer (
|
||
&S4DozeTimer,
|
||
TIMER_ALL_ACCESS,
|
||
&ObjectAttributes,
|
||
NotificationTimer
|
||
);
|
||
|
||
if (NT_SUCCESS(Status2)) {
|
||
|
||
//
|
||
// Get the timer object for this timer
|
||
//
|
||
|
||
Status2 = ObReferenceObjectByHandle (
|
||
S4DozeTimer,
|
||
TIMER_ALL_ACCESS,
|
||
NULL,
|
||
KernelMode,
|
||
&S4DozeObject,
|
||
NULL
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(Status2));
|
||
ObDereferenceObject(S4DozeObject);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Inform drivers of the system sleeping state
|
||
//
|
||
|
||
Status = PopSetDevicesSystemState (FALSE);
|
||
if (!NT_SUCCESS(Status)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Drivers have been informed, this operation is now committed,
|
||
// get the next wakeup time
|
||
//
|
||
|
||
if (!(PopAction.Flags & POWER_ACTION_DISABLE_WAKES)) {
|
||
//
|
||
// Set S4Doze wakeup timer
|
||
//
|
||
|
||
if (S4DozeObject) {
|
||
DueTime.QuadPart = -(LONGLONG) (US2SEC*US2TIME) * PopPolicy->DozeS4Timeout;
|
||
NtSetTimer(S4DozeTimer, &DueTime, NULL, NULL, TRUE, 0, NULL);
|
||
}
|
||
|
||
ExGetNextWakeTime(&WakeTime, &WakeTimeFields, &WakeTimerObject);
|
||
}
|
||
|
||
//
|
||
// Only enable RTC wake if the system is going to an S-state that
|
||
// supports the RTC wake.
|
||
//
|
||
if (PopCapabilities.RtcWake != PowerSystemUnspecified &&
|
||
PopCapabilities.RtcWake >= PopAction.SystemState &&
|
||
WakeTime) {
|
||
|
||
#if DBG
|
||
ULONGLONG InterruptTime;
|
||
|
||
InterruptTime = KeQueryInterruptTime();
|
||
PoPrint (PO_PACT, ("Wake alarm set%s: %d:%02d:%02d %d (%d seconds from now)\n",
|
||
WakeTimerObject == S4DozeObject ? " for s4doze" : "",
|
||
WakeTimeFields.Hour,
|
||
WakeTimeFields.Minute,
|
||
WakeTimeFields.Second,
|
||
WakeTimeFields.Year,
|
||
(WakeTime - InterruptTime) / (US2TIME * US2SEC)
|
||
));
|
||
#endif
|
||
HalSetWakeEnable(TRUE);
|
||
HalSetWakeAlarm(WakeTime, &WakeTimeFields);
|
||
|
||
} else {
|
||
|
||
HalSetWakeEnable(TRUE);
|
||
HalSetWakeAlarm( 0, NULL );
|
||
|
||
}
|
||
|
||
//
|
||
// Capture the last sleep time.
|
||
//
|
||
SleepTime = KeQueryInterruptTime();
|
||
|
||
//
|
||
// Implement system handler for sleep operation
|
||
//
|
||
|
||
Status = PopSleepSystem (PopAction.SystemState,
|
||
PopAction.HiberContext);
|
||
//
|
||
// A sleep or shutdown operation attempt was performed, clean up
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the system slept successfully, update the system time to
|
||
// match the CMOS clock.
|
||
//
|
||
if (NT_SUCCESS(Status)) {
|
||
PopAction.SleepTime = SleepTime;
|
||
ASSERT(TimerRefreshLockOwned);
|
||
ExUpdateSystemTimeFromCmos (TRUE, 1);
|
||
|
||
PERFINFO_HIBER_START_LOGGING();
|
||
}
|
||
|
||
//
|
||
// If DevState was allocated, notify drivers the system is awake
|
||
//
|
||
|
||
if (PopAction.DevState) {
|
||
|
||
//
|
||
// Log any failures
|
||
//
|
||
|
||
PopReportDevState (TRUE);
|
||
|
||
//
|
||
// Notify drivers that the system is now running
|
||
//
|
||
PopSetDevicesSystemState (TRUE);
|
||
|
||
}
|
||
|
||
//
|
||
// Free the device notify list. This must be done before acquiring
|
||
// the policy lock, otherwise we can deadlock with the PNP device
|
||
// tree lock.
|
||
//
|
||
ASSERT(PopAction.DevState != NULL);
|
||
IoFreePoDeviceNotifyList(&PopAction.DevState->Order);
|
||
|
||
//
|
||
// Get the policy lock for the rest of the cleanup
|
||
//
|
||
|
||
if (!PolicyLockOwned) {
|
||
PopAcquirePolicyLock ();
|
||
PolicyLockOwned = TRUE;
|
||
}
|
||
|
||
//
|
||
// Cleanup DevState
|
||
//
|
||
PopCleanupDevState ();
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Now that the time has been fixed, record the last state
|
||
// the system has awoken from and the current time
|
||
//
|
||
|
||
PopAction.LastWakeState = PopAction.SystemState;
|
||
PopAction.WakeTime = KeQueryInterruptTime();
|
||
|
||
//
|
||
// If full wake hasn't been signalled, then set then start a
|
||
// really agressive idle detection
|
||
//
|
||
|
||
if (!AnyBitsSet (PopFullWake, PO_FULL_WAKE_STATUS | PO_FULL_WAKE_PENDING)) {
|
||
|
||
//
|
||
// If there was an S4Doze timer set, check to see if it's
|
||
// expired and update the idle detection to enter S4
|
||
//
|
||
|
||
if (S4DozeObject) {
|
||
|
||
NtQueryTimer (S4DozeTimer,
|
||
TimerBasicInformation,
|
||
&TimerInformation,
|
||
sizeof (TimerInformation),
|
||
NULL);
|
||
|
||
if (TimerInformation.TimerState) {
|
||
|
||
//
|
||
// Update the idle detection action to be hibernate
|
||
//
|
||
|
||
PoPrint (PO_PACT, ("Wake with S4 timer expired\n"));
|
||
|
||
//
|
||
// If the s4timer was the alarm time, and we're awake
|
||
// in under the idle reenter time, just drop right into
|
||
// S4 without any idle detection. (we check the current
|
||
// time in case in case the alarm time expired but for
|
||
// some reason the system did not wake at that time)
|
||
//
|
||
|
||
if ((WakeTimerObject == S4DozeObject) &&
|
||
(PopAction.WakeTime - WakeTime <
|
||
SYS_IDLE_REENTER_TIMEOUT * US2TIME * US2SEC)) {
|
||
|
||
PopAction.Action = PowerActionSleep;
|
||
PopAction.LightestState = PowerSystemHibernate;
|
||
PopAction.Updates |= PO_PM_REISSUE;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Set the system idle detection code to re-enter this state
|
||
// real agressively (assuming a full wake doesn't happen)
|
||
//
|
||
|
||
PopInitSIdle ();
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free anything that's left of the hiber context
|
||
//
|
||
|
||
PopFreeHiberContext (TRUE);
|
||
|
||
//
|
||
// Clear out PopAction unless we have promoted directly to hibernate
|
||
//
|
||
if ((PopAction.Updates & PO_PM_REISSUE) == 0) {
|
||
PopResetActionDefaults();
|
||
}
|
||
|
||
//
|
||
// We are no longer active
|
||
// We don't check for work here as this may be "the thread" from winlogon.
|
||
// So we explicitly queue pending policy work off to a worker thread below
|
||
// after setting the win32k wake notifications.
|
||
//
|
||
|
||
PopAction.State = PO_ACT_CALLOUT;
|
||
PopReleasePolicyLock (FALSE);
|
||
|
||
//
|
||
// If there's been some sort of error, make sure gdi is enabled
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
PopDisplayRequired (0);
|
||
}
|
||
|
||
//
|
||
// If some win32k wake event is pending, tell win32k
|
||
//
|
||
|
||
if (PopFullWake & PO_FULL_WAKE_PENDING) {
|
||
PopSetNotificationWork (PO_NOTIFY_FULL_WAKE);
|
||
} else if (PopFullWake & PO_GDI_ON_PENDING) {
|
||
PopSetNotificationWork (PO_NOTIFY_DISPLAY_REQUIRED);
|
||
}
|
||
|
||
//
|
||
// If the timer refresh lock was acquired, release it
|
||
//
|
||
|
||
if (TimerRefreshLockOwned) {
|
||
ExReleaseTimeRefreshLock();
|
||
} else {
|
||
//
|
||
// try to catch weird case where we exit this routine with the
|
||
// time refresh lock held.
|
||
//
|
||
ASSERT(!ExIsResourceAcquiredExclusive(&ExpTimeRefreshLock));
|
||
}
|
||
|
||
//
|
||
// Unlock pageable code. The unlock is queued off to a delayed worker queue
|
||
// since it is likely to block on pagable code, registry, etc. The PopUnlockComplete
|
||
// event is used to prevent the unlock from racing with a subsequent lock.
|
||
//
|
||
ExQueueWorkItem(&PopUnlockAfterSleepWorkItem, DelayedWorkQueue);
|
||
|
||
//
|
||
// If a timer for s4 dozing was allocated, close it
|
||
//
|
||
|
||
if (S4DozeObject) {
|
||
NtClose (S4DozeTimer);
|
||
}
|
||
|
||
//
|
||
// If we wrote an errlog message indicating that we were hibernating, write a corresponding
|
||
// one to indicate we have woken.
|
||
//
|
||
if (WroteErrLog) {
|
||
|
||
ErrLog = IoAllocateGenericErrorLogEntry(sizeof(IO_ERROR_LOG_PACKET));
|
||
if (ErrLog) {
|
||
|
||
//
|
||
// Fill it in and write it out
|
||
//
|
||
ErrLog->FinalStatus = STATUS_RESUME_HIBERNATION;
|
||
ErrLog->ErrorCode = STATUS_RESUME_HIBERNATION;
|
||
IoWriteErrorLogEntry(ErrLog);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, we can revert the throttle back to a normal value
|
||
//
|
||
PopSetPerfFlag( PSTATE_DISABLE_THROTTLE_NTAPI, TRUE );
|
||
PopUpdateAllThrottles();
|
||
|
||
//
|
||
// Done - kick off the policy worker thread to process any outstanding work in
|
||
// a worker thread.
|
||
//
|
||
PopCheckForWork(TRUE);
|
||
//
|
||
// enable registry's lazzy flusher
|
||
//
|
||
CmSetLazyFlushState(TRUE);
|
||
|
||
//
|
||
// try to catch weird case where we exit this routine with the
|
||
// time refresh lock held.
|
||
//
|
||
ASSERT(!ExIsResourceAcquiredExclusive(&ExpTimeRefreshLock));
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSYSAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
NtRequestDeviceWakeup(
|
||
IN HANDLE Device
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine requests a WAIT_WAKE Irp on the specified handle.
|
||
|
||
If the handle is to a device object, the WAIT_WAKE irp is sent
|
||
to the top of that device's stack.
|
||
|
||
If a WAIT_WAKE is already outstanding on the device, this routine
|
||
increments the WAIT_WAKE reference count and return success.
|
||
|
||
Arguments:
|
||
|
||
Device - Supplies the device which should wake the system
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
PDEVICE_OBJECT targetDevice;
|
||
NTSTATUS status;
|
||
PDEVICE_OBJECT_POWER_EXTENSION dope;
|
||
|
||
//
|
||
// Reference the file object in order to get to the device object
|
||
// in question.
|
||
//
|
||
status = ObReferenceObjectByHandle(Device,
|
||
0L,
|
||
IoFileObjectType,
|
||
KeGetPreviousMode(),
|
||
(PVOID *)&fileObject,
|
||
NULL);
|
||
if (!NT_SUCCESS(status)) {
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Get the address of the target device object.
|
||
//
|
||
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
||
deviceObject = IoGetAttachedDeviceReference( IoGetRelatedDeviceObject( fileObject ));
|
||
} else {
|
||
deviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
||
}
|
||
|
||
//
|
||
// Now that we have the device object, we are done with the file object
|
||
//
|
||
ObDereferenceObject(fileObject);
|
||
|
||
ObDereferenceObject(deviceObject);
|
||
return (STATUS_NOT_IMPLEMENTED);
|
||
}
|
||
|
||
|
||
NTSYSAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
NtCancelDeviceWakeupRequest(
|
||
IN HANDLE Device
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cancels a WAIT_WAKE irp sent to a device previously
|
||
with NtRequestDeviceWakeup.
|
||
|
||
The WAIT_WAKE reference count on the device is decremented. If this
|
||
count goes to zero, the WAIT_WAKE irp is cancelled.
|
||
|
||
Arguments:
|
||
|
||
Device - Supplies the device which should wake the system
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
return(STATUS_NOT_IMPLEMENTED);
|
||
}
|
||
|
||
|
||
NTSYSAPI
|
||
BOOLEAN
|
||
NTAPI
|
||
NtIsSystemResumeAutomatic(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns whether or not the most recent wake was automatic
|
||
or due to a user action.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE - The system was awakened due to a timer or device wake
|
||
|
||
FALSE - The system was awakened due to a user action
|
||
|
||
--*/
|
||
|
||
{
|
||
if (AnyBitsSet(PopFullWake, PO_FULL_WAKE_STATUS | PO_FULL_WAKE_PENDING)) {
|
||
return(FALSE);
|
||
} else {
|
||
return(TRUE);
|
||
}
|
||
}
|
||
|
||
|
||
NTSYSAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
NtGetDevicePowerState(
|
||
IN HANDLE Device,
|
||
OUT DEVICE_POWER_STATE *State
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Queries the current power state of a device.
|
||
|
||
Arguments:
|
||
|
||
Device - Supplies the handle to a device.
|
||
|
||
State - Returns the current power state of the device.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
NTSTATUS status;
|
||
PDEVOBJ_EXTENSION doe;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
DEVICE_POWER_STATE dev_state;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Verify caller's parameter
|
||
//
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
try {
|
||
ProbeForWriteUlong((PULONG)State);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
status = GetExceptionCode();
|
||
return(status);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Reference the file object in order to get to the device object
|
||
// in question.
|
||
//
|
||
status = ObReferenceObjectByHandle(Device,
|
||
0L,
|
||
IoFileObjectType,
|
||
KeGetPreviousMode(),
|
||
(PVOID *)&fileObject,
|
||
NULL);
|
||
if (!NT_SUCCESS(status)) {
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Get the address of the target device object.
|
||
//
|
||
status = IoGetRelatedTargetDevice(fileObject, &deviceObject);
|
||
|
||
//
|
||
// Now that we have the device object, we are done with the file object
|
||
//
|
||
ObDereferenceObject(fileObject);
|
||
if (!NT_SUCCESS(status)) {
|
||
return(status);
|
||
}
|
||
|
||
doe = deviceObject->DeviceObjectExtension;
|
||
dev_state = PopLockGetDoDevicePowerState(doe);
|
||
try {
|
||
*State = dev_state;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
status = GetExceptionCode();
|
||
}
|
||
|
||
ObDereferenceObject(deviceObject);
|
||
return (status);
|
||
}
|
||
|