windows-nt/Source/XPSP1/NT/base/hals/processor/lib/power.c

1206 lines
32 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}