1206 lines
32 KiB
C
1206 lines
32 KiB
C
/*++
|
||
|
||
Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved
|
||
|
||
Module Name:
|
||
|
||
power.c
|
||
|
||
Abstract:
|
||
|
||
The power management related processing.
|
||
|
||
The Power Manager uses IRPs to direct drivers to change system
|
||
and device power levels, to respond to system wake-up events,
|
||
and to query drivers about their devices. All power IRPs have
|
||
the major function code IRP_MJ_POWER.
|
||
|
||
Most function and filter drivers perform some processing for
|
||
each power IRP, then pass the IRP down to the next lower driver
|
||
without completing it. Eventually the IRP reaches the bus driver,
|
||
which physically changes the power state of the device and completes
|
||
the IRP.
|
||
|
||
When the IRP has been completed, the I/O Manager calls any
|
||
IoCompletion routines set by drivers as the IRP traveled
|
||
down the device stack. Whether a driver needs to set a completion
|
||
routine depends upon the type of IRP and the driver's individual
|
||
requirements.
|
||
|
||
The power policy of this driver is simple. The device enters the
|
||
device working state D0 when the system enters the system
|
||
working state S0. The device enters the lowest-powered sleeping state
|
||
D3 for all the other system power states (S1-S5).
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "processor.h"
|
||
#include "power.h"
|
||
|
||
PVOID ProcessorSleepPageLock = NULL;
|
||
AC_DC_TRANSITION_NOTIFY AcDcTransitionNotifyHandler;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text (PAGELK, ProcessorDispatchPower)
|
||
#pragma alloc_text (PAGELK, ProcessorDefaultPowerHandler)
|
||
#pragma alloc_text (PAGELK, ProcessorSetPowerState)
|
||
#pragma alloc_text (PAGELK, ProcessorQueryPowerState)
|
||
#pragma alloc_text (PAGELK, HandleSystemPowerIrp)
|
||
#pragma alloc_text (PAGELK, OnFinishSystemPowerUp)
|
||
#pragma alloc_text (PAGELK, QueueCorrespondingDeviceIrp)
|
||
#pragma alloc_text (PAGELK, OnPowerRequestComplete)
|
||
#pragma alloc_text (PAGELK, HandleDeviceQueryPower)
|
||
#pragma alloc_text (PAGELK, HandleDeviceSetPower)
|
||
#pragma alloc_text (PAGELK, OnFinishDevicePowerUp)
|
||
#pragma alloc_text (PAGELK, BeginSetDevicePowerState)
|
||
#pragma alloc_text (PAGELK, FinishDevicePowerIrp)
|
||
#pragma alloc_text (PAGELK, HoldIoRequests)
|
||
#pragma alloc_text (PAGELK, HoldIoRequestsWorkerRoutine)
|
||
#pragma alloc_text (PAGELK, ProcessorPowerStateCallback)
|
||
#pragma alloc_text (PAGELK, RegisterAcDcTransitionNotifyHandler)
|
||
#endif
|
||
|
||
NTSTATUS
|
||
ProcessorDispatchPower (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The power dispatch routine.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION stack;
|
||
PFDO_DATA fdoData;
|
||
NTSTATUS status;
|
||
|
||
DebugEnter();
|
||
|
||
stack = IoGetCurrentIrpStackLocation(Irp);
|
||
fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
|
||
DebugPrint((TRACE, "FDO %s IRP:0x%x %s %s\n",
|
||
PowerMinorFunctionString(stack->MinorFunction),
|
||
Irp,
|
||
DbgSystemPowerString(fdoData->SystemPowerState),
|
||
DbgDevicePowerString(fdoData->DevicePowerState)));
|
||
|
||
//
|
||
// We don't queue power Irps, we'll only check if the
|
||
// device was removed, otherwise we'll take appropriate
|
||
// action and send it to the next lower driver. In general
|
||
// drivers should not cause long delays while handling power
|
||
// IRPs. If a driver cannot handle a power IRP in a brief time,
|
||
// it should return STATUS_PENDING and queue all incoming
|
||
// IRPs until the IRP completes.
|
||
//
|
||
|
||
|
||
if (Deleted == fdoData->DevicePnPState) {
|
||
|
||
//
|
||
// Even if a driver fails the IRP, it must nevertheless call
|
||
// PoStartNextPowerIrp to inform the Power Manager that it
|
||
// is ready to handle another power IRP.
|
||
//
|
||
|
||
PoStartNextPowerIrp (Irp);
|
||
Irp->IoStatus.Status = status = STATUS_DELETE_PENDING;
|
||
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If the device is not stated yet, just pass it down.
|
||
//
|
||
|
||
if (NotStarted == fdoData->DevicePnPState ) {
|
||
PoStartNextPowerIrp(Irp);
|
||
IoSkipCurrentIrpStackLocation(Irp);
|
||
return PoCallDriver(fdoData->NextLowerDriver, Irp);
|
||
}
|
||
|
||
ProcessorIoIncrement (fdoData);
|
||
|
||
//
|
||
// Check the request type.
|
||
//
|
||
|
||
switch (stack->MinorFunction) {
|
||
|
||
case IRP_MN_SET_POWER :
|
||
|
||
//
|
||
// The Power Manager sends this IRP for one of the
|
||
// following reasons:
|
||
// 1) To notify drivers of a change to the system power state.
|
||
// 2) To change the power state of a device for which
|
||
// the Power Manager is performing idle detection.
|
||
// A driver sends IRP_MN_SET_POWER to change the power
|
||
// state of its device if it's a power policy owner for the
|
||
// device.
|
||
//
|
||
|
||
status = ProcessorSetPowerState(DeviceObject, Irp);
|
||
|
||
break;
|
||
|
||
|
||
case IRP_MN_QUERY_POWER :
|
||
|
||
|
||
//
|
||
// The Power Manager sends a power IRP with the minor
|
||
// IRP code IRP_MN_QUERY_POWER to determine whether it
|
||
// can safely change to the specified system power state
|
||
// (S1-S5) and to allow drivers to prepare for such a change.
|
||
// If a driver can put its device in the requested state,
|
||
// it sets status to STATUS_SUCCESS and passes the IRP down.
|
||
//
|
||
|
||
status = ProcessorQueryPowerState(DeviceObject, Irp);
|
||
break;
|
||
|
||
case IRP_MN_WAIT_WAKE :
|
||
//
|
||
// The minor power IRP code IRP_MN_WAIT_WAKE provides
|
||
// for waking a device or waking the system. Drivers
|
||
// of devices that can wake themselves or the system
|
||
// send IRP_MN_WAIT_WAKE. The system sends IRP_MN_WAIT_WAKE
|
||
// only to devices that always wake the system, such as
|
||
// the power-on switch.
|
||
//
|
||
|
||
case IRP_MN_POWER_SEQUENCE :
|
||
|
||
//
|
||
// A driver sends this IRP as an optimization to determine
|
||
// whether its device actually entered a specific power state.
|
||
// This IRP is optional. Power Manager cannot send this IRP.
|
||
//
|
||
|
||
default:
|
||
//
|
||
// Pass it down
|
||
//
|
||
status = ProcessorDefaultPowerHandler(DeviceObject, Irp);
|
||
ProcessorIoDecrement(fdoData);
|
||
break;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ProcessorDefaultPowerHandler (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN OUT PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If a driver does not support a particular power IRP,
|
||
it must nevertheless pass the IRP down the device stack
|
||
to the next-lower driver. A driver further down the stack
|
||
might be prepared to handle the IRP and must have the
|
||
opportunity to do so.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PFDO_DATA fdoData;
|
||
|
||
DebugEnter();
|
||
|
||
//
|
||
// Drivers must call PoStartNextPowerIrp while the current
|
||
// IRP stack location points to the current driver.
|
||
// This routine can be called from the DispatchPower routine
|
||
// or from the IoCompletion routine. However, PoStartNextPowerIrp
|
||
// must be called before IoCompleteRequest, IoSkipCurrentIrpStackLocation,
|
||
// and PoCallDriver. Calling the routines in the other order might
|
||
// cause a system deadlock.
|
||
//
|
||
|
||
PoStartNextPowerIrp(Irp);
|
||
|
||
IoSkipCurrentIrpStackLocation(Irp);
|
||
|
||
fdoData = (PFDO_DATA)DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Drivers must use PoCallDriver, rather than IoCallDriver,
|
||
// to pass power IRPs. PoCallDriver allows the Power Manager
|
||
// to ensure that power IRPs are properly synchronized throughout
|
||
// the system.
|
||
//
|
||
|
||
status = PoCallDriver(fdoData->NextLowerDriver, Irp);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DebugPrint((0, "Lower driver fails a power irp\n"));
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ProcessorSetPowerState(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes IRP_MN_SET_POWER.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
DebugEnter();
|
||
|
||
return (stack->Parameters.Power.Type == SystemPowerState) ?
|
||
HandleSystemPowerIrp(DeviceObject, Irp) :
|
||
HandleDeviceSetPower(DeviceObject, Irp);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ProcessorQueryPowerState(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes IRP_MN_QUERY_POWER.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
DebugEnter();
|
||
|
||
return (stack->Parameters.Power.Type == SystemPowerState) ?
|
||
HandleSystemPowerIrp(DeviceObject, Irp) :
|
||
HandleDeviceQueryPower(DeviceObject, Irp);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
HandleSystemPowerIrp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes IRP_MN_SET_POWER and IRP_MN_QUERY_POWER
|
||
for the system power Irp (S-IRP).
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
|
||
POWER_STATE_TYPE type = stack->Parameters.Power.Type;
|
||
PFDO_DATA fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
SYSTEM_POWER_STATE newSystemState;
|
||
|
||
DebugEnter();
|
||
|
||
IoMarkIrpPending(Irp);
|
||
|
||
|
||
newSystemState = stack->Parameters.Power.State.SystemState;
|
||
|
||
|
||
//
|
||
// Here we update our cached away system power state.
|
||
//
|
||
|
||
if (stack->MinorFunction == IRP_MN_SET_POWER) {
|
||
|
||
//
|
||
// We are suspending ...
|
||
//
|
||
|
||
if (newSystemState > PowerSystemWorking &&
|
||
newSystemState < PowerSystemShutdown) {
|
||
|
||
ProcessSuspendToSleepState(newSystemState, fdoData);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// We are resuming ...
|
||
//
|
||
|
||
if (newSystemState == PowerSystemWorking) {
|
||
ProcessResumeFromSleepState(fdoData->SystemPowerState, fdoData);
|
||
}
|
||
|
||
|
||
fdoData->SystemPowerState = newSystemState;
|
||
}
|
||
|
||
//
|
||
// Send the IRP down
|
||
//
|
||
|
||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||
|
||
IoSetCompletionRoutine(Irp,
|
||
(PIO_COMPLETION_ROUTINE) OnFinishSystemPowerUp,
|
||
NULL,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
PoCallDriver(fdoData->NextLowerDriver, Irp);
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OnFinishSystemPowerUp(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP Irp,
|
||
IN PVOID NotUsed
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The completion routine for Power Up S-IRP.
|
||
It queues a corresponding D-IRP.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Not used - context pointer
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PFDO_DATA fdoData = (PFDO_DATA) Fdo->DeviceExtension;
|
||
NTSTATUS status = Irp->IoStatus.Status;
|
||
|
||
DebugEnter();
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
PoStartNextPowerIrp(Irp);
|
||
ProcessorIoDecrement(fdoData);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
QueueCorrespondingDeviceIrp(Irp, Fdo);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
VOID
|
||
QueueCorrespondingDeviceIrp(
|
||
IN PIRP SIrp,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine gets the D-State for a particular S-State
|
||
from DeviceCaps and generates a D-IRP.
|
||
|
||
Arguments:
|
||
|
||
Irp - pointer to an S-IRP.
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
{
|
||
POWER_COMPLETION_CONTEXT* powerContext;
|
||
NTSTATUS status;
|
||
POWER_STATE state;
|
||
PFDO_DATA fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(SIrp);
|
||
POWER_STATE systemState = stack->Parameters.Power.State;
|
||
|
||
DebugEnter();
|
||
|
||
//
|
||
// Read the D-IRP out of the S->D mapping array we captured in QueryCap's.
|
||
// We can choose deeper sleep states than our mapping (with the appropriate
|
||
// caveats if we have children), but we can never choose lighter ones, as
|
||
// what hardware stays on in a given S-state is a function of the
|
||
// motherboard wiring.
|
||
//
|
||
// Also note that if a driver rounds down it's D-state, it must ensure that
|
||
// such a state is supported. A driver can do this by examining the
|
||
// D1Supported? and D2Supported? flags (Win2k), or by examining the entire
|
||
// S->D state mapping (Win98).
|
||
//
|
||
state.DeviceState = fdoData->DeviceCaps.DeviceState[systemState.SystemState];
|
||
|
||
powerContext = (POWER_COMPLETION_CONTEXT*)
|
||
ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(POWER_COMPLETION_CONTEXT),
|
||
PROCESSOR_POOL_TAG);
|
||
|
||
if (!powerContext) {
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
powerContext->DeviceObject = DeviceObject;
|
||
powerContext->SIrp = SIrp;
|
||
|
||
//
|
||
// Note: Win2k's PoRequestPowerIrp can take an FDO,
|
||
// but Win9x's requires the PDO.
|
||
//
|
||
|
||
status = PoRequestPowerIrp(fdoData->UnderlyingPDO,
|
||
stack->MinorFunction,
|
||
state, OnPowerRequestComplete,
|
||
powerContext, NULL);
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
if (powerContext) {
|
||
ExFreePool(powerContext);
|
||
}
|
||
|
||
PoStartNextPowerIrp(SIrp);
|
||
SIrp->IoStatus.Status = status;
|
||
IoCompleteRequest(SIrp, IO_NO_INCREMENT);
|
||
ProcessorIoDecrement(fdoData);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
OnPowerRequestComplete(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
UCHAR MinorFunction,
|
||
POWER_STATE state,
|
||
POWER_COMPLETION_CONTEXT* PowerContext,
|
||
PIO_STATUS_BLOCK IoStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Completion routine for D-IRP.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
PFDO_DATA fdoData = (PFDO_DATA) PowerContext->DeviceObject->DeviceExtension;
|
||
PIRP sIrp = PowerContext->SIrp;
|
||
|
||
DebugEnter();
|
||
|
||
//
|
||
// Here we copy the D-IRP status into the S-IRP
|
||
//
|
||
sIrp->IoStatus.Status = IoStatus->Status;
|
||
|
||
//
|
||
// Release the IRP
|
||
//
|
||
PoStartNextPowerIrp(sIrp);
|
||
IoCompleteRequest(sIrp, IO_NO_INCREMENT);
|
||
|
||
//
|
||
// Cleanup
|
||
//
|
||
ExFreePool(PowerContext);
|
||
ProcessorIoDecrement(fdoData);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
HandleDeviceQueryPower(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles IRP_MN_QUERY_POWER for D-IRP
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
|
||
PFDO_DATA fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
DEVICE_POWER_STATE deviceState = stack->Parameters.Power.State.DeviceState;
|
||
NTSTATUS status;
|
||
|
||
DebugEnter();
|
||
|
||
//
|
||
// Here we check to see if it's OK for our hardware to be suspended. Note
|
||
// that our driver may have requests that would cause us to fail this
|
||
// check. If so, we need to begin queuing requests after succeeding this
|
||
// call (otherwise we may succeed such an IRP *after* we've said we can
|
||
// power down).
|
||
//
|
||
// Note - we may be at DISPATCH_LEVEL here. As such the below code assumes
|
||
// all I/O is handled at DISPATCH_LEVEL under a spinlock
|
||
// (IoStartNextPacket style), or that this function cannot fail. If
|
||
// such operations are to be handled at PASSIVE_LEVEL (meaning we
|
||
// need to block on an *event* here), then this code should mark the
|
||
// Irp pending (via IoMarkIrpPending), queue a workitem, and return
|
||
// STATUS_PENDING.
|
||
//
|
||
|
||
if (deviceState == PowerDeviceD0) {
|
||
|
||
//
|
||
// Note - this driver does not queue IRPs if the S-to-D state mapping
|
||
// specifies that the device will remain in D0 during standby.
|
||
// For some devices, this could be a problem. For instance, if
|
||
// an audio card where in a machine where S1->D0, it not want to
|
||
// stay "active" during standby (could be noisy).
|
||
//
|
||
// Ideally, a driver would be able to use the ShutdownType field
|
||
// in the D-IRP to distinguish these cases. Unfortunately, this
|
||
// field cannot be trusted for D0 IRPs. A driver can get the same
|
||
// information however by maintaining a pointer to the current
|
||
// S-IRP in its device extension. Of course, such a driver must
|
||
// be very very careful if it also does idle detection (which is
|
||
// not shown here).
|
||
//
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
|
||
status = HoldIoRequests(DeviceObject, Irp, IRP_NEEDS_FORWARDING);
|
||
if(STATUS_PENDING == status)
|
||
{
|
||
return status;
|
||
}
|
||
}
|
||
|
||
status = FinishDevicePowerIrp(DeviceObject, Irp, IRP_NEEDS_FORWARDING, status);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
HandleDeviceSetPower(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles IRP_MN_SET_POWER for D-IRP
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
|
||
POWER_STATE_TYPE type = stack->Parameters.Power.Type;
|
||
POWER_STATE state = stack->Parameters.Power.State;
|
||
NTSTATUS status;
|
||
PFDO_DATA fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
|
||
DebugEnter();
|
||
|
||
if (state.DeviceState < fdoData->DevicePowerState) { // adding power
|
||
|
||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||
IoSetCompletionRoutine(Irp,
|
||
(PIO_COMPLETION_ROUTINE) OnFinishDevicePowerUp,
|
||
NULL, TRUE, TRUE, TRUE);
|
||
|
||
PoCallDriver(fdoData->NextLowerDriver, Irp);
|
||
return STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We are here if we are entering a deeper sleep or entering a state
|
||
// we are already in.
|
||
//
|
||
// As non-D0 IRPs are not alike (some may be for hibernate, shutdown,
|
||
// or sleeping actions), we present these to our state machine.
|
||
//
|
||
// All D0 IRPs are alike though, and we don't want to touch our hardware
|
||
// on a D0->D0 transition. However, we must still present them to our
|
||
// state machine in case we succeeded a Query-D call (which may begin
|
||
// queueing future requests) and the system has sent an S0 IRP to cancel
|
||
// that preceeding query.
|
||
//
|
||
status = BeginSetDevicePowerState(DeviceObject, Irp, IRP_NEEDS_FORWARDING);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OnFinishDevicePowerUp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID NotUsed
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The completion routine for Power Up D-IRP.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an I/O Request Packet.
|
||
|
||
Not used - context pointer
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PFDO_DATA fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
NTSTATUS status = Irp->IoStatus.Status;
|
||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
|
||
POWER_STATE_TYPE type = stack->Parameters.Power.Type;
|
||
|
||
DebugEnter();
|
||
|
||
if (Irp->PendingReturned) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
PoStartNextPowerIrp(Irp);
|
||
ProcessorIoDecrement(fdoData);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
ASSERT(stack->MajorFunction == IRP_MJ_POWER);
|
||
ASSERT(stack->MinorFunction == IRP_MN_SET_POWER);
|
||
|
||
BeginSetDevicePowerState(DeviceObject, Irp, IRP_ALREADY_FORWARDED);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
BeginSetDevicePowerState(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN IRP_DIRECTION Direction
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the actual power changes to the device.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an D-IRP.
|
||
|
||
Direction - Whether to forward the D-IRP down or not.
|
||
This depends on whether the system is powering
|
||
up or down.
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
PFDO_DATA fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
|
||
POWER_ACTION newDeviceAction;
|
||
DEVICE_POWER_STATE newDeviceState, oldDeviceState;
|
||
POWER_STATE newState;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
DebugEnter();
|
||
|
||
//
|
||
// Note - Here we may be at DISPATCH_LEVEL. The below code assumes all I/O
|
||
// is handled at DISPATCH_LEVEL under a spinlock (IoStartNextPacket
|
||
// style). If such operations are to be handled at PASSIVE_LEVEL then
|
||
// this code should queue a workitem if called at DISPATCH_LEVEL.
|
||
//
|
||
// Also note that we'd want to mark the IRP appropriately (via
|
||
// IoMarkIrpPending) and we'd want to return STATUS_PENDING. As this
|
||
// example does things synchronously, it is enough for us to return
|
||
// the result of FinishSetDevicePowerState.
|
||
//
|
||
|
||
//
|
||
// Update our state.
|
||
//
|
||
newState = stack->Parameters.Power.State;
|
||
newDeviceState = newState.DeviceState;
|
||
oldDeviceState = fdoData->DevicePowerState;
|
||
fdoData->DevicePowerState = newDeviceState;
|
||
|
||
DebugPrint((TRACE, "BeginSetDevicePowerState: Device State = %s\n",
|
||
DbgDevicePowerString(fdoData->DevicePowerState)));
|
||
|
||
if (newDeviceState > PowerDeviceD0) {
|
||
|
||
//
|
||
// We are here if our hardware is about to be turned off. HoldRequests
|
||
// queues a workitem and returns immediately with STATUS_PENDING.
|
||
//
|
||
status = HoldIoRequests(DeviceObject, Irp, Direction);
|
||
|
||
if(STATUS_PENDING == status)
|
||
{
|
||
return status;
|
||
}
|
||
}
|
||
|
||
newDeviceAction = stack->Parameters.Power.ShutdownType;
|
||
|
||
if (newDeviceState > oldDeviceState) {
|
||
|
||
//
|
||
// We are entering a deeper sleep state. Save away the appropriate
|
||
// state and update our hardware. Note that this particular driver does
|
||
// not care to distinguish Hibernates from shutdowns or standbys. If we
|
||
// did the logic would also have to examine newDeviceAction.
|
||
//
|
||
PoSetPowerState(DeviceObject, DevicePowerState, newState);
|
||
|
||
} else if (newDeviceState < oldDeviceState) {
|
||
|
||
//
|
||
// We are entering a lighter sleep state. Restore the appropriate amount
|
||
// of state to our hardware.
|
||
//
|
||
PoSetPowerState(DeviceObject, DevicePowerState, newState);
|
||
}
|
||
|
||
if (newDeviceState == PowerDeviceD0) {
|
||
|
||
//
|
||
// Our hardware is now on again. Here we empty our existing queue of
|
||
// requests and let in new ones. Note that if this is a D0->D0 (ie
|
||
// no change) we will unblock our queue, which may have been blocked
|
||
// processing our Query-D IRP.
|
||
//
|
||
fdoData->QueueState = AllowRequests;
|
||
ProcessorProcessQueuedRequests(fdoData);
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
return FinishDevicePowerIrp(
|
||
DeviceObject,
|
||
Irp,
|
||
Direction,
|
||
status
|
||
);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FinishDevicePowerIrp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN IRP_DIRECTION Direction,
|
||
IN NTSTATUS Result
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the final step in D-IRP handling.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object.
|
||
|
||
Irp - pointer to an D-IRP.
|
||
|
||
Direction - Whether to forward the D-IRP down or not.
|
||
This depends on whether the system is powering
|
||
up or down.
|
||
|
||
Result -
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PFDO_DATA fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
|
||
DebugEnter();
|
||
|
||
if (Direction == IRP_ALREADY_FORWARDED || (!NT_SUCCESS(Result))) {
|
||
|
||
//
|
||
// In either of these cases it is now time to complete the IRP.
|
||
//
|
||
PoStartNextPowerIrp(Irp);
|
||
Irp->IoStatus.Status = Result;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
ProcessorIoDecrement(fdoData);
|
||
return Result;
|
||
}
|
||
|
||
//
|
||
// Here we update our result. Note that ProcessorDefaultPowerHandler calls
|
||
// PoStartNextPowerIrp for us.
|
||
//
|
||
Irp->IoStatus.Status = Result;
|
||
status = ProcessorDefaultPowerHandler(DeviceObject, Irp);
|
||
ProcessorIoDecrement(fdoData);
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
HoldIoRequests(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN IRP_DIRECTION Direction
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets queue state and queues an item to
|
||
HoldIoRequestsWorkerRoutine.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
NT status code
|
||
|
||
--*/
|
||
{
|
||
|
||
PIO_WORKITEM item;
|
||
PWORKER_THREAD_CONTEXT context;
|
||
NTSTATUS status;
|
||
PFDO_DATA fdoData;
|
||
|
||
DebugEnter();
|
||
|
||
fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
|
||
fdoData->QueueState = HoldRequests;
|
||
|
||
//
|
||
// We must wait for the pending I/Os to finish
|
||
// before powering down the device. But we can't wait
|
||
// while handling a power IRP because it can deadlock
|
||
// the system. So let us queue a worker callback
|
||
// item to do the wait and complete the irp.
|
||
//
|
||
context = ExAllocatePoolWithTag(PagedPool,
|
||
sizeof(WORKER_THREAD_CONTEXT),
|
||
PROCESSOR_POOL_TAG);
|
||
if(context)
|
||
{
|
||
item = IoAllocateWorkItem(DeviceObject);
|
||
context->Irp = Irp;
|
||
context->DeviceObject= DeviceObject;
|
||
context->IrpDirection = Direction;
|
||
context->WorkItem = item;
|
||
if (item) {
|
||
|
||
IoMarkIrpPending(Irp);
|
||
IoQueueWorkItem (item,
|
||
HoldIoRequestsWorkerRoutine,
|
||
DelayedWorkQueue,
|
||
context);
|
||
status = STATUS_PENDING;
|
||
}
|
||
else
|
||
{
|
||
ExFreePool(context);
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
else
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
HoldIoRequestsWorkerRoutine(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine waits for the I/O in progress to finish and
|
||
power downs the device.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PFDO_DATA fdoData;
|
||
PWORKER_THREAD_CONTEXT context = (PWORKER_THREAD_CONTEXT)Context;
|
||
|
||
DebugEnter();
|
||
|
||
fdoData = (PFDO_DATA) DeviceObject->DeviceExtension;
|
||
|
||
DebugPrint((TRACE, "Waiting for pending requests to complete\n"));
|
||
|
||
//
|
||
// Wait for the I/O in progress to be finish.
|
||
// The Stop event gets set when the counter drops
|
||
// to Zero. Since our power handler routines incremented
|
||
// the counter twice - once for the S-IRP and once for the
|
||
// D-IRP - we must call the decrement function twice.
|
||
//
|
||
|
||
ProcessorIoDecrement(fdoData); // one
|
||
ProcessorIoDecrement(fdoData);
|
||
|
||
KeWaitForSingleObject(
|
||
&fdoData->StopEvent,
|
||
Executive, // Waiting for reason of a driver
|
||
KernelMode, // Waiting in kernel mode
|
||
FALSE, // No allert
|
||
NULL); // No timeout
|
||
|
||
//
|
||
// Increment the counter back to take into account the S-IRP and D-IRP
|
||
// currently in progress.
|
||
//
|
||
|
||
ProcessorIoIncrement (fdoData);
|
||
ProcessorIoIncrement (fdoData);
|
||
|
||
FinishDevicePowerIrp(
|
||
context->DeviceObject,
|
||
context->Irp,
|
||
context->IrpDirection,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
//
|
||
// Cleanup before exiting from the worker thread.
|
||
//
|
||
IoFreeWorkItem(context->WorkItem);
|
||
ExFreePool((PVOID)context);
|
||
|
||
}
|
||
|
||
VOID
|
||
ProcessorPowerStateCallback(
|
||
IN PVOID CallbackContext,
|
||
IN PVOID Argument1,
|
||
IN PVOID Argument2
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
ULONG_PTR action = (ULONG_PTR)Argument1;
|
||
ULONG_PTR state = (ULONG_PTR)Argument2;
|
||
|
||
DebugEnter();
|
||
DisplayPowerStateInfo(action, state);
|
||
|
||
if (action == PO_CB_SYSTEM_STATE_LOCK) {
|
||
|
||
switch (state) {
|
||
case 0:
|
||
|
||
//
|
||
// Lock down everything in the PAGELK code section.
|
||
//
|
||
|
||
ProcessorSleepPageLock = MmLockPagableCodeSection((PVOID)ProcessorDispatchPower);
|
||
break;
|
||
|
||
case 1:
|
||
|
||
//
|
||
// unlock it all
|
||
//
|
||
|
||
MmUnlockPagableImageSection(ProcessorSleepPageLock);
|
||
break;
|
||
|
||
default:
|
||
|
||
DebugPrint((TRACE, "Unknown callback operation.\n"));
|
||
}
|
||
|
||
} else if (action == PO_CB_AC_STATUS) {
|
||
|
||
//
|
||
// AC <-> DC Transition has occurred, call Notify routine.
|
||
// State == TRUE if on AC, else FALSE.
|
||
//
|
||
|
||
// toddcar - 02/14/01 - ISSUE:
|
||
// should we call this sync or async?
|
||
//
|
||
if (AcDcTransitionNotifyHandler.Handler) {
|
||
AcDcTransitionNotifyHandler.Handler(AcDcTransitionNotifyHandler.Context,
|
||
(BOOLEAN) state);
|
||
}
|
||
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RegisterAcDcTransitionNotifyHandler (
|
||
IN PAC_DC_NOTIFY_HANDLER NewHandler,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// Notify handler can only be registered once.
|
||
//
|
||
|
||
if (AcDcTransitionNotifyHandler.Handler && NewHandler) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
//
|
||
// Set new handler
|
||
//
|
||
|
||
AcDcTransitionNotifyHandler.Handler = NewHandler;
|
||
AcDcTransitionNotifyHandler.Context = Context;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|