1276 lines
40 KiB
C
1276 lines
40 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
pocall
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
PoCallDriver and related routines.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Bryan Willman (bryanwi) 14-Nov-1996
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pop.h"
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PIRP
|
|||
|
PopFindIrpByInrush(
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PopPresentIrp(
|
|||
|
PIO_STACK_LOCATION IrpSp,
|
|||
|
PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PopPassivePowerCall(
|
|||
|
PVOID Parameter
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PopCompleteRequestIrp (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
#if 0
|
|||
|
#define PATHTEST(a) DbgPrint(a)
|
|||
|
#else
|
|||
|
#define PATHTEST(a)
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGELK, PopSystemIrpDispatchWorker)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NTKERNELAPI
|
|||
|
NTSTATUS
|
|||
|
PoCallDriver (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN OUT PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the routine that must be used to send an
|
|||
|
IRP_MJ_POWER irp to device drivers.
|
|||
|
|
|||
|
It performs specialized synchronization on power operations
|
|||
|
for device drivers.
|
|||
|
|
|||
|
NOTE WELL:
|
|||
|
|
|||
|
All callers to PoCallDriver MUST set the current io
|
|||
|
stack location parameter value SystemContext to 0,
|
|||
|
unless they are passing on an IRP to lower drivers,
|
|||
|
in which case they must copy the value from above.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - the device object the irp is to be routed to
|
|||
|
|
|||
|
Irp - pointer to the irp of interest
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Normal NTSTATUS data.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PIO_STACK_LOCATION irpsp;
|
|||
|
PDEVOBJ_EXTENSION doe;
|
|||
|
PWORK_QUEUE_ITEM pwi;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
|
|||
|
ASSERT(KeGetCurrentIrql()<=DISPATCH_LEVEL);
|
|||
|
PopLockIrpSerialList(&oldIrql);
|
|||
|
|
|||
|
|
|||
|
irpsp = IoGetNextIrpStackLocation(Irp);
|
|||
|
doe = DeviceObject->DeviceObjectExtension;
|
|||
|
irpsp->DeviceObject = DeviceObject;
|
|||
|
|
|||
|
ASSERT(irpsp->MajorFunction == IRP_MJ_POWER);
|
|||
|
|
|||
|
PoPowerTrace(POWERTRACE_CALL,DeviceObject,Irp,irpsp);
|
|||
|
if (DeviceObject->Flags & DO_POWER_NOOP) {
|
|||
|
PATHTEST("PoCallDriver #01\n");
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = 0L;
|
|||
|
|
|||
|
// we *don't* need to call PoStartNextPowerIrp() because we'll
|
|||
|
// never enqueue anything for this DO, so there will never be
|
|||
|
// any other IRP to run.
|
|||
|
|
|||
|
IoCompleteRequest(Irp, 0);
|
|||
|
PopUnlockIrpSerialList(oldIrql);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
if (irpsp->MinorFunction != IRP_MN_SET_POWER &&
|
|||
|
irpsp->MinorFunction != IRP_MN_QUERY_POWER) {
|
|||
|
|
|||
|
PopUnlockIrpSerialList(oldIrql);
|
|||
|
return IoCallDriver (DeviceObject, Irp);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We never query going up, so being inrush sensitive
|
|||
|
// only matters for SET_POWER to D0
|
|||
|
// If this is an inrush sensitive DevObj, and we're going TO PowerDeviceD0,
|
|||
|
// then serialize on the gobal Inrush flag.
|
|||
|
//
|
|||
|
if ((irpsp->MinorFunction == IRP_MN_SET_POWER) &&
|
|||
|
(irpsp->Parameters.Power.Type == DevicePowerState) &&
|
|||
|
(irpsp->Parameters.Power.State.DeviceState == PowerDeviceD0) &&
|
|||
|
(PopGetDoDevicePowerState(doe) != PowerDeviceD0) &&
|
|||
|
(DeviceObject->Flags & DO_POWER_INRUSH))
|
|||
|
{
|
|||
|
PATHTEST("PoCallDriver #02\n");
|
|||
|
|
|||
|
if (PopInrushIrpPointer == Irp) {
|
|||
|
|
|||
|
//
|
|||
|
// This irp has already been identified as an INRUSH irp,
|
|||
|
// and it is the active inrush irp,
|
|||
|
// so it can actually just continue on, after we increment
|
|||
|
// the ref count
|
|||
|
//
|
|||
|
PATHTEST("PoCallDriver #03\n");
|
|||
|
ASSERT((irpsp->Parameters.Power.SystemContext & POP_INRUSH_CONTEXT) == POP_INRUSH_CONTEXT);
|
|||
|
PopInrushIrpReferenceCount++;
|
|||
|
if (PopInrushIrpReferenceCount > 256) {
|
|||
|
PopInternalAddToDumpFile ( irpsp, sizeof(IO_STACK_LOCATION), DeviceObject, NULL, NULL, NULL );
|
|||
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 0x400, 1, (ULONG_PTR)irpsp, (ULONG_PTR)DeviceObject);
|
|||
|
}
|
|||
|
|
|||
|
} else if ((!PopInrushIrpPointer) && (!PopInrushPending)) {
|
|||
|
|
|||
|
//
|
|||
|
// This is a freshly starting inrush IRP, AND there is not
|
|||
|
// already an inrush irp, so mark this as an inrush irp,
|
|||
|
// note that inrush is active, and continue
|
|||
|
//
|
|||
|
PATHTEST("PoCallDriver #04\n");
|
|||
|
PopInrushIrpPointer = Irp;
|
|||
|
PopInrushIrpReferenceCount = 1;
|
|||
|
irpsp->Parameters.Power.SystemContext = POP_INRUSH_CONTEXT;
|
|||
|
|
|||
|
//
|
|||
|
// Inrush irps will cause us to free the processor throttling.
|
|||
|
//
|
|||
|
PopPerfHandleInrush ( TRUE );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
PATHTEST("PoCallDriver #05\n");
|
|||
|
ASSERT(PopInrushIrpPointer || PopInrushPending);
|
|||
|
//
|
|||
|
// There is already an active Inrush irp, and this one isn't it.
|
|||
|
// OR there is an inrush irp blocked on the queue, in either case,
|
|||
|
// mark this as an inrush irp and enqueue it.
|
|||
|
//
|
|||
|
doe->PowerFlags |= POPF_DEVICE_PENDING;
|
|||
|
irpsp->Parameters.Power.SystemContext = POP_INRUSH_CONTEXT;
|
|||
|
InsertTailList(
|
|||
|
&PopIrpSerialList,
|
|||
|
&(Irp->Tail.Overlay.ListEntry)
|
|||
|
);
|
|||
|
PopIrpSerialListLength++;
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (PopIrpSerialListLength > 10) {
|
|||
|
DbgPrint("WARNING: PopIrpSerialListLength > 10!!!\n");
|
|||
|
}
|
|||
|
if (PopIrpSerialListLength > 100) {
|
|||
|
DbgPrint("WARNING: PopIrpSerialListLength > **100**!!!\n");
|
|||
|
PopInternalAddToDumpFile ( &PopIrpSerialList, PAGE_SIZE, DeviceObject, NULL, NULL, NULL );
|
|||
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 0x401, 2, (ULONG_PTR)&PopIrpSerialList, (ULONG_PTR)DeviceObject);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
PopInrushPending = TRUE;
|
|||
|
PopUnlockIrpSerialList(oldIrql);
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// See if there is already a power irp active for this
|
|||
|
// device object. If not, send this one on. If so, enqueue
|
|||
|
// it to wait.
|
|||
|
//
|
|||
|
if (irpsp->Parameters.Power.Type == SystemPowerState) {
|
|||
|
|
|||
|
PATHTEST("PoCallDriver #06\n");
|
|||
|
|
|||
|
if (doe->PowerFlags & POPF_SYSTEM_ACTIVE) {
|
|||
|
|
|||
|
//
|
|||
|
// we already have one active system power state irp for the devobj,
|
|||
|
// so enqueue this one on the global power irp holding list,
|
|||
|
// and set the pending bit.
|
|||
|
//
|
|||
|
PATHTEST("PoCallDriver #07\n");
|
|||
|
doe->PowerFlags |= POPF_SYSTEM_PENDING;
|
|||
|
InsertTailList(
|
|||
|
&PopIrpSerialList,
|
|||
|
(&(Irp->Tail.Overlay.ListEntry))
|
|||
|
);
|
|||
|
PopIrpSerialListLength++;
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (PopIrpSerialListLength > 10) {
|
|||
|
DbgPrint("WARNING: PopIrpSerialListLength > 10!!!\n");
|
|||
|
}
|
|||
|
if (PopIrpSerialListLength > 100) {
|
|||
|
DbgPrint("WARNING: PopIrpSerialListLength > **100**!!!\n");
|
|||
|
PopInternalAddToDumpFile ( &PopIrpSerialList, PAGE_SIZE, DeviceObject, NULL, NULL, NULL );
|
|||
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 0x402, 3, (ULONG_PTR)&PopIrpSerialList, (ULONG_PTR)DeviceObject);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
PopUnlockIrpSerialList(oldIrql);
|
|||
|
return STATUS_PENDING;
|
|||
|
} else {
|
|||
|
PATHTEST("PoCallDriver #08\n");
|
|||
|
doe->PowerFlags |= POPF_SYSTEM_ACTIVE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (irpsp->Parameters.Power.Type == DevicePowerState) {
|
|||
|
|
|||
|
PATHTEST("PoCallDriver #09\n");
|
|||
|
|
|||
|
if ((doe->PowerFlags & POPF_DEVICE_ACTIVE) ||
|
|||
|
(doe->PowerFlags & POPF_DEVICE_PENDING))
|
|||
|
{
|
|||
|
//
|
|||
|
// we already have one active device power state irp for the devobj,
|
|||
|
// OR we're behind an inrush irp (if pending but not active)
|
|||
|
// so enqueue this irp on the global power irp holdinglist,
|
|||
|
// and set the pending bit.
|
|||
|
//
|
|||
|
PATHTEST("PoCallDriver #10\n");
|
|||
|
doe->PowerFlags |= POPF_DEVICE_PENDING;
|
|||
|
InsertTailList(
|
|||
|
&PopIrpSerialList,
|
|||
|
&(Irp->Tail.Overlay.ListEntry)
|
|||
|
);
|
|||
|
PopIrpSerialListLength++;
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (PopIrpSerialListLength > 10) {
|
|||
|
DbgPrint("WARNING: PopIrpSerialListLength > 10!!!\n");
|
|||
|
}
|
|||
|
if (PopIrpSerialListLength > 100) {
|
|||
|
DbgPrint("WARNING: PopIrpSerialListLength > **100**!!!\n");
|
|||
|
PopInternalAddToDumpFile ( &PopIrpSerialList, PAGE_SIZE, DeviceObject, NULL, NULL, NULL );
|
|||
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 0x403, 4, (ULONG_PTR)&PopIrpSerialList, (ULONG_PTR)DeviceObject);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
PopUnlockIrpSerialList(oldIrql);
|
|||
|
return STATUS_PENDING;
|
|||
|
} else {
|
|||
|
PATHTEST("PoCallDriver #11\n");
|
|||
|
doe->PowerFlags |= POPF_DEVICE_ACTIVE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we get here it's time to send this IRP on to the driver.
|
|||
|
// If the driver is NOT marked INRUSH and it IS marked PAGABLE
|
|||
|
// (which is hopefully the normal case) we will arrange to call
|
|||
|
// it from PASSIVE_LEVEL.
|
|||
|
//
|
|||
|
// If it is NOT pagable or IS INRUSH, we will arrange to call
|
|||
|
// it from DPC level.
|
|||
|
//
|
|||
|
// Note that if a driver is marked INRUSH, it will ALWAYS be called
|
|||
|
// from DPC level with power irps, even though some of them may not
|
|||
|
// be inrush irps.
|
|||
|
//
|
|||
|
// having your driver be both PAGABLE and INRUSH is incorrect
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
ASSERT(irpsp->DeviceObject->DeviceObjectExtension->PowerFlags & (POPF_DEVICE_ACTIVE | POPF_SYSTEM_ACTIVE));
|
|||
|
PopUnlockIrpSerialList(oldIrql);
|
|||
|
status = PopPresentIrp(irpsp, Irp);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PopPresentIrp(
|
|||
|
PIO_STACK_LOCATION IrpSp,
|
|||
|
PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
When PoCallDriver, PoCompleteRequest, etc, need to actually present
|
|||
|
an Irp to a devobj, they call PopPresentIrp.
|
|||
|
|
|||
|
This routine will compute whether the Irp should be presented at
|
|||
|
PASSIVE or DISPATCH level, and make an appropriately structured call
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IrpSp - provides current stack location in Irp of interest
|
|||
|
|
|||
|
Irp - provides irp of interest
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Normal NTSTATUS data.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PWORK_QUEUE_ITEM pwi;
|
|||
|
PDEVICE_OBJECT devobj;
|
|||
|
BOOLEAN PassiveLevel;
|
|||
|
KIRQL OldIrql;
|
|||
|
|
|||
|
PATHTEST("PopPresentIrp #01\n");
|
|||
|
devobj = IrpSp->DeviceObject;
|
|||
|
|
|||
|
ASSERT (IrpSp->MajorFunction == IRP_MJ_POWER);
|
|||
|
PassiveLevel = TRUE;
|
|||
|
if (IrpSp->MinorFunction == IRP_MN_SET_POWER &&
|
|||
|
(!(devobj->Flags & DO_POWER_PAGABLE) || (devobj->Flags & DO_POWER_INRUSH)) ) {
|
|||
|
|
|||
|
if ((PopCallSystemState & PO_CALL_NON_PAGED) ||
|
|||
|
( (IrpSp->Parameters.Power.Type == DevicePowerState &&
|
|||
|
IrpSp->Parameters.Power.State.DeviceState == PowerDeviceD0) ||
|
|||
|
(IrpSp->Parameters.Power.Type == SystemPowerState &&
|
|||
|
IrpSp->Parameters.Power.State.SystemState == PowerSystemWorking)) ) {
|
|||
|
|
|||
|
PassiveLevel = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
PoPowerTrace(POWERTRACE_PRESENT,devobj,Irp,IrpSp);
|
|||
|
if (PassiveLevel)
|
|||
|
{
|
|||
|
//
|
|||
|
// WARNING: A WORK_QUEUE_ITEM must fit in the DriverContext field of an IRP
|
|||
|
//
|
|||
|
ASSERT(sizeof(WORK_QUEUE_ITEM) <= sizeof(Irp->Tail.Overlay.DriverContext));
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ((IrpSp->Parameters.Power.SystemContext & POP_INRUSH_CONTEXT) == POP_INRUSH_CONTEXT) {
|
|||
|
//
|
|||
|
// we are sending an inrush irp off to a passive dispatch devobj
|
|||
|
// this is *probably* a bug
|
|||
|
//
|
|||
|
KdPrint(("PopPresentIrp: inrush irp to passive level dispatch!!!\n"));
|
|||
|
PopInternalAddToDumpFile ( IrpSp, sizeof(IO_STACK_LOCATION), devobj, NULL, NULL, NULL );
|
|||
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 0x404, 5, (ULONG_PTR)IrpSp, (ULONG_PTR)devobj);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
PATHTEST("PopPresentIrp #02\n");
|
|||
|
|
|||
|
//
|
|||
|
// If we're already at passive level, just dispatch the irp
|
|||
|
//
|
|||
|
|
|||
|
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
|||
|
|
|||
|
status = IoCallDriver(IrpSp->DeviceObject, Irp);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Irp needs to be queued to some worker thread before
|
|||
|
// it can be dispatched. Mark it pending
|
|||
|
//
|
|||
|
|
|||
|
IrpSp->Control |= SL_PENDING_RETURNED;
|
|||
|
status = STATUS_PENDING;
|
|||
|
|
|||
|
PopLockWorkerQueue(&OldIrql);
|
|||
|
|
|||
|
if (PopCallSystemState & PO_CALL_SYSDEV_QUEUE) {
|
|||
|
|
|||
|
//
|
|||
|
// Queue to dedicated system power worker thread
|
|||
|
//
|
|||
|
|
|||
|
InsertTailList (&PopAction.DevState->PresentIrpQueue, &(Irp->Tail.Overlay.ListEntry));
|
|||
|
KeSetEvent (&PopAction.DevState->Event, IO_NO_INCREMENT, FALSE);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Queue to generic system worker thread
|
|||
|
//
|
|||
|
|
|||
|
pwi = (PWORK_QUEUE_ITEM)(&(Irp->Tail.Overlay.DriverContext[0]));
|
|||
|
ExInitializeWorkItem(pwi, PopPassivePowerCall, Irp);
|
|||
|
ExQueueWorkItem(pwi, DelayedWorkQueue);
|
|||
|
}
|
|||
|
|
|||
|
PopUnlockWorkerQueue(OldIrql);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Non-blocking request. To ensure proper behaviour, dispatch
|
|||
|
// the irp from dispatch_level
|
|||
|
//
|
|||
|
PATHTEST("PopPresentIrp #03\n");
|
|||
|
#if DBG
|
|||
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
|||
|
status = IoCallDriver(IrpSp->DeviceObject, Irp);
|
|||
|
KeLowerIrql(OldIrql);
|
|||
|
#else
|
|||
|
status = IoCallDriver(IrpSp->DeviceObject, Irp);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
PopPassivePowerCall(
|
|||
|
PVOID Parameter
|
|||
|
)
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpsp;
|
|||
|
PIRP Irp;
|
|||
|
PDEVICE_OBJECT devobj;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// Parameter points to Irp we are to send to driver
|
|||
|
//
|
|||
|
PATHTEST("PopPassivePowerCall #01\n");
|
|||
|
Irp = (PIRP)Parameter;
|
|||
|
irpsp = IoGetNextIrpStackLocation(Irp);
|
|||
|
devobj = irpsp->DeviceObject;
|
|||
|
status = IoCallDriver(devobj, Irp);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTKERNELAPI
|
|||
|
VOID
|
|||
|
PoStartNextPowerIrp(
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This procedure must be applied to every power irp, and only
|
|||
|
power irps, when a driver is finished with them.
|
|||
|
|
|||
|
It will force post-irp completion items relevent to the irp
|
|||
|
to execute:
|
|||
|
|
|||
|
a. If the irp is an inrush irp, and this is the top of the
|
|||
|
inrush irp stack, then this particular inrush irp is done,
|
|||
|
and we go find the next inrush irp (if any) and dispatch it.
|
|||
|
|
|||
|
b. If step a. did NOT send an irp to the dev obj we came
|
|||
|
from, it is eligible for step c, otherwise it is not.
|
|||
|
|
|||
|
c. If anything is pending on the dev obj, of the type that
|
|||
|
just completed, find the waiting irp and post it to the
|
|||
|
driver.
|
|||
|
|
|||
|
This routine will NOT complete the Irp, the driver must do that.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - pointer to the irp of interest
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpsp;
|
|||
|
PIO_STACK_LOCATION nextsp;
|
|||
|
PIO_STACK_LOCATION secondsp;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PDEVOBJ_EXTENSION doe;
|
|||
|
KIRQL oldirql;
|
|||
|
PIRP nextirp;
|
|||
|
PIRP secondirp;
|
|||
|
PIRP hangirp;
|
|||
|
|
|||
|
irpsp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
ASSERT(irpsp->MajorFunction == IRP_MJ_POWER);
|
|||
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|||
|
deviceObject = irpsp->DeviceObject;
|
|||
|
doe = deviceObject->DeviceObjectExtension;
|
|||
|
nextirp = NULL;
|
|||
|
secondirp = NULL;
|
|||
|
|
|||
|
PoPowerTrace(POWERTRACE_STARTNEXT,deviceObject,Irp,irpsp);
|
|||
|
|
|||
|
//
|
|||
|
// a. if (partially completed inrush irp)
|
|||
|
// run any pending non-inrush irps on this DeviceObject, would be queued up
|
|||
|
// as DevicePowerState irps since inrush is always DevicePowerState
|
|||
|
//
|
|||
|
// b. else if (fully complete inrush irp)
|
|||
|
// clear the ir busy flag
|
|||
|
// find next inrush irp that applies to any DeviceObject
|
|||
|
// find any irps in queue for same DeviceObject ahead of inrush irp
|
|||
|
// if no leader, and target DeviceObject not DEVICE_ACTIVE, present inrush irp
|
|||
|
// else an active normal irp will unplug it all, so ignore that DeviceObject
|
|||
|
// [this makes sure next inrush is unstuck, wherever it is]
|
|||
|
// if no irp was presented, or an irp was presented to a DeviceObject other than us
|
|||
|
// look for next pending (non-inrush) irp to run on this DeviceObject
|
|||
|
// [this makes sure this DeviceObject is unstuck]
|
|||
|
//
|
|||
|
// c. else [normal irp has just completed]
|
|||
|
// find next irp of same type that applies to this DeviceObject
|
|||
|
// if (it's an inrush irp) && (inrush flag is set)
|
|||
|
// don't try to present anything
|
|||
|
// else
|
|||
|
// present the irp
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
PATHTEST("PoStartNextPowerIrp #01\n");
|
|||
|
PopLockIrpSerialList(&oldirql);
|
|||
|
|
|||
|
if (PopInrushIrpPointer == Irp) {
|
|||
|
|
|||
|
ASSERT((irpsp->Parameters.Power.SystemContext & POP_INRUSH_CONTEXT) == POP_INRUSH_CONTEXT);
|
|||
|
PATHTEST("PoStartNextPowerIrp #02\n");
|
|||
|
|
|||
|
if (PopInrushIrpReferenceCount > 1) {
|
|||
|
//
|
|||
|
// case a.
|
|||
|
// we have an inrush irp, and it has NOT completed all of its power
|
|||
|
// management work. therefore, do NOT try to run the next inrush
|
|||
|
// irp, but do try to run any non-inrush irp pending on this
|
|||
|
// device object
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #03\n");
|
|||
|
PopInrushIrpReferenceCount--;
|
|||
|
ASSERT(PopInrushIrpReferenceCount >= 0);
|
|||
|
|
|||
|
nextirp = PopFindIrpByDeviceObject(deviceObject, DevicePowerState);
|
|||
|
if (nextirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #04\n");
|
|||
|
nextsp = IoGetNextIrpStackLocation(nextirp);
|
|||
|
|
|||
|
if ( ! ((nextsp->Parameters.Power.SystemContext & POP_INRUSH_CONTEXT) == POP_INRUSH_CONTEXT)) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #05\n");
|
|||
|
RemoveEntryList((&(nextirp->Tail.Overlay.ListEntry)));
|
|||
|
PopIrpSerialListLength--;
|
|||
|
} else {
|
|||
|
PATHTEST("PoStartNextPowerIrp #06\n");
|
|||
|
nextirp = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!nextirp) {
|
|||
|
//
|
|||
|
// there's no more device irp waiting for this do, so
|
|||
|
// we can clear DO pending and active
|
|||
|
// but what if there's another inrush irp! no worries, it
|
|||
|
// will be run when the one we just partially finished completes.
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #07\n");
|
|||
|
doe->PowerFlags = doe->PowerFlags & ~POPF_DEVICE_ACTIVE;
|
|||
|
doe->PowerFlags = doe->PowerFlags & ~POPF_DEVICE_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
PopUnlockIrpSerialList(oldirql);
|
|||
|
|
|||
|
if (nextirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #08\n");
|
|||
|
ASSERT(nextsp->DeviceObject->DeviceObjectExtension->PowerFlags & POPF_DEVICE_ACTIVE);
|
|||
|
PopPresentIrp(nextsp, nextirp);
|
|||
|
}
|
|||
|
|
|||
|
return; // end of case a.
|
|||
|
} else {
|
|||
|
//
|
|||
|
// case b.
|
|||
|
// we've just completed the last work item of an inrush irp, so we
|
|||
|
// want to try to make the next inrush irp runnable.
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #09\n");
|
|||
|
PopInrushIrpReferenceCount--;
|
|||
|
ASSERT(PopInrushIrpReferenceCount == 0);
|
|||
|
nextirp = PopFindIrpByInrush();
|
|||
|
|
|||
|
if (nextirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #10\n");
|
|||
|
ASSERT(PopInrushPending);
|
|||
|
nextsp = IoGetNextIrpStackLocation(nextirp);
|
|||
|
hangirp = PopFindIrpByDeviceObject(nextsp->DeviceObject, DevicePowerState);
|
|||
|
|
|||
|
if (hangirp) {
|
|||
|
//
|
|||
|
// if we get where, there is a non inrush irp in front of the next inrush
|
|||
|
// irp, so try to run the non-inrush one, and set flags for later
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #11\n");
|
|||
|
nextirp = hangirp;
|
|||
|
PopInrushIrpPointer = NULL;
|
|||
|
PopInrushIrpReferenceCount = 0;
|
|||
|
nextsp = IoGetNextIrpStackLocation(nextirp);
|
|||
|
|
|||
|
//
|
|||
|
// Can allow processor voltages to swing again
|
|||
|
//
|
|||
|
PopPerfHandleInrush ( FALSE );
|
|||
|
|
|||
|
if (!(nextsp->DeviceObject->DeviceObjectExtension->PowerFlags & POPF_DEVICE_ACTIVE)) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #12\n");
|
|||
|
RemoveEntryList((&(nextirp->Tail.Overlay.ListEntry)));
|
|||
|
nextsp->DeviceObject->DeviceObjectExtension->PowerFlags |= POPF_DEVICE_ACTIVE;
|
|||
|
PopIrpSerialListLength--;
|
|||
|
} else {
|
|||
|
PATHTEST("PoStartNextPowerIrp #13\n");
|
|||
|
nextirp = NULL;
|
|||
|
nextsp = NULL;
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// we did find another inrush irp, and it's NOT block by a normal
|
|||
|
// irp, so we will run it.
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #14\n");
|
|||
|
RemoveEntryList((&(nextirp->Tail.Overlay.ListEntry)));
|
|||
|
nextsp->DeviceObject->DeviceObjectExtension->PowerFlags |= POPF_DEVICE_ACTIVE;
|
|||
|
PopIrpSerialListLength--;
|
|||
|
PopInrushIrpPointer = nextirp;
|
|||
|
PopInrushIrpReferenceCount = 1;
|
|||
|
}
|
|||
|
} else { // nextirp
|
|||
|
//
|
|||
|
// this inrush irp is done, and we didn't find any others
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #15\n");
|
|||
|
nextsp = NULL;
|
|||
|
PopInrushIrpPointer = NULL;
|
|||
|
PopInrushIrpReferenceCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Can allow processor voltages to swing again
|
|||
|
//
|
|||
|
PopPerfHandleInrush ( FALSE );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// see if *either* of the above possible irps is posted against
|
|||
|
// this devobj. if not, see if there's one to run here
|
|||
|
//
|
|||
|
if ( ! ((nextsp) && (nextsp->DeviceObject == deviceObject))) {
|
|||
|
//
|
|||
|
// same is if nextsp == null or nextsp->do != do..
|
|||
|
// either case, there may be one more irp to run
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #16\n");
|
|||
|
secondirp = PopFindIrpByDeviceObject(deviceObject, DevicePowerState);
|
|||
|
if (secondirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #17\n");
|
|||
|
secondsp = IoGetNextIrpStackLocation(secondirp);
|
|||
|
RemoveEntryList((&(secondirp->Tail.Overlay.ListEntry)));
|
|||
|
secondsp->DeviceObject->DeviceObjectExtension->PowerFlags |= POPF_DEVICE_ACTIVE;
|
|||
|
PopIrpSerialListLength--;
|
|||
|
} else {
|
|||
|
PATHTEST("PoStartNextPowerIrp #18\n");
|
|||
|
secondsp = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// nextsp/nextirp are not pending against us AND
|
|||
|
// secondsp/secondirp are not pending against us, SO
|
|||
|
// clear both pending and active flags
|
|||
|
//
|
|||
|
doe->PowerFlags = doe->PowerFlags & ~POPF_DEVICE_ACTIVE;
|
|||
|
doe->PowerFlags = doe->PowerFlags & ~POPF_DEVICE_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
PATHTEST("PoStartNextPowerIrp #19\n");
|
|||
|
secondirp = NULL;
|
|||
|
secondsp = NULL;
|
|||
|
//
|
|||
|
// nextsp/nextirp is coming right at us, so pending/active stay set
|
|||
|
//
|
|||
|
}
|
|||
|
} // end of case b.
|
|||
|
|
|||
|
} else if (irpsp->MinorFunction == IRP_MN_SET_POWER ||
|
|||
|
irpsp->MinorFunction == IRP_MN_QUERY_POWER) {
|
|||
|
|
|||
|
//
|
|||
|
// case c.
|
|||
|
//
|
|||
|
// might be pending inrush to run, might be just non-inrush to run
|
|||
|
//
|
|||
|
if (irpsp->Parameters.Power.Type == DevicePowerState) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #20\n");
|
|||
|
|
|||
|
if ((PopInrushIrpPointer == NULL) && (PopInrushPending)) {
|
|||
|
//
|
|||
|
// it may be that the completion of the ordinary irp
|
|||
|
// that brought us here has made some inrush irp runnable, AND
|
|||
|
// there isn't currently an active inrush irp, and there might be one pending
|
|||
|
// so try to find and run the next inrush irp
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #21\n");
|
|||
|
nextirp = PopFindIrpByInrush();
|
|||
|
|
|||
|
if (nextirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #22\n");
|
|||
|
nextsp = IoGetNextIrpStackLocation(nextirp);
|
|||
|
|
|||
|
if (!(nextsp->DeviceObject->DeviceObjectExtension->PowerFlags & POPF_DEVICE_ACTIVE)) {
|
|||
|
//
|
|||
|
// we've found an inrush irp, and it's runnable...
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #23\n");
|
|||
|
RemoveEntryList((&(nextirp->Tail.Overlay.ListEntry)));
|
|||
|
PopIrpSerialListLength--;
|
|||
|
nextsp->DeviceObject->DeviceObjectExtension->PowerFlags |= POPF_DEVICE_ACTIVE;
|
|||
|
PopInrushIrpPointer = nextirp;
|
|||
|
PopInrushIrpReferenceCount = 1;
|
|||
|
|
|||
|
//
|
|||
|
// Running Inrush irp. Disable processor throttling.
|
|||
|
//
|
|||
|
PopPerfHandleInrush ( TRUE );
|
|||
|
|
|||
|
} else {
|
|||
|
PATHTEST("PoStartNextPowerIrp #24\n");
|
|||
|
nextirp = NULL;
|
|||
|
nextsp = NULL;
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// no more inrush irps in queue
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #25\n");
|
|||
|
nextsp = NULL;
|
|||
|
PopInrushPending = FALSE;
|
|||
|
}
|
|||
|
} else { // end of inrush
|
|||
|
PATHTEST("PoStartNextPowerIrp #26\n");
|
|||
|
nextirp = NULL;
|
|||
|
nextsp = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// look for for next devicepowerstate irp for this DeviceObject
|
|||
|
// unless we're already found an inrush irp, and it's for us
|
|||
|
//
|
|||
|
if ( ! ((nextirp) && (nextsp->DeviceObject == deviceObject))) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #27\n");
|
|||
|
secondirp = PopFindIrpByDeviceObject(deviceObject, DevicePowerState);
|
|||
|
|
|||
|
if (!secondirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #28\n");
|
|||
|
doe->PowerFlags = doe->PowerFlags & ~POPF_DEVICE_ACTIVE;
|
|||
|
doe->PowerFlags = doe->PowerFlags & ~POPF_DEVICE_PENDING;
|
|||
|
}
|
|||
|
} else {
|
|||
|
PATHTEST("PoStartNextPowerIrp #29\n");
|
|||
|
secondirp = NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} else if (irpsp->Parameters.Power.Type == SystemPowerState) {
|
|||
|
|
|||
|
//
|
|||
|
// look for next systempowerstate irp for this DeviceObject
|
|||
|
//
|
|||
|
PATHTEST("PoStartNextPowerIrp #30\n");
|
|||
|
nextirp = NULL;
|
|||
|
nextsp = NULL;
|
|||
|
secondirp = PopFindIrpByDeviceObject(deviceObject, SystemPowerState);
|
|||
|
if (!secondirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #31\n");
|
|||
|
doe->PowerFlags = doe->PowerFlags & ~POPF_SYSTEM_ACTIVE;
|
|||
|
doe->PowerFlags = doe->PowerFlags & ~POPF_SYSTEM_PENDING;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (secondirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #33\n");
|
|||
|
secondsp = IoGetNextIrpStackLocation(secondirp);
|
|||
|
RemoveEntryList((&(secondirp->Tail.Overlay.ListEntry)));
|
|||
|
PopIrpSerialListLength--;
|
|||
|
}
|
|||
|
|
|||
|
} else { // end of case c.
|
|||
|
PoPrint(PO_POCALL, ("PoStartNextPowerIrp: Irp @ %08x, minor function %d\n",
|
|||
|
Irp, irpsp->MinorFunction
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PopUnlockIrpSerialList(oldirql);
|
|||
|
|
|||
|
//
|
|||
|
// case b. and case c. might both make two pending irps runnable,
|
|||
|
// could be a normal irp and an inrush irp, or only 1 of the two, or neither of the two
|
|||
|
//
|
|||
|
if (nextirp || secondirp) {
|
|||
|
|
|||
|
if (nextirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #34\n");
|
|||
|
ASSERT(nextsp->DeviceObject->DeviceObjectExtension->PowerFlags & (POPF_DEVICE_ACTIVE | POPF_SYSTEM_ACTIVE));
|
|||
|
PopPresentIrp(nextsp, nextirp);
|
|||
|
}
|
|||
|
|
|||
|
if (secondirp) {
|
|||
|
PATHTEST("PoStartNextPowerIrp #35\n");
|
|||
|
ASSERT(secondsp->DeviceObject->DeviceObjectExtension->PowerFlags & (POPF_DEVICE_ACTIVE | POPF_SYSTEM_ACTIVE));
|
|||
|
PopPresentIrp(secondsp, secondirp);
|
|||
|
}
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PIRP
|
|||
|
PopFindIrpByInrush(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This procedure runs the irp serial list (which contains all
|
|||
|
waiting irps, be they queued up on a single device object or
|
|||
|
multiple inrush irps) looking for the first inrush irp.
|
|||
|
If one is found, it's address is returned, with it still enqueued
|
|||
|
in the list.
|
|||
|
|
|||
|
Caller must be holding PopIrpSerialList lock.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY item;
|
|||
|
PIRP irp;
|
|||
|
PIO_STACK_LOCATION irpsp;
|
|||
|
|
|||
|
item = PopIrpSerialList.Flink;
|
|||
|
while (item != &PopIrpSerialList) {
|
|||
|
|
|||
|
irp = CONTAINING_RECORD(item, IRP, Tail.Overlay.ListEntry);
|
|||
|
irpsp = IoGetNextIrpStackLocation(irp);
|
|||
|
|
|||
|
if ((irpsp->Parameters.Power.SystemContext & POP_INRUSH_CONTEXT) == POP_INRUSH_CONTEXT) {
|
|||
|
//
|
|||
|
// we've found an inrush irp
|
|||
|
//
|
|||
|
return irp;
|
|||
|
}
|
|||
|
item = item->Flink;
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
PIRP
|
|||
|
PopFindIrpByDeviceObject(
|
|||
|
PDEVICE_OBJECT DeviceObject,
|
|||
|
POWER_STATE_TYPE Type
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This procedure runs the irp serial list (which contains all
|
|||
|
waiting irps, be they queued up on a single device object or
|
|||
|
multiple inrush irps) looking for the first irp that applies
|
|||
|
the the supplied device driver. If one is found, its address,
|
|||
|
while still in the list, is returned. Else, null is returned.
|
|||
|
|
|||
|
Caller must be holding PopIrpSerialList lock.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - address of device object we're looking for the next irp for
|
|||
|
|
|||
|
Type - whether an irp of type SystemPowerState, DevicePowerState, etc, is wanted
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
address of found irp, or null if none.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY item;
|
|||
|
PIRP irp;
|
|||
|
PIO_STACK_LOCATION irpsp;
|
|||
|
|
|||
|
for(item = PopIrpSerialList.Flink;
|
|||
|
item != &PopIrpSerialList;
|
|||
|
item = item->Flink)
|
|||
|
{
|
|||
|
irp = CONTAINING_RECORD(item, IRP, Tail.Overlay.ListEntry);
|
|||
|
irpsp = IoGetNextIrpStackLocation(irp);
|
|||
|
|
|||
|
if (irpsp->DeviceObject == DeviceObject) {
|
|||
|
//
|
|||
|
// we've found a waiting irp that applies to the device object
|
|||
|
// the caller is interested in
|
|||
|
//
|
|||
|
if (irpsp->Parameters.Power.Type == Type) {
|
|||
|
//
|
|||
|
// irp is of the type that the caller wants
|
|||
|
//
|
|||
|
return irp;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PoRequestPowerIrp (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN UCHAR MinorFunction,
|
|||
|
IN POWER_STATE PowerState,
|
|||
|
IN PREQUEST_POWER_COMPLETE CompletionFunction,
|
|||
|
IN PVOID Context,
|
|||
|
OUT PIRP *ResultIrp OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This allocates a device power irp and sends it to the top of the
|
|||
|
PDO stack for the passed device object. When the irp completes,
|
|||
|
the CompletionFunction is called.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - address of a device object who's stack is to get the
|
|||
|
device power irp
|
|||
|
|
|||
|
MinorFunction - Minor fuction code for power irp
|
|||
|
|
|||
|
DeviceState - The DeviceState to send in the irp
|
|||
|
|
|||
|
CompletionFunction- The requestors completion function to invoke once the
|
|||
|
irp has completed
|
|||
|
|
|||
|
Context - The requestors context for the completion function
|
|||
|
|
|||
|
Irp - Irp which is only valid until CompletionFunction is called
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of the request
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PIRP Irp;
|
|||
|
PIO_STACK_LOCATION IrpSp;
|
|||
|
PDEVICE_OBJECT TargetDevice;
|
|||
|
POWER_ACTION IrpAction;
|
|||
|
|
|||
|
TargetDevice = IoGetAttachedDeviceReference (DeviceObject);
|
|||
|
Irp = IoAllocateIrp ((CCHAR) (TargetDevice->StackSize+2), FALSE);
|
|||
|
if (!Irp) {
|
|||
|
ObDereferenceObject (TargetDevice);
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
SPECIALIRP_WATERMARK_IRP(Irp, IRP_SYSTEM_RESTRICTED);
|
|||
|
|
|||
|
//
|
|||
|
// For debugging, keep a list of all outstanding irps allocated
|
|||
|
// through this function
|
|||
|
//
|
|||
|
|
|||
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|||
|
ExInterlockedInsertTailList(
|
|||
|
&PopRequestedIrps,
|
|||
|
(PLIST_ENTRY) &IrpSp->Parameters.Others.Argument1,
|
|||
|
&PopIrpSerialLock
|
|||
|
);
|
|||
|
IrpSp->Parameters.Others.Argument3 = Irp;
|
|||
|
IoSetNextIrpStackLocation (Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Save the datum needed to complete this request
|
|||
|
//
|
|||
|
|
|||
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|||
|
IrpSp->DeviceObject = TargetDevice;
|
|||
|
IrpSp->Parameters.Others.Argument1 = (PVOID) DeviceObject;
|
|||
|
IrpSp->Parameters.Others.Argument2 = (PVOID) MinorFunction;
|
|||
|
IrpSp->Parameters.Others.Argument3 = (PVOID) PowerState.DeviceState;
|
|||
|
IrpSp->Parameters.Others.Argument4 = (PVOID) Context;
|
|||
|
IoSetNextIrpStackLocation (Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Build the power irp
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED ;
|
|||
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|||
|
IrpSp->MajorFunction = IRP_MJ_POWER;
|
|||
|
IrpSp->MinorFunction = MinorFunction;
|
|||
|
IrpSp->DeviceObject = TargetDevice;
|
|||
|
switch (MinorFunction) {
|
|||
|
case IRP_MN_WAIT_WAKE:
|
|||
|
IrpSp->Parameters.WaitWake.PowerState = PowerState.SystemState;
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_SET_POWER:
|
|||
|
case IRP_MN_QUERY_POWER:
|
|||
|
IrpSp->Parameters.Power.SystemContext = POP_DEVICE_REQUEST;
|
|||
|
IrpSp->Parameters.Power.Type = DevicePowerState;
|
|||
|
IrpSp->Parameters.Power.State.DeviceState = PowerState.DeviceState;
|
|||
|
|
|||
|
//
|
|||
|
// N.B.
|
|||
|
//
|
|||
|
// You would think we stamp every D-state IRP with
|
|||
|
// PowerActionNone. However, we have a special scenario to consider
|
|||
|
// for hibernation. Let's say S4 goes to a stack. If the device is
|
|||
|
// on the hibernate path, one of two designs for WDM is possible:
|
|||
|
// (BTW, we chose the 2nd)
|
|||
|
//
|
|||
|
// 1) The FDO sees an S-IRP but because it's device is on the
|
|||
|
// hibernate path, it simply forwards the S Irp down. The PDO
|
|||
|
// takes note of the S-IRP being PowerSystemHibernate, and it
|
|||
|
// records hardware settings. Upon wake-up, the stack receives
|
|||
|
// an S0 IRP, which the FDO converts into a D0 request. Upon
|
|||
|
// receiving the D0 IRP, the PDO restores the settings.
|
|||
|
// 2) The FDO *always* requests the corresponding D IRP, regardless
|
|||
|
// of if it's on the hibernate path. The D-IRP also comes stamped
|
|||
|
// with the PowerAction in ShutdownType (ie, PowerActionSleeping,
|
|||
|
// PowerActionShutdown, PowerActionHibernate). Now the PDO can
|
|||
|
// identify transitions to D3 for the purpose of hibernation. The
|
|||
|
// PDO would *not* actually transition into D3, but it would save
|
|||
|
// it's state, and restore it at D0 time.
|
|||
|
//
|
|||
|
// < These are mutually exclusive designs >
|
|||
|
//
|
|||
|
// The reason we choose #2 as a design is so miniport models can
|
|||
|
// expose only D IRPs as neccessary, and S IRPs can be abstracted
|
|||
|
// out. There is a penalty for this design in that PoRequestPowerIrp
|
|||
|
// doesn't *take* a PowerAction or the old S-IRP, so we pick up the
|
|||
|
// existing action that the system is already undertaking.
|
|||
|
// Therefore, if the device powers itself on when the system decides
|
|||
|
// to begin a hibernation. the stack may receive nonsensical data
|
|||
|
// like an IRP_MN_SET_POWER(DevicePower, D0, PowerActionHibernate).
|
|||
|
//
|
|||
|
|
|||
|
IrpAction = PopMapInternalActionToIrpAction (
|
|||
|
PopAction.Action,
|
|||
|
PopAction.SystemState,
|
|||
|
TRUE // UnmapWarmEject
|
|||
|
);
|
|||
|
|
|||
|
IrpSp->Parameters.Power.ShutdownType = IrpAction;
|
|||
|
|
|||
|
//
|
|||
|
// Log the call.
|
|||
|
//
|
|||
|
|
|||
|
if (PERFINFO_IS_GROUP_ON(PERF_POWER)) {
|
|||
|
PopLogNotifyDevice(TargetDevice, NULL, Irp);
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
ObDereferenceObject (TargetDevice);
|
|||
|
IoFreeIrp (Irp);
|
|||
|
return STATUS_INVALID_PARAMETER_2;
|
|||
|
}
|
|||
|
|
|||
|
IoSetCompletionRoutine(
|
|||
|
Irp,
|
|||
|
PopCompleteRequestIrp,
|
|||
|
(PVOID) CompletionFunction,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE
|
|||
|
);
|
|||
|
|
|||
|
if (ResultIrp) {
|
|||
|
*ResultIrp = Irp;
|
|||
|
}
|
|||
|
|
|||
|
PoCallDriver(TargetDevice, Irp);
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PopCompleteRequestIrp (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Completion routine for PoRequestPowerChange. Invokes the
|
|||
|
requestors completion routine, and free resources associated
|
|||
|
with the request
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The target device which the request was sent
|
|||
|
|
|||
|
Irp - The irp completing
|
|||
|
|
|||
|
Context - The requestors completion routine
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_MORE_PROCESSING_REQUIRED is return to IO
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION IrpSp;
|
|||
|
PREQUEST_POWER_COMPLETE CompletionFunction;
|
|||
|
POWER_STATE PowerState;
|
|||
|
KIRQL OldIrql;
|
|||
|
|
|||
|
//
|
|||
|
// 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));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch to requestors completion function
|
|||
|
//
|
|||
|
|
|||
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
CompletionFunction = (PREQUEST_POWER_COMPLETE) Context;
|
|||
|
PowerState.DeviceState = (DEVICE_POWER_STATE) ((ULONG_PTR)IrpSp->Parameters.Others.Argument3);
|
|||
|
|
|||
|
if (CompletionFunction) {
|
|||
|
CompletionFunction (
|
|||
|
(PDEVICE_OBJECT) IrpSp->Parameters.Others.Argument1,
|
|||
|
(UCHAR) (ULONG_PTR)IrpSp->Parameters.Others.Argument2,
|
|||
|
PowerState,
|
|||
|
(PVOID) IrpSp->Parameters.Others.Argument4,
|
|||
|
&Irp->IoStatus
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Cleanup
|
|||
|
//
|
|||
|
|
|||
|
IoSkipCurrentIrpStackLocation(Irp);
|
|||
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
KeAcquireSpinLock (&PopIrpSerialLock, &OldIrql);
|
|||
|
RemoveEntryList ((PLIST_ENTRY) &IrpSp->Parameters.Others.Argument1);
|
|||
|
KeReleaseSpinLock (&PopIrpSerialLock, OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Mark the irp CurrentLocation as completed (to catch multiple completes)
|
|||
|
//
|
|||
|
|
|||
|
Irp->CurrentLocation = (CCHAR) (Irp->StackCount + 2);
|
|||
|
|
|||
|
ObDereferenceObject (DeviceObject);
|
|||
|
IoFreeIrp (Irp);
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PopSystemIrpDispatchWorker (
|
|||
|
IN BOOLEAN LastCall
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine runs whenever the policy manager calls us to tell us
|
|||
|
that a big burst of system irps, which need to be dispatched from
|
|||
|
a private thread (this one) rather than from an executive worker
|
|||
|
thread. This is mostly to avoid deadlocks at sleep time.
|
|||
|
|
|||
|
Globals:
|
|||
|
|
|||
|
PopWorkerLock - protects access to the queue, and avoids races
|
|||
|
over using this routine or using exec worker
|
|||
|
|
|||
|
PopWorkerItemQueue - list of irps to send off...
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
LastCall - Indicates irps are to be sent normally
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY Item;
|
|||
|
PIRP Irp;
|
|||
|
KIRQL OldIrql;
|
|||
|
|
|||
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|||
|
|
|||
|
PopLockWorkerQueue(&OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch everything on the queue
|
|||
|
//
|
|||
|
|
|||
|
if (PopAction.DevState != NULL) {
|
|||
|
while (!IsListEmpty(&PopAction.DevState->PresentIrpQueue)) {
|
|||
|
Item = RemoveHeadList(&PopAction.DevState->PresentIrpQueue);
|
|||
|
Irp = CONTAINING_RECORD(Item, IRP, Tail.Overlay.ListEntry);
|
|||
|
|
|||
|
PopUnlockWorkerQueue(OldIrql);
|
|||
|
PopPassivePowerCall(Irp);
|
|||
|
PopLockWorkerQueue(&OldIrql);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (LastCall) {
|
|||
|
PopCallSystemState = 0;
|
|||
|
}
|
|||
|
|
|||
|
PopUnlockWorkerQueue(OldIrql);
|
|||
|
}
|