|
|
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
sysdev.c
Abstract:
This module interfaces to the system power state IRPs for devices
Author:
Ken Reneris (kenr) 17-Jan-1997
Revision History:
--*/
#include "pop.h"
//
// External used to determine if the device tree has changed between
// passes of informing devices of a system power state
//
extern ULONG IoDeviceNodeTreeSequence;
//
// Internal prototypes
//
VOID PopSleepDeviceList ( IN PPOP_DEVICE_SYS_STATE DevState, IN PPO_NOTIFY_ORDER_LEVEL Level );
VOID PopWakeDeviceList ( IN PPOP_DEVICE_SYS_STATE DevState, IN PPO_NOTIFY_ORDER_LEVEL Level );
VOID PopNotifyDevice ( IN PPOP_DEVICE_SYS_STATE DevState, IN PPO_DEVICE_NOTIFY Notify );
VOID PopWaitForSystemPowerIrp ( IN PPOP_DEVICE_SYS_STATE DevState, IN BOOLEAN WaitForAll );
NTSTATUS PopCompleteSystemPowerIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );
BOOLEAN PopCheckSystemPowerIrpStatus ( IN PPOP_DEVICE_SYS_STATE DevState, IN PIRP Irp, IN BOOLEAN AllowTestFailure );
VOID PopDumpSystemIrp ( IN PCHAR Desc, IN PPOP_DEVICE_POWER_IRP PowerIrp );
VOID PopResetChildCount( IN PLIST_ENTRY ListHead );
VOID PopSetupListForWake( IN PPO_NOTIFY_ORDER_LEVEL Level, IN PLIST_ENTRY ListHead );
VOID PopWakeSystemTimeout( IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGELK, PopSetDevicesSystemState)
#pragma alloc_text(PAGELK, PopWakeDeviceList)
#pragma alloc_text(PAGELK, PopSleepDeviceList)
#pragma alloc_text(PAGELK, PopResetChildCount)
#pragma alloc_text(PAGELK, PopSetupListForWake)
#pragma alloc_text(PAGELK, PopNotifyDevice)
#pragma alloc_text(PAGELK, PopWaitForSystemPowerIrp)
#pragma alloc_text(PAGELK, PopCompleteSystemPowerIrp)
#pragma alloc_text(PAGELK, PopCheckSystemPowerIrpStatus)
#pragma alloc_text(PAGELK, PopCleanupDevState)
#pragma alloc_text(PAGELK, PopRestartSetSystemState)
#pragma alloc_text(PAGELK, PopReportDevState)
#pragma alloc_text(PAGELK, PopDumpSystemIrp)
#pragma alloc_text(PAGELK, PopWakeSystemTimeout)
#pragma alloc_text(PAGE, PopAllocateDevState)
#endif
ULONG PopCurrentLevel=0; LONG PopWakeTimer = 1; KTIMER PopWakeTimeoutTimer; KDPC PopWakeTimeoutDpc;
NTSTATUS PopSetDevicesSystemState ( IN BOOLEAN Wake ) /*++
Routine Description:
Sends a system power irp of IrpMinor and SystemState from PopAction to all devices.
N.B. Function is not re-entrant. N.B. Policy lock must be held. This function releases and reacquires the policy lock.
Arguments:
Wake - TRUE if a transition to S0 should be broadcast to all drivers. FALSE if the appropriate sleep transition can be found in PopAction.DevState
Return Value:
Status. SUCCESS - all devices contacted without any errors. CANCELLED - operation was aborted. Error - error code of first failure. All failed IRPs and related device objects are on the Failed list.
--*/ { LONG i; BOOLEAN NotifyGdi; BOOLEAN DidIoMmShutdown = FALSE; PPO_DEVICE_NOTIFY NotifyDevice; PLIST_ENTRY Link; PPOP_DEVICE_POWER_IRP PowerIrp; POWER_ACTION powerOperation; PPOP_DEVICE_SYS_STATE DevState; PDEVICE_OBJECT DeviceObject;
ASSERT(PopAction.DevState ); DevState = PopAction.DevState;
//
// Intialize DevState for this pass
//
DevState->IrpMinor = PopAction.IrpMinor; DevState->SystemState = PopAction.SystemState; DevState->Status = STATUS_SUCCESS; DevState->FailedDevice = NULL; DevState->Cancelled = FALSE; DevState->IgnoreErrors = FALSE; DevState->IgnoreNotImplemented = FALSE; DevState->Waking = Wake; NotifyGdi = FALSE;
if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_SET_DEVICES_STATE LogEntry;
LogEntry.SystemState = (ULONG) DevState->SystemState; LogEntry.IrpMinor = PopAction.IrpMinor; LogEntry.Waking = Wake; LogEntry.Shutdown = PopAction.Shutdown;
PerfInfoLogBytes(PERFINFO_LOG_TYPE_SET_DEVICES_STATE, &LogEntry, sizeof(LogEntry)); }
//
// If this is a set operation, and the Gdi state is on then we need to
// notify gdi of the set power operation
//
if (PopAction.IrpMinor == IRP_MN_SET_POWER && AnyBitsSet (PopFullWake, PO_FULL_WAKE_STATUS | PO_GDI_STATUS)) {
NotifyGdi = TRUE; }
//
// If the request is for Query on a shutdown operarion, ignore any
// drivers which don't implment it. If it's for a Set on a
// shutdown operation, ignore any errors - the system is going to
// shutdown
//
if (PopAction.Shutdown) { DevState->IgnoreNotImplemented = TRUE; if (PopAction.IrpMinor == IRP_MN_SET_POWER) { DevState->IgnoreErrors = TRUE; } }
//
// This function is not re-entrant, and the operation has been
// serialized before here
//
ASSERT (DevState->Thread == KeGetCurrentThread());
//
// Notify all devices.
//
if (!Wake) {
//
// If it's time to update the device list, then do so
//
if (DevState->GetNewDeviceList) { DevState->GetNewDeviceList = FALSE; IoFreePoDeviceNotifyList (&DevState->Order); DevState->Status = IoBuildPoDeviceNotifyList (&DevState->Order); } else { //
// Reset the active child count of each notification
//
for (i=0;i<=PO_ORDER_MAXIMUM;i++) { PopResetChildCount(&DevState->Order.OrderLevel[i].WaitSleep); PopResetChildCount(&DevState->Order.OrderLevel[i].ReadySleep); PopResetChildCount(&DevState->Order.OrderLevel[i].ReadyS0); PopResetChildCount(&DevState->Order.OrderLevel[i].WaitS0); PopResetChildCount(&DevState->Order.OrderLevel[i].Complete); } }
if (NT_SUCCESS(DevState->Status)) {
//
// Notify all devices of operation in forward order. Wait between each level.
//
for (i=PO_ORDER_MAXIMUM; i >= 0; i--) {
//
// Notify this list
//
if (DevState->Order.OrderLevel[i].DeviceCount) {
if ((NotifyGdi) && (i <= PO_ORDER_GDI_NOTIFICATION)) {
NotifyGdi = FALSE; InterlockedExchange (&PopFullWake, 0); if (PopEventCallout) {
//
// Turn off the special system irp dispatcher here
// as when we call into GDI it is going to block on its
// D irp and we are not going to get control back.
//
PopSystemIrpDispatchWorker(TRUE); PopEventCalloutDispatch (PsW32GdiOff, DevState->SystemState); } }
//
// If we're shutting down and if we're done
// notifying paged devices, shut down filesystems
// and MM to free up all resources on the paging
// path (which we should no longer need).
//
if (PopAction.Shutdown && !DidIoMmShutdown && (PopAction.IrpMinor == IRP_MN_SET_POWER) && (i < PO_ORDER_PAGABLE)) {
//
// Disable crash-dump stack. From this point on, we
// will get no logs.
//
IoConfigureCrashDump(CrashDumpDisable);
//
// At this point, we are about to drop the final refs
// on the pagefile file-objects. Since close IRPs go
// through pagable code, there should be no open handles
// in the system process at this point (note: we don't
// look for file objects without handles, the pagefiles
// being among them.)
//
if (PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_OB) { ObShutdownSystem (1); }
//
// ISSUE - 2002/02/20 - ADRIAO: Unwind of final closes
// We should lock down close paths, so that unwind
// from closing the paging file will work properly.
// This is one reason why the clean shutdown code isn't
// enabled by default.
//
//
// Memory management will drop the pagefile file-object
// references, in theory allowing the filesystem stack
// to unload.
//
// NO MORE REFERENCES TO PAGABLE CODE OR DATA MAY BE
// MADE!
//
MmShutdownSystem(1);
DidIoMmShutdown = TRUE; }
//
// Remove the warm eject node if we might have gotten here
// without a query.
//
if (PopAction.Flags & POWER_ACTION_CRITICAL) {
*DevState->Order.WarmEjectPdoPointer = NULL; }
//
// Notify this list
//
PopCurrentLevel = i; PopSleepDeviceList (DevState, &DevState->Order.OrderLevel[i]); PopWaitForSystemPowerIrp (DevState, TRUE); }
//
// If there's been an error, stop and issue wakes to all devices
//
if (!NT_SUCCESS(DevState->Status)) { Wake = TRUE; if ((DevState->FailedDevice != NULL) && (PopAction.NextSystemState == PowerSystemWorking)) {
powerOperation = PopMapInternalActionToIrpAction( PopAction.Action, DevState->SystemState, FALSE );
if (powerOperation == PowerActionWarmEject) { DeviceObject = *DevState->Order.WarmEjectPdoPointer; } else { DeviceObject = NULL; }
IoNotifyPowerOperationVetoed( powerOperation, DeviceObject, DevState->FailedDevice ); } break; } } } //
// This will cause us to wake up all the devices after putting
// them to sleep. Useful for test automation.
//
if ((PopSimulate & POP_WAKE_DEVICE_AFTER_SLEEP) && (PopAction.IrpMinor == IRP_MN_SET_POWER)) { DbgPrint ("po: POP_WAKE_DEVICE_AFTER_SLEEP enabled.\n"); Wake = TRUE; DevState->Status = STATUS_UNSUCCESSFUL; } }
//
// Verify we shut down everything
//
ASSERT((!PopAction.Shutdown) || (PopAction.IrpMinor != IRP_MN_SET_POWER) || DidIoMmShutdown);
//
// Some debugging code here. If the debug flag is set, then loop on failed
// devices and continue to retry them. This will allow someone to step
// them through the driver stack to determine where the failure is.
//
while ((PopSimulate & POP_LOOP_ON_FAILED_DRIVERS) && !IsListEmpty(&PopAction.DevState->Head.Failed)) {
Link = PopAction.DevState->Head.Failed.Flink; RemoveEntryList(Link);
PowerIrp = CONTAINING_RECORD (Link, POP_DEVICE_POWER_IRP, Failed); PopDumpSystemIrp ("Retry", PowerIrp);
IoFreeIrp (PowerIrp->Irp); NotifyDevice = PowerIrp->Notify;
PowerIrp->Irp = NULL; PowerIrp->Notify = NULL;
PushEntryList ( &PopAction.DevState->Head.Free, &PowerIrp->Free );
DbgBreakPoint (); PopNotifyDevice (DevState, NotifyDevice); PopWaitForSystemPowerIrp (DevState, TRUE); }
//
// If waking, send set power to the working state to all devices which where
// send something else
//
DevState->Waking = Wake; if (DevState->Waking) {
DevState->IgnoreErrors = TRUE; DevState->IrpMinor = IRP_MN_SET_POWER; DevState->SystemState = PowerSystemWorking;
//
// Notify all devices of the wake operation in reverse (level) order.
//
KeInitializeTimer(&PopWakeTimeoutTimer); KeInitializeDpc(&PopWakeTimeoutDpc, PopWakeSystemTimeout, NULL);
for (i=0; i <= PO_ORDER_MAXIMUM; i++) { PopCurrentLevel = i; PopWakeDeviceList (DevState, &DevState->Order.OrderLevel[i]);
PopWaitForSystemPowerIrp (DevState, TRUE); if (PopSimulate & POP_WAKE_DEADMAN) { KeCancelTimer(&PopWakeTimeoutTimer); } }
// restore
DevState->IrpMinor = PopAction.IrpMinor; DevState->SystemState = PopAction.SystemState; }
//
// Done
//
if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_SET_DEVICES_STATE_RET LogEntry;
LogEntry.Status = DevState->Status;
PerfInfoLogBytes(PERFINFO_LOG_TYPE_SET_DEVICES_STATE_RET, &LogEntry, sizeof(LogEntry)); }
return DevState->Status; }
VOID PopReportDevState ( IN BOOLEAN LogErrors ) /*++
Routine Description:
Verifies that the DevState structure is idle
Arguments:
None
Return Value:
None
--*/ { PIRP Irp; PLIST_ENTRY Link; PPOP_DEVICE_POWER_IRP PowerIrp; PIO_ERROR_LOG_PACKET ErrLog;
if (!PopAction.DevState) { return ; }
//
// Cleanup any irps on the failed list
//
while (!IsListEmpty(&PopAction.DevState->Head.Failed)) { Link = PopAction.DevState->Head.Failed.Flink; RemoveEntryList(Link);
PowerIrp = CONTAINING_RECORD (Link, POP_DEVICE_POWER_IRP, Failed); Irp = PowerIrp->Irp;
PopDumpSystemIrp ( LogErrors ? "Abort" : "fyi", PowerIrp );
if (LogErrors) { ErrLog = IoAllocateErrorLogEntry ( PowerIrp->Notify->TargetDevice->DriverObject, ERROR_LOG_MAXIMUM_SIZE );
if (ErrLog) { RtlZeroMemory (ErrLog, sizeof (*ErrLog)); ErrLog->FinalStatus = Irp->IoStatus.Status; ErrLog->DeviceOffset.QuadPart = Irp->IoStatus.Information; ErrLog->MajorFunctionCode = IRP_MJ_POWER; ErrLog->UniqueErrorValue = (PopAction.DevState->IrpMinor << 16) | PopAction.DevState->SystemState; ErrLog->ErrorCode = IO_SYSTEM_SLEEP_FAILED; IoWriteErrorLogEntry (ErrLog); } }
IoFreeIrp (Irp); PowerIrp->Irp = NULL; PowerIrp->Notify = NULL;
PushEntryList ( &PopAction.DevState->Head.Free, &PowerIrp->Free ); }
//
// Errors have been purged, we can now allocate a new device notification list if needed
//
if (PopAction.DevState->Order.DevNodeSequence != IoDeviceNodeTreeSequence) { PopAction.DevState->GetNewDeviceList = TRUE; } }
VOID PopAllocateDevState( VOID ) /*++
Routine Description:
Allocates and initialies the DevState structure.
Arguments:
None
Return Value:
PopAction.DevState != NULL if successful. PopAction.DevState == NULL otherwise.
--*/
{ PPOP_DEVICE_SYS_STATE DevState; ULONG i;
PAGED_CODE();
ASSERT(PopAction.DevState == NULL);
//
// Allocate a device state structure
//
DevState = (PPOP_DEVICE_SYS_STATE) ExAllocatePoolWithTag(NonPagedPool, sizeof (POP_DEVICE_SYS_STATE), POP_PDSS_TAG); if (!DevState) { PopAction.DevState = NULL; return; }
RtlZeroMemory (DevState, sizeof(POP_DEVICE_SYS_STATE)); DevState->Thread = KeGetCurrentThread(); DevState->GetNewDeviceList = TRUE;
KeInitializeSpinLock (&DevState->SpinLock); KeInitializeEvent (&DevState->Event, SynchronizationEvent, FALSE);
DevState->Head.Free.Next = NULL; InitializeListHead (&DevState->Head.Pending); InitializeListHead (&DevState->Head.Complete); InitializeListHead (&DevState->Head.Abort); InitializeListHead (&DevState->Head.Failed); InitializeListHead (&DevState->PresentIrpQueue);
for (i=0; i < MAX_SYSTEM_POWER_IRPS; i++) { DevState->PowerIrpState[i].Irp = NULL; PushEntryList (&DevState->Head.Free, &DevState->PowerIrpState[i].Free); }
for (i=0; i <= PO_ORDER_MAXIMUM; i++) { KeInitializeEvent(&DevState->Order.OrderLevel[i].LevelReady, NotificationEvent, FALSE); InitializeListHead(&DevState->Order.OrderLevel[i].WaitSleep); InitializeListHead(&DevState->Order.OrderLevel[i].ReadySleep); InitializeListHead(&DevState->Order.OrderLevel[i].Pending); InitializeListHead(&DevState->Order.OrderLevel[i].Complete); InitializeListHead(&DevState->Order.OrderLevel[i].ReadyS0); InitializeListHead(&DevState->Order.OrderLevel[i].WaitS0); }
PopAction.DevState = DevState;
}
VOID PopCleanupDevState ( VOID ) /*++
Routine Description:
Verifies that the DevState structure is idle
Arguments:
None
Return Value:
None
--*/ { //
// Notify power irp code that the device system state irps
// are done
//
PopSystemIrpDispatchWorker (TRUE);
//
// Verify all lists are empty
//
ASSERT(IsListEmpty(&PopAction.DevState->Head.Pending) && IsListEmpty(&PopAction.DevState->Head.Complete) && IsListEmpty(&PopAction.DevState->Head.Abort) && IsListEmpty(&PopAction.DevState->Head.Failed) && IsListEmpty(&PopAction.DevState->PresentIrpQueue));
ExFreePool (PopAction.DevState); PopAction.DevState = NULL; }
#define STATE_DONE_WAITING 0
#define STATE_COMPLETE_IRPS 1
#define STATE_PRESENT_PAGABLE_IRPS 2
#define STATE_CHECK_CANCEL 3
#define STATE_WAIT_NOW 4
VOID PopWaitForSystemPowerIrp ( IN PPOP_DEVICE_SYS_STATE DevState, IN BOOLEAN WaitForAll ) /*++
Routine Description:
Called to wait for one or more system power irps to complete. Handles final processing of any completed irp.
Arguments:
DevState - Current DevState structure
WaitForAll - If TRUE all outstanding IRPs are waited for, else any outstanding irp will do
Return Value:
None
--*/ { KIRQL OldIrql; ULONG State; BOOLEAN IrpCompleted; PIRP Irp; PLIST_ENTRY Link; PPOP_DEVICE_POWER_IRP PowerIrp; PPO_DEVICE_NOTIFY Notify; NTSTATUS Status; LARGE_INTEGER Timeout;
IrpCompleted = FALSE; KeAcquireSpinLock (&DevState->SpinLock, &OldIrql);
//
// Signal completion function that we are waiting
//
State = STATE_COMPLETE_IRPS; while (State != STATE_DONE_WAITING) { switch (State) { case STATE_COMPLETE_IRPS: //
// Assume we're going to advance to the next state
//
State += 1;
//
// If there arn't any irps on the complete list, move to the
// next state
//
if (IsListEmpty(&DevState->Head.Complete)) { break; }
//
// Handle the completed irps
//
IrpCompleted = TRUE; while (!IsListEmpty(&DevState->Head.Complete)) { Link = DevState->Head.Complete.Flink; RemoveEntryList(Link);
PowerIrp = CONTAINING_RECORD (Link, POP_DEVICE_POWER_IRP, Complete); Notify = PowerIrp->Notify; PowerIrp->Complete.Flink = NULL; Irp = PowerIrp->Irp;
//
// Verify the device driver called PoStartNextPowerIrp
//
if ((Notify->TargetDevice->DeviceObjectExtension->PowerFlags & POPF_SYSTEM_ACTIVE) || (Notify->DeviceObject->DeviceObjectExtension->PowerFlags & POPF_SYSTEM_ACTIVE)) { PDEVICE_OBJECT DeviceObject = Notify->DeviceObject; KeReleaseSpinLock (&DevState->SpinLock, OldIrql); PopDumpSystemIrp ("SYS STATE", PowerIrp); PopInternalAddToDumpFile ( NULL, 0, DeviceObject, NULL, NULL, NULL ); PopInternalAddToDumpFile ( NULL, 0, Notify->TargetDevice, NULL, NULL, NULL ); KeBugCheckEx ( DRIVER_POWER_STATE_FAILURE, 0x500, DEVICE_SYSTEM_STATE_HUNG, (ULONG_PTR) Notify->TargetDevice, (ULONG_PTR) DeviceObject ); }
//
// If success, or cancelled, or not implemented that's OK, then
// the irp is complete
//
if (PopCheckSystemPowerIrpStatus(DevState, Irp, TRUE)) { //
// See if IRP is failure being allowed for testing
//
if (!PopCheckSystemPowerIrpStatus(DevState, Irp, FALSE)) { KeReleaseSpinLock (&DevState->SpinLock, OldIrql); PopDumpSystemIrp ("ignored", PowerIrp); KeAcquireSpinLock (&DevState->SpinLock, &OldIrql); }
//
// Request is complete, free it
//
IoFreeIrp (Irp);
PowerIrp->Irp = NULL; PowerIrp->Notify = NULL; PushEntryList ( &DevState->Head.Free, &PowerIrp->Free );
} else {
//
// Some sort of error. Keep track of the failure
//
ASSERT (!DevState->Waking); InsertTailList(&DevState->Head.Failed, &PowerIrp->Failed); } } break;
case STATE_PRESENT_PAGABLE_IRPS: //
// Assume we're going to advance to the next state
//
State += 1;
//
// If the last device that a system irp was sent to was pagable,
// we use a thread to present them to the driver so it can page.
//
if (!(PopCallSystemState & PO_CALL_NON_PAGED)) { KeReleaseSpinLock (&DevState->SpinLock, OldIrql); PopSystemIrpDispatchWorker (FALSE); KeAcquireSpinLock (&DevState->SpinLock, &OldIrql); }
break;
case STATE_CHECK_CANCEL: //
// Assume we're going to advance to the next state
//
State += 1;
//
// If there's no error or we've already canceled move to the state
//
if (NT_SUCCESS(DevState->Status) || DevState->Cancelled || DevState->Waking) {
break; }
//
// First time the error has been seen. Cancel anything outstanding.
// Build list of all pending irps
//
DevState->Cancelled = TRUE; for (Link = DevState->Head.Pending.Flink; Link != &DevState->Head.Pending; Link = Link->Flink) {
PowerIrp = CONTAINING_RECORD (Link, POP_DEVICE_POWER_IRP, Pending); InsertTailList (&DevState->Head.Abort, &PowerIrp->Abort); }
//
// Drop completion lock and cancel irps on abort list
//
KeReleaseSpinLock (&DevState->SpinLock, OldIrql);
for (Link = DevState->Head.Abort.Flink; Link != &DevState->Head.Abort; Link = Link->Flink) {
PowerIrp = CONTAINING_RECORD (Link, POP_DEVICE_POWER_IRP, Abort); IoCancelIrp (PowerIrp->Irp); }
KeAcquireSpinLock (&DevState->SpinLock, &OldIrql); InitializeListHead (&DevState->Head.Abort);
//
// After canceling check for more completed irps
//
State = STATE_COMPLETE_IRPS; break;
case STATE_WAIT_NOW: //
// Check for wait condition
//
if ((!WaitForAll && IrpCompleted) || IsListEmpty(&DevState->Head.Pending)) {
//
// Done. After waiting, verify there's at least struct on the
// free list. If not, recycle something off the failured list
//
if (!DevState->Head.Free.Next && !IsListEmpty(&DevState->Head.Failed)) { Link = DevState->Head.Failed.Blink; PowerIrp = CONTAINING_RECORD (Link, POP_DEVICE_POWER_IRP, Failed);
RemoveEntryList(Link); PowerIrp->Failed.Flink = NULL; PowerIrp->Irp = NULL; PowerIrp->Notify = NULL;
PushEntryList ( &DevState->Head.Free, &PowerIrp->Free ); }
State = STATE_DONE_WAITING; break; }
//
// Signal completion function that we are waiting
//
DevState->WaitAll = TRUE; DevState->WaitAny = !WaitForAll;
//
// Drop locks and wait for event to be signalled
//
KeClearEvent (&DevState->Event); KeReleaseSpinLock (&DevState->SpinLock, OldIrql);
Timeout.QuadPart = DevState->Cancelled ? POP_ACTION_CANCEL_TIMEOUT : POP_ACTION_TIMEOUT; Timeout.QuadPart = Timeout.QuadPart * US2SEC * US2TIME * -1;
Status = KeWaitForSingleObject ( &DevState->Event, Suspended, KernelMode, FALSE, &Timeout );
KeAcquireSpinLock (&DevState->SpinLock, &OldIrql);
//
// No longer waiting
//
DevState->WaitAll = FALSE; DevState->WaitAny = FALSE;
//
// If this is a timeout, then dump all the pending irps
//
if (Status == STATUS_TIMEOUT) {
for (Link = DevState->Head.Pending.Flink; Link != &DevState->Head.Pending; Link = Link->Flink) {
PowerIrp = CONTAINING_RECORD (Link, POP_DEVICE_POWER_IRP, Pending); InsertTailList (&DevState->Head.Abort, &PowerIrp->Abort); }
KeReleaseSpinLock (&DevState->SpinLock, OldIrql);
for (Link = DevState->Head.Abort.Flink; Link != &DevState->Head.Abort; Link = Link->Flink) {
PowerIrp = CONTAINING_RECORD (Link, POP_DEVICE_POWER_IRP, Abort); PopDumpSystemIrp ("Waiting on", PowerIrp); }
KeAcquireSpinLock (&DevState->SpinLock, &OldIrql); InitializeListHead (&DevState->Head.Abort); }
//
// Check for completed irps
//
State = STATE_COMPLETE_IRPS; break; } }
KeReleaseSpinLock (&DevState->SpinLock, OldIrql); }
VOID PopSleepDeviceList ( IN PPOP_DEVICE_SYS_STATE DevState, IN PPO_NOTIFY_ORDER_LEVEL Level ) /*++
Routine Description:
Sends Sx power irps to all devices in the supplied level
Arguments:
DevState - Supplies the devstate
Level - Supplies the level to send power irps to
Return Value:
None. DevState->Status is set on error.
--*/ { PPO_DEVICE_NOTIFY NotifyDevice; PLIST_ENTRY Link; KIRQL OldIrql;
ASSERT(!DevState->Waking); ASSERT(IsListEmpty(&Level->Pending)); ASSERT(IsListEmpty(&Level->ReadyS0)); ASSERT(IsListEmpty(&Level->WaitS0));
//
// Move any devices from the completed list back to their correct spots.
//
Link = Level->ReadyS0.Flink; while (Link != &Level->ReadyS0) { NotifyDevice = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link); Link = NotifyDevice->Link.Flink; if (NotifyDevice->ChildCount) { InsertHeadList(&Level->WaitSleep, Link); } else { ASSERT(NotifyDevice->ActiveChild == 0); InsertHeadList(&Level->ReadySleep, Link); } } while (!IsListEmpty(&Level->Complete)) { Link = RemoveHeadList(&Level->Complete); NotifyDevice = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link); if (NotifyDevice->ChildCount) { InsertHeadList(&Level->WaitSleep, Link); } else { ASSERT(NotifyDevice->ActiveChild == 0); InsertHeadList(&Level->ReadySleep, Link); } }
ASSERT(!IsListEmpty(&Level->ReadySleep)); Level->ActiveCount = Level->DeviceCount;
KeAcquireSpinLock(&DevState->SpinLock, &OldIrql);
while ((Level->ActiveCount) && (NT_SUCCESS(DevState->Status))) {
if (!IsListEmpty(&Level->ReadySleep)) { Link = RemoveHeadList(&Level->ReadySleep); InsertTailList(&Level->Pending, Link); KeReleaseSpinLock(&DevState->SpinLock, OldIrql); NotifyDevice = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link); ASSERT(NotifyDevice->ActiveChild == 0); PopNotifyDevice(DevState, NotifyDevice); } else {
if ((Level->ActiveCount) && (NT_SUCCESS(DevState->Status))) {
//
// No devices are ready to receive IRPs yet, so wait for
// one of the pending IRPs to complete.
//
ASSERT(!IsListEmpty(&Level->Pending)); KeReleaseSpinLock(&DevState->SpinLock, OldIrql); PopWaitForSystemPowerIrp(DevState, FALSE); }
}
KeAcquireSpinLock(&DevState->SpinLock, &OldIrql); } KeReleaseSpinLock(&DevState->SpinLock, OldIrql); }
VOID PopResetChildCount( IN PLIST_ENTRY ListHead ) /*++
Routine Description:
Enumerates the notify structures in the supplied list and sets their active child count to be equal to the total child count.
Arguments:
ListHead - supplies the list head.
Return Value:
None
--*/
{ PPO_DEVICE_NOTIFY Notify; PLIST_ENTRY Link;
Link = ListHead->Flink; while (Link != ListHead) { Notify = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link); Link = Link->Flink; Notify->ActiveChild = Notify->ChildCount; }
}
VOID PopSetupListForWake( IN PPO_NOTIFY_ORDER_LEVEL Level, IN PLIST_ENTRY ListHead ) /*++
Routine Description:
Moves all devices that have WakeNeeded=TRUE from the specified list to either the ReadyS0 or WaitS0 lists.
Arguments:
Level - Supplies the level
ListHead - Supplies the list to be moved.
Return Value:
None
--*/
{ PPO_DEVICE_NOTIFY NotifyDevice; PPO_DEVICE_NOTIFY ParentNotify; PLIST_ENTRY Link;
Link = ListHead->Flink; while (Link != ListHead) { NotifyDevice = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link); Link = NotifyDevice->Link.Flink; if (NotifyDevice->WakeNeeded) { --Level->ActiveCount; RemoveEntryList(&NotifyDevice->Link); ParentNotify = IoGetPoNotifyParent(NotifyDevice); if ((ParentNotify==NULL) || (!ParentNotify->WakeNeeded)) { InsertTailList(&Level->ReadyS0, &NotifyDevice->Link); } else { InsertTailList(&Level->WaitS0, &NotifyDevice->Link); } } }
}
VOID PopWakeDeviceList( IN PPOP_DEVICE_SYS_STATE DevState, IN PPO_NOTIFY_ORDER_LEVEL Level ) /*++
Routine Description:
Sends S0 power irps to all devices that need waking in the given order level.
Arguments:
DevState - Supplies the device state
Level - supplies the level to send power irps to
Return Value:
None. DevState->Status is set on error.
--*/
{ PPO_DEVICE_NOTIFY NotifyDevice; PLIST_ENTRY Link; KIRQL OldIrql;
ASSERT(DevState->Waking); ASSERT(IsListEmpty(&Level->Pending)); ASSERT(IsListEmpty(&Level->WaitS0));
Level->ActiveCount = Level->DeviceCount; //
// Run through all the devices and put everything that has
// WakeNeeded=TRUE onto the wake list.
//
PopSetupListForWake(Level, &Level->WaitSleep); PopSetupListForWake(Level, &Level->ReadySleep); PopSetupListForWake(Level, &Level->Complete);
ASSERT((Level->DeviceCount == 0) || (Level->ActiveCount == Level->DeviceCount) || !IsListEmpty(&Level->ReadyS0));
KeAcquireSpinLock(&DevState->SpinLock, &OldIrql);
while (Level->ActiveCount < Level->DeviceCount) {
if (!IsListEmpty(&Level->ReadyS0)) { Link = RemoveHeadList(&Level->ReadyS0); InsertTailList(&Level->Pending,Link); KeReleaseSpinLock(&DevState->SpinLock, OldIrql); NotifyDevice = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link);
//
// Set the timer to go off if we are not done by the timeout period
//
if (PopSimulate & POP_WAKE_DEADMAN) { LARGE_INTEGER DueTime; DueTime.QuadPart = (LONGLONG)PopWakeTimer * -1 * 1000 * 1000 * 10; KeSetTimer(&PopWakeTimeoutTimer, DueTime, &PopWakeTimeoutDpc); } PopNotifyDevice(DevState, NotifyDevice); } else {
//
// No devices are ready to receive IRPs yet, so wait for
// one of the pending IRPs to complete.
//
ASSERT(!IsListEmpty(&Level->Pending)); KeReleaseSpinLock(&DevState->SpinLock, OldIrql); PopWaitForSystemPowerIrp(DevState, FALSE); } KeAcquireSpinLock(&DevState->SpinLock, &OldIrql); } KeReleaseSpinLock(&DevState->SpinLock, OldIrql);
ASSERT(Level->ActiveCount == Level->DeviceCount);
}
VOID PopLogNotifyDevice ( IN PDEVICE_OBJECT TargetDevice, IN OPTIONAL PPO_DEVICE_NOTIFY Notify, IN PIRP Irp ) /*++
Routine Description:
This routine logs a Po device notification. It is a seperate function so that the local buffer does not eat stack space through the PoCallDriver call.
Arguments:
TargetDevice - Device IRP is being sent to.
Notify - If present, supplies the power notify structure for the specified device This will only be present on Sx irps, not Dx irps.
Irp - Pointer to the built Irp for PoCallDriver.
Return Value:
None.
--*/ { UCHAR StackBuffer[256]; ULONG StackBufferSize; PPERFINFO_PO_NOTIFY_DEVICE LogEntry; ULONG MaxDeviceNameLength; ULONG DeviceNameLength; ULONG CopyLength; ULONG RemainingSize; ULONG LogEntrySize; PIO_STACK_LOCATION IrpSp;
//
// Initialize locals.
//
StackBufferSize = sizeof(StackBuffer); LogEntry = (PVOID) StackBuffer; IrpSp = IoGetNextIrpStackLocation(Irp);
//
// Stack buffer should be large enough to contain at least the fixed
// part of the LogEntry structure.
//
if (StackBufferSize < sizeof(PERFINFO_PO_NOTIFY_DEVICE)) { ASSERT(FALSE); return; }
//
// Fill in the LogEntry fields.
//
LogEntry->Irp = Irp; LogEntry->DriverStart = TargetDevice->DriverObject->DriverStart; LogEntry->MajorFunction = IrpSp->MajorFunction; LogEntry->MinorFunction = IrpSp->MinorFunction; LogEntry->Type = IrpSp->Parameters.Power.Type; LogEntry->State = IrpSp->Parameters.Power.State;
if (Notify) { LogEntry->OrderLevel = Notify->OrderLevel; if (Notify->DeviceName) {
//
// Determine what the maximum device name length (excluding NUL) we
// can fit into our stack buffer. Note that PERFINFO_NOTIFY_DEVICE
// has space for the terminating NUL character.
//
RemainingSize = StackBufferSize - sizeof(PERFINFO_PO_NOTIFY_DEVICE); MaxDeviceNameLength = RemainingSize / sizeof(WCHAR);
//
// Determine the length of the device name and adjust the copy length.
//
DeviceNameLength = (ULONG) wcslen(Notify->DeviceName); CopyLength = DeviceNameLength;
if (CopyLength > MaxDeviceNameLength) { CopyLength = MaxDeviceNameLength; }
//
// Copy CopyLength characters from the end of the DeviceName.
// This way if our buffer is not enough, we get a more distinct part
// of the name.
//
wcscpy(LogEntry->DeviceName, Notify->DeviceName + DeviceNameLength - CopyLength);
} else {
//
// There is no device name.
//
CopyLength = 0; LogEntry->DeviceName[CopyLength] = 0; } } else { LogEntry->OrderLevel = 0; CopyLength = 0; LogEntry->DeviceName[CopyLength] = 0; }
//
// Copied device name should be terminated: we had enough room for it.
//
ASSERT(LogEntry->DeviceName[CopyLength] == 0);
//
// Log the entry.
//
LogEntrySize = sizeof(PERFINFO_PO_NOTIFY_DEVICE); LogEntrySize += CopyLength * sizeof(WCHAR);
PerfInfoLogBytes(PERFINFO_LOG_TYPE_PO_NOTIFY_DEVICE, LogEntry, LogEntrySize);
//
// We are done.
//
return; }
VOID PopNotifyDevice ( IN PPOP_DEVICE_SYS_STATE DevState, IN PPO_DEVICE_NOTIFY Notify ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { PPOP_DEVICE_POWER_IRP PowerIrp; PSINGLE_LIST_ENTRY Entry; PIO_STACK_LOCATION IrpSp; PIRP Irp; ULONG SysCall; KIRQL OldIrql; PDEVICE_OBJECT *WarmEjectDevice; POWER_ACTION IrpAction;
//
// Set the SysCall state to match our notify current state
//
ASSERT(PopCurrentLevel == Notify->OrderLevel); SysCall = PO_CALL_SYSDEV_QUEUE; if (!(Notify->OrderLevel & PO_ORDER_PAGABLE)) { SysCall |= PO_CALL_NON_PAGED; }
if (PopCallSystemState != SysCall) { PopLockWorkerQueue(&OldIrql); PopCallSystemState = SysCall; PopUnlockWorkerQueue(OldIrql); }
//
// Allocate an PowerIrp & Irp structures
//
PowerIrp = NULL; Irp = NULL;
for (; ;) { Entry = PopEntryList(&DevState->Head.Free); if (Entry) { break; }
PopWaitForSystemPowerIrp (DevState, FALSE); }
PowerIrp = CONTAINING_RECORD(Entry, POP_DEVICE_POWER_IRP, Free);
for (; ;) { Irp = IoAllocateIrp ((CHAR) Notify->TargetDevice->StackSize, FALSE); if (Irp) { break; }
PopWaitForSystemPowerIrp (DevState, FALSE); }
SPECIALIRP_WATERMARK_IRP(Irp, IRP_SYSTEM_RESTRICTED);
if (!DevState->Waking) {
//
// If the device node list changed, then restart. This could have
// happened when we dropped our list and then rebuilt it inbetween
// queries for sleep states.
//
if (DevState->Order.DevNodeSequence != IoDeviceNodeTreeSequence) {
PopRestartSetSystemState(); }
//
// If there's been some sort of error, then abort
//
if (!NT_SUCCESS(DevState->Status)) { PushEntryList (&DevState->Head.Free, &PowerIrp->Free); IoFreeIrp (Irp); return ; // abort
}
//
// Mark notify as needing wake.
//
Notify->WakeNeeded = TRUE; } else { Notify->WakeNeeded = FALSE; }
//
// Put irp onto pending queue
//
PowerIrp->Irp = Irp; PowerIrp->Notify = Notify;
ExInterlockedInsertTailList ( &DevState->Head.Pending, &PowerIrp->Pending, &DevState->SpinLock );
//
// Setup irp
//
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED ; Irp->IoStatus.Information = 0; IrpSp = IoGetNextIrpStackLocation(Irp); IrpSp->MajorFunction = IRP_MJ_POWER; IrpSp->MinorFunction = DevState->IrpMinor; IrpSp->Parameters.Power.SystemContext = 0; IrpSp->Parameters.Power.Type = SystemPowerState; IrpSp->Parameters.Power.State.SystemState = DevState->SystemState;
ASSERT(PopAction.Action != PowerActionHibernate);
WarmEjectDevice = DevState->Order.WarmEjectPdoPointer;
//
// We need to determine the appropriate power action to place in our IRP.
// For instance, we send PowerActionWarmEject to the devnode being warm
// ejected, and we convert our internal PowerActionSleep to
// PowerActionHibernate if the sleep state is S4.
//
IrpAction = PopMapInternalActionToIrpAction ( PopAction.Action, DevState->SystemState, (BOOLEAN) (DevState->Waking || (*WarmEjectDevice != Notify->DeviceObject)) );
//
// If we are sending a set power to the devnode to be warm ejected,
// zero out the warm eject device field to signify we have handled to
// requested operation.
//
if ((IrpAction == PowerActionWarmEject) && (*WarmEjectDevice == Notify->DeviceObject) && (DevState->IrpMinor == IRP_MN_SET_POWER)) {
*WarmEjectDevice = NULL; }
IrpSp->Parameters.Power.ShutdownType = IrpAction;
IoSetCompletionRoutine (Irp, PopCompleteSystemPowerIrp, PowerIrp, TRUE, TRUE, TRUE);
//
// Log the call.
//
if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PopLogNotifyDevice(Notify->TargetDevice, Notify, Irp); }
//
// Give it to the driver, and continue
//
PoCallDriver (Notify->TargetDevice, Irp); }
NTSTATUS PopCompleteSystemPowerIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++
Routine Description:
IRP completion routine for system power irps. Takes the irp from the DevState pending queue and puts it on the DevState complete queue.
Arguments:
DeviceObect - The device object
Irp - The IRP
Context - Device power irp structure for this request
Return Value:
STATUS_MORE_PROCESSING_REQUIRED
--*/ { PPOP_DEVICE_POWER_IRP PowerIrp; PPOP_DEVICE_SYS_STATE DevState; KIRQL OldIrql; BOOLEAN SetEvent; PPO_DEVICE_NOTIFY Notify; PPO_DEVICE_NOTIFY ParentNotify; PPO_NOTIFY_ORDER_LEVEL Order;
UNREFERENCED_PARAMETER (DeviceObject);
PowerIrp = (PPOP_DEVICE_POWER_IRP) Context; DevState = PopAction.DevState;
SetEvent = FALSE;
//
// Log the completion.
//
if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_PO_NOTIFY_DEVICE_COMPLETE LogEntry; LogEntry.Irp = Irp; LogEntry.Status = Irp->IoStatus.Status; PerfInfoLogBytes(PERFINFO_LOG_TYPE_PO_NOTIFY_DEVICE_COMPLETE, &LogEntry, sizeof(LogEntry)); }
KeAcquireSpinLock (&DevState->SpinLock, &OldIrql);
//
// Move irp from pending queue to complete queue
//
RemoveEntryList (&PowerIrp->Pending); PowerIrp->Pending.Flink = NULL; InsertTailList (&DevState->Head.Complete, &PowerIrp->Complete);
//
// Move notify from pending queue to the appropriate queue
// depending on whether we are sleeping or waking.
//
Notify=PowerIrp->Notify; ASSERT(Notify->OrderLevel == PopCurrentLevel); Order = &DevState->Order.OrderLevel[Notify->OrderLevel]; RemoveEntryList(&Notify->Link); InsertTailList(&Order->Complete, &Notify->Link); if (DevState->Waking) { ++Order->ActiveCount; IoMovePoNotifyChildren(Notify, &DevState->Order); } else {
//
// We will only decrement the parent's active count if the IRP was
// completed successfully. Otherwise it is possible for a parent to
// get put on the ReadySleep list even though its child has failed
// the query/set irp.
//
if (NT_SUCCESS(Irp->IoStatus.Status) || DevState->IgnoreErrors) { --Order->ActiveCount; ParentNotify = IoGetPoNotifyParent(Notify); if (ParentNotify) { ASSERT(ParentNotify->ActiveChild > 0); if (--ParentNotify->ActiveChild == 0) { RemoveEntryList(&ParentNotify->Link); InsertTailList(&DevState->Order.OrderLevel[ParentNotify->OrderLevel].ReadySleep, &ParentNotify->Link); } } } }
//
// If there is a wait any, then kick event
// If there is a wait all, then check for empty pending queue
//
if ((DevState->WaitAny) || (DevState->WaitAll && IsListEmpty(&DevState->Head.Pending))) { SetEvent = TRUE; }
//
// If the IRP is in error and it's the first such IRP start aborting
// the current operation
//
if (!PopCheckSystemPowerIrpStatus(DevState, Irp, TRUE) && NT_SUCCESS(DevState->Status)) {
//
// We need to set the failed device here. If we are warm ejecting
// however, the warm eject devnode will *legitimately* fail any queries
// for S states it doesn't support. As we will be trying several Sx
// states, the trick is to preserve any failed device that is *not*
// warm eject devnode, and update failed device to the warm eject
// devnode only if failed device is currently NULL.
//
if ((PopAction.Action != PowerActionWarmEject) || (DevState->FailedDevice == NULL) || (PowerIrp->Notify->DeviceObject != *DevState->Order.WarmEjectPdoPointer)) {
DevState->FailedDevice = PowerIrp->Notify->DeviceObject; }
DevState->Status = Irp->IoStatus.Status; SetEvent = TRUE; // wake to cancel pending irps
}
KeReleaseSpinLock (&DevState->SpinLock, OldIrql);
if (SetEvent) { KeSetEvent (&DevState->Event, IO_NO_INCREMENT, FALSE); }
return STATUS_MORE_PROCESSING_REQUIRED; }
BOOLEAN PopCheckSystemPowerIrpStatus ( IN PPOP_DEVICE_SYS_STATE DevState, IN PIRP Irp, IN BOOLEAN AllowTestFailure ) // return FALSE if irp is some sort of unallowed error
{ NTSTATUS Status;
Status = Irp->IoStatus.Status;
//
// If Status is sucess, then no problem
//
if (NT_SUCCESS(Status)) { return TRUE; }
//
// If errors are allowed, or it's a cancelled request no problem
//
if (DevState->IgnoreErrors || Status == STATUS_CANCELLED) { return TRUE; }
//
// Check to see if the error is that the driver doesn't implement the
// request and if such a condition is allowed
//
if (Status == STATUS_NOT_SUPPORTED && DevState->IgnoreNotImplemented) { return TRUE; }
//
// For testing purposes, optionally let through unsupported device drivers
//
if (Status == STATUS_NOT_SUPPORTED && AllowTestFailure && (PopSimulate & POP_IGNORE_UNSUPPORTED_DRIVERS)) {
return TRUE; }
//
// Some unexpected failure, treat it as an error
//
return FALSE; }
VOID PopRestartSetSystemState ( VOID ) /*++
Routine Description:
Aborts current system power state operation.
Arguments:
None
Return Value:
None
--*/ { KIRQL OldIrql;
KeAcquireSpinLock (&PopAction.DevState->SpinLock, &OldIrql); if (!PopAction.Shutdown && NT_SUCCESS(PopAction.DevState->Status)) { PopAction.DevState->Status = STATUS_CANCELLED; } KeReleaseSpinLock (&PopAction.DevState->SpinLock, OldIrql); KeSetEvent (&PopAction.DevState->Event, IO_NO_INCREMENT, FALSE); }
VOID PopDumpSystemIrp ( IN PCHAR Desc, IN PPOP_DEVICE_POWER_IRP PowerIrp ) { PCHAR IrpType; PPO_DEVICE_NOTIFY Notify;
Notify = PowerIrp->Notify;
//
// Dump errors to debugger
//
switch (PopAction.DevState->IrpMinor) { case IRP_MN_QUERY_POWER: IrpType = "QueryPower"; break; case IRP_MN_SET_POWER: IrpType = "SetPower"; break; default: IrpType = "?"; break; }
DbgPrint ("%s: ", Desc);
if (Notify->DriverName) { DbgPrint ("%ws ", Notify->DriverName); } else { DbgPrint ("%x ", Notify->TargetDevice->DriverObject); }
if (Notify->DeviceName) { DbgPrint ("%ws ", Notify->DeviceName); } else { DbgPrint ("%x ", Notify->TargetDevice); }
DbgPrint ("irp (%x) %s-%s status %x\n", PowerIrp->Irp, IrpType, PopSystemStateString(PopAction.DevState->SystemState), PowerIrp->Irp->IoStatus.Status ); }
VOID PopWakeSystemTimeout( IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++
Routine Description:
This routine is used to break into the kernel debugger if somebody is taking too long processing their S irps.
Arguments:
Return Value:
None
--*/
{ UNREFERENCED_PARAMETER (Dpc); UNREFERENCED_PARAMETER (DeferredContext); UNREFERENCED_PARAMETER (SystemArgument1); UNREFERENCED_PARAMETER (SystemArgument2);
try { DbgBreakPoint(); } except (EXCEPTION_EXECUTE_HANDLER) { ; }
}
|