|
|
/*++
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); }
|