|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
power.c
Abstract
Power handling
Author:
ervinp
Environment:
Kernel mode only
Revision History:
--*/
#include "pch.h"
BOOLEAN HidpIsWaitWakePending(FDO_EXTENSION *fdoExt, BOOLEAN setIfNotPending) { KIRQL irql; BOOLEAN isWaitWakePending;
KeAcquireSpinLock(&fdoExt->waitWakeSpinLock, &irql); isWaitWakePending = fdoExt->isWaitWakePending; if (fdoExt->isWaitWakePending == FALSE) { if (setIfNotPending) { fdoExt->isWaitWakePending = TRUE; } } KeReleaseSpinLock(&fdoExt->waitWakeSpinLock, irql);
return isWaitWakePending; }
VOID HidpPowerDownFdo(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension) { POWER_STATE powerState; FDO_EXTENSION *fdoExt;
DBGVERBOSE(("powering down fdo 0x%x\n", HidDeviceExtension));
fdoExt = &HidDeviceExtension->fdoExt;
powerState.DeviceState = fdoExt->deviceCapabilities.DeviceWake;
PoRequestPowerIrp(HidDeviceExtension->hidExt.PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, NULL, // completion routine
NULL, // completion routine context
NULL); }
VOID HidpPowerUpPdos(IN PFDO_EXTENSION fdoExt) { PDEVICE_OBJECT pdo; PDO_EXTENSION *pdoExt; POWER_STATE powerState; ULONG iPdo;
iPdo = 0;
powerState.DeviceState = PowerDeviceD0;
for (iPdo = 0; iPdo < fdoExt->deviceRelations->Count; iPdo++) { pdoExt = &fdoExt->collectionPdoExtensions[iPdo]->pdoExt; pdo = pdoExt->pdo;
DBGVERBOSE(("power up pdos, requesting D0 on pdo #%d %x\n", iPdo, pdo));
//
// We could check // pdoExt->devicePowerState != PowerDeviceD0
// but, if the stack gets 2 D0 irps in a row, nothing bad should happen
//
PoRequestPowerIrp(pdo, IRP_MN_SET_POWER, powerState, NULL, // completion routine
NULL, // context
NULL); } HidpSetDeviceBusy(fdoExt); KeSetEvent(&fdoExt->idleDoneEvent, 0, FALSE); }
VOID HidpPdoIdleOutComplete( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN PIO_STATUS_BLOCK IoStatus ) { FDO_EXTENSION *fdoExt = &HidDeviceExtension->fdoExt; LONG prevIdleState; BOOLEAN idleCancelling = FALSE; KIRQL irql;
DBGSUCCESS(IoStatus->Status, TRUE)
if (InterlockedDecrement(&fdoExt->numIdlePdos) == 0) { HidpPowerDownFdo(HidDeviceExtension);
KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
prevIdleState = InterlockedCompareExchange(&fdoExt->idleState, IdleComplete, IdleCallbackReceived); if (fdoExt->idleCancelling) { DBGINFO(("Cancelling idle in pdoidleoutcomplete on 0x%x\n", HidDeviceExtension)); idleCancelling = TRUE; }
DBGASSERT (prevIdleState == IdleCallbackReceived, ("Race condition in HidpPdoIdleOutComplete. Prev state = %x", prevIdleState), TRUE);
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
KeResetEvent(&fdoExt->idleDoneEvent); if (idleCancelling) { POWER_STATE powerState; powerState.DeviceState = PowerDeviceD0; DBGINFO(("Cancelling idle. Send power irp from pdo idle complete.")) PoRequestPowerIrp(((PHIDCLASS_DEVICE_EXTENSION) fdoExt->fdo->DeviceExtension)->hidExt.PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, HidpDelayedPowerPoRequestComplete, fdoExt, NULL); } } }
VOID HidpIdleNotificationCallback(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension) { PDEVICE_OBJECT pdo; FDO_EXTENSION *fdoExt; POWER_STATE powerState; ULONG iPdo; BOOLEAN ok = TRUE; KIRQL irql; LONG idleState, prevIdleState;
iPdo = 0; fdoExt = &HidDeviceExtension->fdoExt;
DBGINFO(("------ IDLE NOTIFICATION on fdo 0x%x\n", fdoExt->fdo));
KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
if (fdoExt->idleCancelling) { DBGINFO(("We are cancelling idle on fdo 0x%x", fdoExt->fdo)); fdoExt->idleState = IdleWaiting; if (ISPTR(fdoExt->idleTimeoutValue)) { InterlockedExchange(fdoExt->idleTimeoutValue, 0); } fdoExt->idleCancelling = FALSE; KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
IoCancelIrp(fdoExt->idleNotificationRequest); return; } prevIdleState = InterlockedCompareExchange(&fdoExt->idleState, IdleCallbackReceived, IdleIrpSent); DBGASSERT(prevIdleState == IdleIrpSent, ("Idle callback in wrong state %x for fdo %x. Exitting.", prevIdleState, fdoExt->fdo), FALSE);
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
if (prevIdleState != IdleIrpSent) { return; }
if (HidpIsWaitWakePending(fdoExt, TRUE) == FALSE) { SubmitWaitWakeIrp((HIDCLASS_DEVICE_EXTENSION *) fdoExt->fdo->DeviceExtension); }
powerState.DeviceState = fdoExt->deviceCapabilities.DeviceWake;
fdoExt->numIdlePdos = fdoExt->deviceRelations->Count+1;
for (iPdo = 0; iPdo < fdoExt->deviceRelations->Count; iPdo++) { pdo = fdoExt->collectionPdoExtensions[iPdo]->pdoExt.pdo;
DBGVERBOSE(("power down pdos, requesting D%d on pdo #%d %x\n", powerState.DeviceState-1, iPdo, pdo));
//
// We could check // pdoExt->devicePowerState != PowerDeviceD0
// but, if the stack gets 2 D0 irps in a row, nothing bad should happen
//
PoRequestPowerIrp(pdo, IRP_MN_SET_POWER, powerState, HidpPdoIdleOutComplete, HidDeviceExtension, NULL); }
if (InterlockedDecrement(&fdoExt->numIdlePdos) == 0) { BOOLEAN idleCancelling = FALSE; HidpPowerDownFdo(HidDeviceExtension); KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
prevIdleState = InterlockedCompareExchange(&fdoExt->idleState, IdleComplete, IdleCallbackReceived); idleCancelling = fdoExt->idleCancelling;
DBGASSERT (prevIdleState == IdleCallbackReceived, ("Race condition in HidpPdoIdleOutComplete. Prev state = %x", prevIdleState), FALSE);
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
KeResetEvent(&fdoExt->idleDoneEvent); if (idleCancelling) { POWER_STATE powerState; powerState.DeviceState = PowerDeviceD0; DBGINFO(("Cancelling idle. Send power irp from idle callback.")) PoRequestPowerIrp(((PHIDCLASS_DEVICE_EXTENSION) fdoExt->fdo->DeviceExtension)->hidExt.PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, HidpDelayedPowerPoRequestComplete, fdoExt, NULL); } } }
/*
******************************************************************************** * EnqueueCollectionWaitWakeIrp ******************************************************************************** * */ NTSTATUS EnqueueCollectionWaitWakeIrp( IN FDO_EXTENSION *FdoExt, IN PDO_EXTENSION *PdoExt, IN PIRP WaitWakeIrp) { PDRIVER_CANCEL oldCancelRoutine; KIRQL oldIrql; NTSTATUS status; PHIDCLASS_DEVICE_EXTENSION devExt = (PHIDCLASS_DEVICE_EXTENSION)FdoExt->fdo->DeviceExtension;
KeAcquireSpinLock(&FdoExt->collectionWaitWakeIrpQueueSpinLock, &oldIrql);
if (InterlockedCompareExchangePointer(&PdoExt->waitWakeIrp, WaitWakeIrp, NULL) != NULL) { //
// More than one WW irp? Unthinkable!
//
DBGWARN(("Another WW irp was already queued on pdoExt %x", PdoExt)) status = STATUS_INVALID_DEVICE_STATE; } else { /*
* Must set a cancel routine before checking the Cancel flag * (this makes the cancel code path for the IRP have to contend * for our local spinlock). */ oldCancelRoutine = IoSetCancelRoutine(WaitWakeIrp, CollectionWaitWakeIrpCancelRoutine); ASSERT(!oldCancelRoutine);
if (WaitWakeIrp->Cancel){ /*
* This IRP has already been cancelled. */ oldCancelRoutine = IoSetCancelRoutine(WaitWakeIrp, NULL); if (oldCancelRoutine){ /*
* Cancel routine was NOT called, so complete the IRP here * (caller will do this when we return error). */ ASSERT(oldCancelRoutine == CollectionWaitWakeIrpCancelRoutine); status = STATUS_CANCELLED; } else { /*
* Cancel routine was called, and it will dequeue and complete the IRP * as soon as we drop the spinlock. * Initialize the IRP's listEntry so the dequeue doesn't corrupt the list. * Then return STATUS_PENDING so we don't touch the IRP */ InitializeListHead(&WaitWakeIrp->Tail.Overlay.ListEntry);
IoMarkIrpPending(WaitWakeIrp); status = STATUS_PENDING; } } else { /*
* IoMarkIrpPending sets a bit in the current stack location * to indicate that the Irp may complete on a different thread. */ InsertTailList(&FdoExt->collectionWaitWakeIrpQueue, &WaitWakeIrp->Tail.Overlay.ListEntry);
IoMarkIrpPending(WaitWakeIrp); status = STATUS_PENDING; } }
if (status != STATUS_PENDING) { //
// The irp was cancelled. Remove it from the extension.
//
InterlockedExchangePointer(&PdoExt->waitWakeIrp, NULL); }
KeReleaseSpinLock(&FdoExt->collectionWaitWakeIrpQueueSpinLock, oldIrql);
if (status == STATUS_PENDING){ #if WIN95_BUILD
DBGERR(("WaitWake IRP sent by client on Win98 ???")) #else
if (!HidpIsWaitWakePending(FdoExt, TRUE)){ DBGVERBOSE(("WW 5 %x\n", devExt)) SubmitWaitWakeIrp(devExt); } #endif
}
return status; }
NTSTATUS HidpPdoPower( IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp ) { NTSTATUS status = NO_STATUS; PIO_STACK_LOCATION irpSp; FDO_EXTENSION *fdoExt; PDO_EXTENSION *pdoExt; KIRQL oldIrql; UCHAR minorFunction; LIST_ENTRY dequeue, *entry; PIO_STACK_LOCATION stack; PIRP irp; ULONG count; POWER_STATE powerState; SYSTEM_POWER_STATE systemState; BOOLEAN justReturnPending = FALSE; BOOLEAN runPowerCode;
irpSp = IoGetCurrentIrpStackLocation(Irp);
/*
* Keep these privately so we still have it after the IRP completes * or after the device extension is freed on a REMOVE_DEVICE */ minorFunction = irpSp->MinorFunction;
pdoExt = &HidDeviceExtension->pdoExt; fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
runPowerCode = (pdoExt->state == COLLECTION_STATE_RUNNING) || (pdoExt->state == COLLECTION_STATE_STOPPED) || (pdoExt->state == COLLECTION_STATE_STOPPING);
if (runPowerCode) { switch (minorFunction){
case IRP_MN_SET_POWER: PoSetPowerState(pdoExt->pdo, irpSp->Parameters.Power.Type, irpSp->Parameters.Power.State);
switch (irpSp->Parameters.Power.Type) {
case SystemPowerState: systemState = irpSp->Parameters.Power.State.SystemState;
pdoExt->systemPowerState = systemState;
if (systemState == PowerSystemWorking){ powerState.DeviceState = PowerDeviceD0; } else { powerState.DeviceState = PowerDeviceD3; }
DBGVERBOSE(("S irp, requesting D%d on pdo %x\n", powerState.DeviceState-1, pdoExt->pdo));
IoMarkIrpPending(Irp); PoRequestPowerIrp(pdoExt->pdo, IRP_MN_SET_POWER, powerState, CollectionPowerRequestCompletion, Irp, // context
NULL);
/*
* We want to complete the system-state power Irp * with the result of the device-state power Irp. * We'll complete the system-state power Irp when * the device-state power Irp completes. * * Note: this may have ALREADY happened, so don't * touch the original Irp anymore. */ status = STATUS_PENDING; justReturnPending = TRUE;
break;
case DevicePowerState: switch (irpSp->Parameters.Power.State.DeviceState) {
case PowerDeviceD0: /*
* Resume from APM Suspend * * Do nothing here; Send down the read IRPs in the * completion routine for this (the power) IRP. */ DBGVERBOSE(("pdo %x on fdo %x going to D0\n", pdoExt->pdo, fdoExt->fdo));
pdoExt->devicePowerState = irpSp->Parameters.Power.State.DeviceState; status = STATUS_SUCCESS;
//
// Resend all power delayed IRPs
//
count = DequeueAllPdoPowerDelayedIrps(pdoExt, &dequeue); DBGVERBOSE(("dequeued %d requests\n", count));
while (!IsListEmpty(&dequeue)) { entry = RemoveHeadList(&dequeue); irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); stack = IoGetCurrentIrpStackLocation(irp);
DBGINFO(("resending %x to pdo %x in set D0 for pdo.\n", irp, pdoExt->pdo));
pdoExt->pdo->DriverObject-> MajorFunction[stack->MajorFunction] (pdoExt->pdo, irp); } break;
case PowerDeviceD1: case PowerDeviceD2: case PowerDeviceD3: /*
* Suspend */
DBGVERBOSE(("pdo %x on fdo %x going to D%d\n", pdoExt->pdo, fdoExt->fdo, irpSp->Parameters.Power.State.DeviceState-1));
pdoExt->devicePowerState = irpSp->Parameters.Power.State.DeviceState; status = STATUS_SUCCESS;
//
// Only manually power down the PDO if the
// machine is not going into low power,
// the PDO is going into a D state we can
// wake out of, and we have idle time out
// enabled.
//
if (pdoExt->systemPowerState == PowerSystemWorking && pdoExt->devicePowerState <= fdoExt->deviceCapabilities.DeviceWake && fdoExt->idleState != IdleDisabled) { DBGVERBOSE(("maybe powering down fdo\n"));
HidpPowerDownFdo(HidDeviceExtension->pdoExt.deviceFdoExt); }
break;
default: /*
* Do not return STATUS_NOT_SUPPORTED; * keep the default status * (this allows filter drivers to work). */ status = Irp->IoStatus.Status; break; } break;
default: /*
* Do not return STATUS_NOT_SUPPORTED; * keep the default status * (this allows filter drivers to work). */ status = Irp->IoStatus.Status; break; } break;
case IRP_MN_WAIT_WAKE: /*
* WaitWake IRPs to the collection-PDO's * just get queued in the base device's extension; * when the base device's WaitWake IRP gets * completed, we'll also complete these collection * WaitWake IRPs. */
if (fdoExt->systemPowerState > fdoExt->deviceCapabilities.SystemWake) { status = STATUS_POWER_STATE_INVALID; } else { status = EnqueueCollectionWaitWakeIrp(fdoExt, pdoExt, Irp); if (status == STATUS_PENDING){ justReturnPending = TRUE; } }
break;
case IRP_MN_POWER_SEQUENCE: TRAP; // client-PDO should never get this
status = Irp->IoStatus.Status; break;
case IRP_MN_QUERY_POWER: /*
* We allow all power transitions. * But make sure that there's no WW down that shouldn't be. */ DBGVERBOSE(("Query power")); status = HidpCheckIdleState(HidDeviceExtension, Irp); if (status != STATUS_SUCCESS) { justReturnPending = TRUE; } break;
default: /*
* 'fail' the Irp by returning the default status. * Do not return STATUS_NOT_SUPPORTED; * keep the default status * (this allows filter drivers to work). */ status = Irp->IoStatus.Status; break; } } else { switch (minorFunction){ case IRP_MN_SET_POWER: case IRP_MN_QUERY_POWER: status = STATUS_SUCCESS; break; default: status = Irp->IoStatus.Status; break; } }
if (!justReturnPending) { /*
* Whether we are completing or relaying this power IRP, * we must call PoStartNextPowerIrp on Windows NT. */ PoStartNextPowerIrp(Irp);
ASSERT(status != NO_STATUS); Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT); }
DBGSUCCESS(status, FALSE) return status; }
NTSTATUS HidpFdoPower( IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp ) { NTSTATUS status = NO_STATUS; PIO_STACK_LOCATION irpSp; FDO_EXTENSION *fdoExt; KIRQL oldIrql; BOOLEAN completeIrpHere = FALSE; BOOLEAN returnPending = FALSE; UCHAR minorFunction; SYSTEM_POWER_STATE systemState; BOOLEAN runPowerCode;
irpSp = IoGetCurrentIrpStackLocation(Irp);
/*
* Keep these privately so we still have it after the IRP completes * or after the device extension is freed on a REMOVE_DEVICE */ minorFunction = irpSp->MinorFunction;
fdoExt = &HidDeviceExtension->fdoExt;
runPowerCode = (fdoExt->state == DEVICE_STATE_START_SUCCESS) || (fdoExt->state == DEVICE_STATE_STOPPING) || (fdoExt->state == DEVICE_STATE_STOPPED);
if (runPowerCode) { switch (minorFunction){
case IRP_MN_SET_POWER: PoSetPowerState(fdoExt->fdo, irpSp->Parameters.Power.Type, irpSp->Parameters.Power.State);
switch (irpSp->Parameters.Power.Type) {
case SystemPowerState:
systemState = irpSp->Parameters.Power.State.SystemState;
if (systemState < PowerSystemMaximum) { /*
* For the 'regular' system power states, * we convert to a device power state * and request a callback with the device power state. */ PDEVICE_OBJECT pdo = HidDeviceExtension->hidExt.PhysicalDeviceObject; POWER_STATE powerState; KIRQL oldIrql; BOOLEAN isWaitWakePending;
if (systemState != PowerSystemWorking) { //
// We don't want to be idling during regular system
// power stuff.
//
HidpCancelIdleNotification(fdoExt, FALSE); }
fdoExt->systemPowerState = systemState; isWaitWakePending = HidpIsWaitWakePending(fdoExt, FALSE);
if (isWaitWakePending && systemState > fdoExt->deviceCapabilities.SystemWake){ /*
* We're transitioning to a system state from which * this device cannot perform a wake-up. * So fail all the WaitWake IRPs. */ CompleteAllCollectionWaitWakeIrps(fdoExt, STATUS_POWER_STATE_INVALID); } returnPending = TRUE; } else { TRAP; /*
* For the remaining system power states, * just pass down the IRP. */ runPowerCode = FALSE; Irp->IoStatus.Status = STATUS_SUCCESS; }
break;
case DevicePowerState: switch (irpSp->Parameters.Power.State.DeviceState) {
case PowerDeviceD0: /*
* Resume from APM Suspend * * Do nothing here; Send down the read IRPs in the * completion routine for this (the power) IRP. */ DBGVERBOSE(("fdo powering up to D0\n")); break;
case PowerDeviceD1: case PowerDeviceD2: case PowerDeviceD3: /*
* Suspend */
DBGVERBOSE(("fdo going down to D%d\n", fdoExt->devicePowerState-1));
if (fdoExt->state == DEVICE_STATE_START_SUCCESS && fdoExt->devicePowerState == PowerDeviceD0){ CancelAllPingPongIrps(fdoExt); } fdoExt->devicePowerState = irpSp->Parameters.Power.State.DeviceState;
break; } break; } break;
case IRP_MN_WAIT_WAKE: KeAcquireSpinLock(&fdoExt->waitWakeSpinLock, &oldIrql); if (fdoExt->waitWakeIrp == BAD_POINTER) { DBGVERBOSE(("new wait wake irp 0x%x\n", Irp)); fdoExt->waitWakeIrp = Irp; } else { DBGVERBOSE(("1+ wait wake irps 0x%x\n", Irp)); completeIrpHere = TRUE; status = STATUS_POWER_STATE_INVALID; } KeReleaseSpinLock(&fdoExt->waitWakeSpinLock, oldIrql);
break; } } else { switch (minorFunction){ case IRP_MN_SET_POWER: case IRP_MN_QUERY_POWER: Irp->IoStatus.Status = STATUS_SUCCESS; break; default: // nothing
break; } }
/*
* Whether we are completing or relaying this power IRP, * we must call PoStartNextPowerIrp on Windows NT. */ PoStartNextPowerIrp(Irp);
/*
* If this is a call for a collection-PDO, we complete it ourselves here. * Otherwise, we pass it to the minidriver stack for more processing. */ if (completeIrpHere){
/*
* Note: Don't touch the Irp after completing it. */ ASSERT(status != NO_STATUS); Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { /*
* Call the minidriver with this Irp. * The rest of our processing will be done in our completion routine. * * Note: Don't touch the Irp after sending it down, since it may * be completed immediately. */
if (runPowerCode) { IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, HidpFdoPowerCompletion, (PVOID)HidDeviceExtension, TRUE, TRUE, TRUE); } else { IoSkipCurrentIrpStackLocation(Irp); }
/*
* * Want to use PoCallDriver here, but PoCallDriver * uses IoCallDriver, * which uses the driverObject->MajorFunction[] array * instead of the hidDriverExtension->MajorFunction[] functions. * SHOULD FIX THIS FOR NT -- should use PoCallDriver * */ // status = PoCallDriver(HidDeviceExtension->hidExt.NextDeviceObject, Irp);
// status = PoCallDriver(fdoExt->fdo, Irp);
if (returnPending) { DBGASSERT(runPowerCode, ("We are returning pending, but not running completion routine.\n"), TRUE) IoMarkIrpPending(Irp); HidpCallDriver(fdoExt->fdo, Irp); status = STATUS_PENDING; } else { status = HidpCallDriver(fdoExt->fdo, Irp); } }
DBGSUCCESS(status, FALSE) return status; }
/*
******************************************************************************** * HidpIrpMajorPower ******************************************************************************** * * * Note: This function cannot be pageable because (on Win98 anyway) * NTKERN calls it back on the thread of the completion routine * that returns the "Cntrl-Alt-Del" keystrokes. * Also, we may or may not have set the DO_POWER_PAGABLE; * so power IRPs may or may not come in at DISPATCH_LEVEL. * So we must keep this code locked. */ NTSTATUS HidpIrpMajorPower( IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp ) { PIO_STACK_LOCATION irpSp; BOOLEAN isClientPdo; NTSTATUS status; UCHAR minorFunction;
irpSp = IoGetCurrentIrpStackLocation(Irp);
minorFunction = irpSp->MinorFunction;
isClientPdo = HidDeviceExtension->isClientPdo;
if (minorFunction != IRP_MN_SET_POWER){ DBG_LOG_POWER_IRP(HidDeviceExtension, minorFunction, isClientPdo, FALSE, "", -1, -1) } else { switch (irpSp->Parameters.Power.Type) { case SystemPowerState: DBG_LOG_POWER_IRP(HidDeviceExtension, minorFunction, isClientPdo, FALSE, "SystemState", irpSp->Parameters.Power.State.SystemState, 0xffffffff); case DevicePowerState: DBG_LOG_POWER_IRP(HidDeviceExtension, minorFunction, isClientPdo, FALSE, "DeviceState", irpSp->Parameters.Power.State.DeviceState, 0xffffffff); } }
if (isClientPdo){ status = HidpPdoPower(HidDeviceExtension, Irp); } else { status = HidpFdoPower(HidDeviceExtension, Irp); }
DBG_LOG_POWER_IRP(HidDeviceExtension, minorFunction, isClientPdo, TRUE, "", -1, status)
return status; }
/*
******************************************************************************** * SubmitWaitWakeIrp ******************************************************************************** * * */ NTSTATUS SubmitWaitWakeIrp(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension) { NTSTATUS status; POWER_STATE powerState; FDO_EXTENSION *fdoExt;
ASSERT(!HidDeviceExtension->isClientPdo); fdoExt = &HidDeviceExtension->fdoExt;
powerState.SystemState = fdoExt->deviceCapabilities.SystemWake;
DBGVERBOSE(("SystemWake=%x, submitting waitwake irp.", fdoExt->deviceCapabilities.SystemWake))
status = PoRequestPowerIrp( HidDeviceExtension->hidExt.PhysicalDeviceObject, IRP_MN_WAIT_WAKE, powerState, HidpWaitWakeComplete, HidDeviceExtension, // context
NULL);
// if (status != STATUS_PENDING){
// fdoExt->waitWakeIrp = BAD_POINTER;
// }
DBGASSERT((status == STATUS_PENDING), ("Expected STATUS_PENDING when submitting WW, got %x", status), TRUE) return status; }
/*
******************************************************************************** * HidpFdoPowerCompletion ******************************************************************************** * * */ NTSTATUS HidpFdoPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PIO_STACK_LOCATION irpSp; FDO_EXTENSION *fdoExt; NTSTATUS status = Irp->IoStatus.Status; PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)Context; SYSTEM_POWER_STATE systemState;
if (Irp->PendingReturned) { IoMarkIrpPending(Irp); }
ASSERT(ISPTR(HidDeviceExtension));
if (HidDeviceExtension->isClientPdo){ fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt; } else { fdoExt = &HidDeviceExtension->fdoExt; }
irpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(irpSp->MajorFunction == IRP_MJ_POWER);
if (NT_SUCCESS(status)) { switch (irpSp->MinorFunction) {
case IRP_MN_SET_POWER: switch (irpSp->Parameters.Power.Type) { case DevicePowerState: switch (irpSp->Parameters.Power.State.DeviceState){ case PowerDeviceD0:
if (fdoExt->devicePowerState != PowerDeviceD0) { KIRQL irql; LONG prevIdleState;
fdoExt->devicePowerState = irpSp->Parameters.Power.State.DeviceState;
ASSERT(!HidDeviceExtension->isClientPdo);
//
// Reset the idle stuff if it's not disabled.
//
KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql); if (fdoExt->idleState != IdleDisabled) { prevIdleState = InterlockedExchange(&fdoExt->idleState, IdleWaiting); DBGASSERT(prevIdleState == IdleComplete, ("Previous idle state while completing actually %x", prevIdleState), TRUE); fdoExt->idleCancelling = FALSE; } KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
/*
* On APM resume, restart the ping-pong IRPs * for interrupt devices. */ if (!fdoExt->driverExt->DevicesArePolled && !fdoExt->isOutputOnlyDevice) { NTSTATUS ntStatus = HidpStartAllPingPongs(fdoExt); if (!NT_SUCCESS(ntStatus)) { fdoExt->state = DEVICE_STATE_START_FAILURE; } } } break; } break; case SystemPowerState: ASSERT (!HidDeviceExtension->isClientPdo);
systemState = irpSp->Parameters.Power.State.SystemState;
ASSERT((ULONG)systemState < PowerSystemMaximum);
if (systemState < PowerSystemMaximum){ /*
* For the 'regular' system power states, * we convert to a device power state * and request a callback with the device power state. */ PDEVICE_OBJECT pdo = HidDeviceExtension->hidExt.PhysicalDeviceObject; POWER_STATE powerState; KIRQL oldIrql; BOOLEAN isWaitWakePending;
fdoExt->systemPowerState = systemState; isWaitWakePending = HidpIsWaitWakePending(fdoExt, FALSE);
if (isWaitWakePending){ if (systemState == PowerSystemWorking){ powerState.DeviceState = PowerDeviceD0; } else { powerState.DeviceState = fdoExt->deviceCapabilities.DeviceState[systemState];
/*
* If the bus does not map the system state to * a defined device state, request PowerDeviceD3 * and cancel the WaitWake IRP. */ if (powerState.DeviceState == PowerDeviceUnspecified){ DBGERR(("IRP_MN_SET_POWER: systemState %d mapped not mapped so using device state PowerDeviceD3.", systemState)) powerState.DeviceState = PowerDeviceD3; } } } else { /*
* If we don't have a WaitWake IRP pending, * then every reduced-power system state * should get mapped to D3. */ if (systemState == PowerSystemWorking){ powerState.DeviceState = PowerDeviceD0; } else { DBGVERBOSE(("IRP_MN_SET_POWER: no waitWake IRP, so requesting PowerDeviceD3.")) powerState.DeviceState = PowerDeviceD3; } }
DBGVERBOSE(("IRP_MN_SET_POWER: mapped systemState %d to device state %d.", systemState, powerState.DeviceState))
IoMarkIrpPending(Irp); fdoExt->currentSystemStateIrp = Irp; PoRequestPowerIrp( pdo, IRP_MN_SET_POWER, powerState, DevicePowerRequestCompletion, fdoExt, // context
NULL);
status = STATUS_MORE_PROCESSING_REQUIRED; } else { TRAP; /*
* For the remaining system power states, * just pass down the IRP. */ } break; } break; } } else if (status == STATUS_CANCELLED){ /*
* Client cancelled the power IRP, probably getting removed. */ } else { DBGWARN(("HidpPowerCompletion: Power IRP %ph (minor function %xh) failed with status %xh.", Irp, irpSp->MinorFunction, Irp->IoStatus.Status)) }
return status; }
/*
******************************************************************************** * DevicePowerRequestCompletion ******************************************************************************** * * Note: the DeviceObject here is the PDO (e.g. usbhub's PDO), not our FDO, * so we cannot use its device context. */ VOID DevicePowerRequestCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { FDO_EXTENSION *fdoExt = (FDO_EXTENSION *)Context; PIRP systemStateIrp;
DBG_COMMON_ENTRY()
systemStateIrp = fdoExt->currentSystemStateIrp; fdoExt->currentSystemStateIrp = BAD_POINTER; ASSERT(systemStateIrp);
DBGSUCCESS(IoStatus->Status, TRUE) // systemStateIrp->IoStatus.Status = IoStatus->Status;
PoStartNextPowerIrp(systemStateIrp);
/*
* Complete the system-state IRP. */ IoCompleteRequest(systemStateIrp, IO_NO_INCREMENT);
if (PowerState.DeviceState == PowerDeviceD0) { //
// Powering up. Restart the idling.
//
HidpStartIdleTimeout(fdoExt, FALSE); }
DBG_COMMON_EXIT() }
/*
******************************************************************************** * CollectionPowerRequestCompletion ******************************************************************************** * * */ VOID CollectionPowerRequestCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { PIRP systemStateIrp = (PIRP)Context; PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension; PDO_EXTENSION *pdoExt; IO_STACK_LOCATION *irpSp; SYSTEM_POWER_STATE systemState;
DBG_COMMON_ENTRY()
hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension; pdoExt = &hidDeviceExtension->pdoExt;
ASSERT(systemStateIrp);
/*
* This is the completion routine for the device-state power * Irp which we've requested. Complete the original system-state * power Irp with the result of the device-state power Irp. */
irpSp = IoGetCurrentIrpStackLocation(systemStateIrp); systemState = irpSp->Parameters.Power.State.SystemState;
systemStateIrp->IoStatus.Status = IoStatus->Status; PoStartNextPowerIrp(systemStateIrp);
IoCompleteRequest(systemStateIrp, IO_NO_INCREMENT);
//
// If we're powering up, check if we should have a WW irp pending.
//
if (systemState == PowerSystemWorking && SHOULD_SEND_WAITWAKE(pdoExt)) { HidpCreateRemoteWakeIrp(pdoExt); }
DBG_COMMON_EXIT() }
/*
******************************************************************************** * HidpWaitWakePoRequestComplete ******************************************************************************** * */ NTSTATUS HidpWaitWakePoRequestComplete( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { PHIDCLASS_DEVICE_EXTENSION hidDevExt = (PHIDCLASS_DEVICE_EXTENSION)Context; FDO_EXTENSION *fdoExt;
ASSERT(!hidDevExt->isClientPdo); fdoExt = &hidDevExt->fdoExt;
DBGVERBOSE(("HidpWaitWakePoRequestComplete!, status == %xh", IoStatus->Status))
#if WIN95_BUILD
if (NT_SUCCESS(IoStatus->Status)) { //
// Resubmit the wait wake irp
//
HidpPowerUpPdos(fdoExt); SubmitWaitWakeIrp(hidDevExt); } #else
/*
* Complete all the collections' WaitWake IRPs with this same status. */ CompleteAllCollectionWaitWakeIrps(fdoExt, IoStatus->Status);
if (NT_SUCCESS(IoStatus->Status) && fdoExt->idleState != IdleDisabled) { HidpPowerUpPdos(fdoExt); } #endif
return STATUS_SUCCESS; }
/*
******************************************************************************** * HidpWaitWakeComplete ******************************************************************************** * */ NTSTATUS HidpWaitWakeComplete( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { PHIDCLASS_DEVICE_EXTENSION hidDevExt = (PHIDCLASS_DEVICE_EXTENSION)Context; FDO_EXTENSION *fdoExt; PDO_EXTENSION *pdoExt; NTSTATUS status; KIRQL oldIrql;
ASSERT(!hidDevExt->isClientPdo); fdoExt = &hidDevExt->fdoExt;
status = IoStatus->Status; DBGVERBOSE(("HidpWaitWakeComplete!, status == %xh", status))
KeAcquireSpinLock(&fdoExt->waitWakeSpinLock, &oldIrql); fdoExt->waitWakeIrp = BAD_POINTER; fdoExt->isWaitWakePending = FALSE; KeReleaseSpinLock(&fdoExt->waitWakeSpinLock, oldIrql);
/*
* Call HidpWaitWakePoRequestComplete (either directly or * as a completion routine to the power IRP that we request * to wake up the machine); it will complete the clients' * WaitWake IRPs with the same status as this device WaitWake IRP. */ PowerState.DeviceState = PowerDeviceD0;
if (NT_SUCCESS(status)){ /*
* Our device is waking up the machine. * So request the D0 (working) power state. */ // PowerState is undefined when a wait wake irp is completing
// ASSERT(PowerState.DeviceState == PowerDeviceD0);
DBGVERBOSE(("ww irp, requesting D0 on pdo %x\n", DeviceObject))
PoRequestPowerIrp( DeviceObject, IRP_MN_SET_POWER, PowerState, HidpWaitWakePoRequestComplete, Context, NULL); } else if (status != STATUS_CANCELLED) {
//
// If the wait wake failed, then there is no way for us to wake the
// device when we are in S0. Turn off idle detection.
//
// This doesn't need to be guarded by a spin lock because the only
// places we look at these values is in the power dispatch routine
// and when an interrupt read completes...
//
// 1) no interrupt read will be completing b/c the pingpong engine has
// been suspended and will not start until we power up the stack
// 2) I think we are still considered to be handling a power irp. If
// not, then we need to guard the isIdleTimeoutEnabled field
//
// ISSUE! we should also only turn off idle detection if the WW fails in
// S0. If we hiber, then the WW will fail, but we should not turn off
// idle detection in this case. I think that checking
// systemPowerState is not PowerSystemWorking will do the trick,
// BUT THIS MUST BE CONFIRMED!!!!
//
if (fdoExt->idleState != IdleDisabled && fdoExt->systemPowerState == PowerSystemWorking) { DBGWARN(("Turning off idle detection due to WW failure, status = %x\n", status))
ASSERT(ISPTR(fdoExt->idleTimeoutValue));
//
// Don't set any state before calling because we may have to power
// stuff up.
//
HidpCancelIdleNotification(fdoExt, FALSE); }
HidpWaitWakePoRequestComplete( DeviceObject, MinorFunction, PowerState, Context, IoStatus); }
return STATUS_SUCCESS; }
/*
******************************************************************************** * QueuePowerEventIrp ******************************************************************************** * */ NTSTATUS QueuePowerEventIrp( IN PHIDCLASS_COLLECTION hidCollection, IN PIRP Irp ) { NTSTATUS status; KIRQL oldIrql; PDRIVER_CANCEL oldCancelRoutine;
KeAcquireSpinLock(&hidCollection->powerEventSpinLock, &oldIrql);
/*
* Must set a cancel routine before checking the Cancel flag. */ oldCancelRoutine = IoSetCancelRoutine(Irp, PowerEventCancelRoutine); ASSERT(!oldCancelRoutine);
if (Irp->Cancel){ /*
* This IRP was cancelled. Do not queue it. * The calling function will complete the IRP with error. */ oldCancelRoutine = IoSetCancelRoutine(Irp, NULL); if (oldCancelRoutine){ /*
* Cancel routine was NOT called. * Complete the IRP here. */ ASSERT(oldCancelRoutine == PowerEventCancelRoutine); status = STATUS_CANCELLED; } else { /*
* The cancel routine was called, * and it will complete this IRP as soon as we drop the spinlock. * Return PENDING so the caller doesn't touch this IRP. */ status = STATUS_PENDING; } } else if (ISPTR(hidCollection->powerEventIrp)){ /*
* We already have a power event IRP queued. * This shouldn't happen, but we'll handle it. */ DBGWARN(("Already have a power event irp queued.")); oldCancelRoutine = IoSetCancelRoutine(Irp, NULL); if (oldCancelRoutine){ /*
* Cancel routine was NOT called. * Complete the IRP here. */ ASSERT(oldCancelRoutine == PowerEventCancelRoutine); status = STATUS_UNSUCCESSFUL; } else { /*
* The irp was cancelled and the cancel routine was called; * it will complete this IRP as soon as we drop the spinlock. * Return PENDING so the caller doesn't touch this IRP. */ ASSERT(Irp->Cancel); status = STATUS_PENDING; } } else { /*
* Save a pointer to this power event IRP and return PENDING. * This qualifies as "queuing" the IRP, so we must have * a cancel routine. */ hidCollection->powerEventIrp = Irp; IoMarkIrpPending(Irp); status = STATUS_PENDING; }
KeReleaseSpinLock(&hidCollection->powerEventSpinLock, oldIrql);
return status; }
/*
******************************************************************************** * PowerEventCancelRoutine ******************************************************************************** * */ VOID PowerEventCancelRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension; FDO_EXTENSION *fdoExt; PHIDCLASS_COLLECTION hidCollection; ULONG collectionIndex; KIRQL oldIrql;
ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG); ASSERT(hidDeviceExtension->isClientPdo); fdoExt = &hidDeviceExtension->pdoExt.deviceFdoExt->fdoExt; collectionIndex = hidDeviceExtension->pdoExt.collectionIndex; hidCollection = &fdoExt->classCollectionArray[collectionIndex];
KeAcquireSpinLock(&hidCollection->powerEventSpinLock, &oldIrql);
ASSERT(Irp == hidCollection->powerEventIrp); hidCollection->powerEventIrp = BAD_POINTER;
KeReleaseSpinLock(&hidCollection->powerEventSpinLock, oldIrql);
IoReleaseCancelSpinLock(Irp->CancelIrql);
Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); }
/*
******************************************************************************** * CollectionWaitWakeIrpCancelRoutine ******************************************************************************** * */ VOID CollectionWaitWakeIrpCancelRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension; KIRQL oldIrql, oldIrql2; PIRP deviceWaitWakeIrpToCancel = NULL; FDO_EXTENSION *fdoExt; PDO_EXTENSION *pdoExt;
ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG); ASSERT(hidDeviceExtension->isClientPdo); pdoExt = &hidDeviceExtension->pdoExt; fdoExt = &pdoExt->deviceFdoExt->fdoExt;
KeAcquireSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, &oldIrql);
/*
* Dequeue the client's WaitWake IRP. */ RemoveEntryList(&Irp->Tail.Overlay.ListEntry); InterlockedExchangePointer(&pdoExt->waitWakeIrp, NULL);
/*
* If the last collection WaitWake IRP just got cancelled, * cancel our WaitWake IRP as well. * * NOTE: we only cancel the FDO wait wake irp if we are not doing idle * detection, otherwise, there would be no way for the device to * wake up when we put it into low power * */ KeAcquireSpinLock(&fdoExt->waitWakeSpinLock, &oldIrql2); if (IsListEmpty(&fdoExt->collectionWaitWakeIrpQueue) && fdoExt->isWaitWakePending && fdoExt->idleState == IdleDisabled){ ASSERT(ISPTR(fdoExt->waitWakeIrp)); deviceWaitWakeIrpToCancel = fdoExt->waitWakeIrp; fdoExt->waitWakeIrp = BAD_POINTER; fdoExt->isWaitWakePending = FALSE; } KeReleaseSpinLock(&fdoExt->waitWakeSpinLock, oldIrql2);
KeReleaseSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, oldIrql);
IoReleaseCancelSpinLock(Irp->CancelIrql);
/*
* Complete the cancelled IRP only if it was in the list. */ Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (ISPTR(deviceWaitWakeIrpToCancel)){ IoCancelIrp(deviceWaitWakeIrpToCancel); } }
/*
******************************************************************************** * CompleteAllCollectionWaitWakeIrps ******************************************************************************** * * Note: this function cannot be pageable because it is called * from a completion routine. */ VOID CompleteAllCollectionWaitWakeIrps( IN FDO_EXTENSION *fdoExt, IN NTSTATUS status ) { LIST_ENTRY irpsToComplete; KIRQL oldIrql; PLIST_ENTRY listEntry; PIRP irp; PDO_EXTENSION *pdoExt; PIO_STACK_LOCATION irpSp;
InitializeListHead(&irpsToComplete);
KeAcquireSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, &oldIrql);
while (!IsListEmpty(&fdoExt->collectionWaitWakeIrpQueue)){ PDRIVER_CANCEL oldCancelRoutine;
listEntry = RemoveHeadList(&fdoExt->collectionWaitWakeIrpQueue); InitializeListHead(listEntry); // in case cancel routine tries to dequeue again
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
oldCancelRoutine = IoSetCancelRoutine(irp, NULL); if (oldCancelRoutine){ ASSERT(oldCancelRoutine == CollectionWaitWakeIrpCancelRoutine);
/*
* We can't complete an IRP while holding a spinlock. * Also, we don't want to complete a WaitWake IRP while * still processing collectionWaitWakeIrpQueue because a driver * may resend an IRP on the same thread, causing us to loop forever. * So just move the IRPs to a private queue and we'll complete them later. */ InsertTailList(&irpsToComplete, listEntry); irpSp = IoGetCurrentIrpStackLocation(irp); pdoExt = &((PHIDCLASS_DEVICE_EXTENSION)irpSp->DeviceObject->DeviceExtension)->pdoExt; InterlockedExchangePointer(&pdoExt->waitWakeIrp, NULL); } else { /*
* This IRP was cancelled and the cancel routine WAS called. * The cancel routine will complete the IRP as soon as we drop the spinlock. * So don't touch the IRP. */ ASSERT(irp->Cancel); } }
KeReleaseSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, oldIrql);
while (!IsListEmpty(&irpsToComplete)){ listEntry = RemoveHeadList(&irpsToComplete); irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry); irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); } }
VOID PowerDelayedCancelRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension; FDO_EXTENSION *fdoExt; KIRQL oldIrql;
IoReleaseCancelSpinLock(Irp->CancelIrql);
ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG); ASSERT(hidDeviceExtension->isClientPdo); fdoExt = &hidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
KeAcquireSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, &oldIrql);
RemoveEntryList(&Irp->Tail.Overlay.ListEntry); ASSERT(Irp->Tail.Overlay.DriverContext[0] == (PVOID) hidDeviceExtension); Irp->Tail.Overlay.DriverContext[0] = NULL;
ASSERT(fdoExt->numPendingPowerDelayedIrps > 0); fdoExt->numPendingPowerDelayedIrps--;
KeReleaseSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, oldIrql);
Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); }
NTSTATUS HidpDelayedPowerPoRequestComplete( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus) { KIRQL irql; LONG prevIdleState; PFDO_EXTENSION fdoExt = (PFDO_EXTENSION) Context;
DBGINFO(("powering up all pdos due to delayed request, 0x%x\n", IoStatus->Status))
DBGVERBOSE(("HidpDelayedPowerPoRequestComplete!, status == %xh", IoStatus->Status))
if (NT_SUCCESS(IoStatus->Status)) { HidpPowerUpPdos(fdoExt); } else { //
// All bets are off.
//
KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql); prevIdleState = InterlockedExchange(&fdoExt->idleState, IdleDisabled); fdoExt->idleCancelling = FALSE; KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
KeSetEvent(&fdoExt->idleDoneEvent, 0, FALSE); }
return STATUS_SUCCESS; }
NTSTATUS EnqueuePowerDelayedIrp( IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN PIRP Irp ) { FDO_EXTENSION *fdoExt; NTSTATUS status; KIRQL oldIrql; PDRIVER_CANCEL oldCancelRoutine;
ASSERT(HidDeviceExtension->isClientPdo); fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
DBGINFO(("enqueuing irp %x (mj %x, mn %x)\n", Irp, (ULONG) IoGetCurrentIrpStackLocation(Irp)->MajorFunction, (ULONG) IoGetCurrentIrpStackLocation(Irp)->MinorFunction))
KeAcquireSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, &oldIrql);
/*
* Must set a cancel routine before * checking the Cancel flag. */ oldCancelRoutine = IoSetCancelRoutine(Irp, PowerDelayedCancelRoutine); ASSERT(!oldCancelRoutine);
/*
* Make sure this Irp wasn't just cancelled. * Note that there is NO RACE CONDITION here * because we are holding the fileExtension lock. */ if (Irp->Cancel){ /*
* This IRP was cancelled. */ oldCancelRoutine = IoSetCancelRoutine(Irp, NULL); if (oldCancelRoutine){ /*
* The cancel routine was NOT called. * Return error so that caller completes the IRP. */ ASSERT(oldCancelRoutine == PowerDelayedCancelRoutine); status = STATUS_CANCELLED; } else { /*
* The cancel routine was called. * As soon as we drop the spinlock it will dequeue * and complete the IRP. * Initialize the IRP's listEntry so that the dequeue * doesn't cause corruption. * Then don't touch the irp. */ InitializeListHead(&Irp->Tail.Overlay.ListEntry); fdoExt->numPendingPowerDelayedIrps++; // because cancel routine will decrement
//
// We assert that this value is set in the cancel routine
//
Irp->Tail.Overlay.DriverContext[0] = (PVOID) HidDeviceExtension;
IoMarkIrpPending(Irp); status = Irp->IoStatus.Status = STATUS_PENDING; } } else { /*
* Queue this irp onto the fdo's power delayed queue */ InsertTailList(&fdoExt->collectionPowerDelayedIrpQueue, &Irp->Tail.Overlay.ListEntry); fdoExt->numPendingPowerDelayedIrps++;
Irp->Tail.Overlay.DriverContext[0] = (PVOID) HidDeviceExtension;
IoMarkIrpPending(Irp); status = Irp->IoStatus.Status = STATUS_PENDING; }
KeReleaseSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, oldIrql);
return status; }
PIRP DequeuePowerDelayedIrp(FDO_EXTENSION *fdoExt) { KIRQL oldIrql; PIRP irp = NULL;
KeAcquireSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, &oldIrql);
while (!irp && !IsListEmpty(&fdoExt->collectionPowerDelayedIrpQueue)){ PDRIVER_CANCEL oldCancelRoutine; PLIST_ENTRY listEntry = RemoveHeadList(&fdoExt->collectionPowerDelayedIrpQueue);
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
if (oldCancelRoutine){ ASSERT(oldCancelRoutine == PowerDelayedCancelRoutine); ASSERT(fdoExt->numPendingPowerDelayedIrps > 0); fdoExt->numPendingPowerDelayedIrps--; } else { /*
* IRP was cancelled and cancel routine was called. * As soon as we drop the spinlock, * the cancel routine will dequeue and complete this IRP. * Initialize the IRP's listEntry so that the dequeue doesn't cause corruption. * Then, don't touch the IRP. */ ASSERT(irp->Cancel); InitializeListHead(&irp->Tail.Overlay.ListEntry); irp = NULL; } }
KeReleaseSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, oldIrql);
return irp; }
ULONG DequeueAllPdoPowerDelayedIrps( PDO_EXTENSION *pdoExt, PLIST_ENTRY dequeue )
{ PDRIVER_CANCEL oldCancelRoutine; FDO_EXTENSION *fdoExt; PDO_EXTENSION *irpPdoExt; PLIST_ENTRY entry; KIRQL oldIrql; PIRP irp; ULONG count = 0;
InitializeListHead(dequeue);
fdoExt = &pdoExt->deviceFdoExt->fdoExt;
KeAcquireSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, &oldIrql);
for (entry = fdoExt->collectionPowerDelayedIrpQueue.Flink; entry != &fdoExt->collectionPowerDelayedIrpQueue; ) {
irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
irpPdoExt = &((PHIDCLASS_DEVICE_EXTENSION) irp->Tail.Overlay.DriverContext[0])->pdoExt;
entry = entry->Flink;
if (irpPdoExt == pdoExt) {
//
// Remove the entry from the linked list and then either queue it
// in the dequeue or init the entry so it is valid for the cancel
// routine
//
RemoveEntryList(&irp->Tail.Overlay.ListEntry);
oldCancelRoutine = IoSetCancelRoutine(irp, NULL); if (oldCancelRoutine != NULL) { InsertTailList(dequeue, &irp->Tail.Overlay.ListEntry); fdoExt->numPendingPowerDelayedIrps--; count++; } else { /*
* This IRP was cancelled and the cancel routine WAS called. * The cancel routine will complete the IRP as soon as we drop the spinlock. * So don't touch the IRP. */ ASSERT(irp->Cancel); InitializeListHead(&irp->Tail.Overlay.ListEntry); // in case cancel routine tries to dequeue again
} } }
KeReleaseSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, oldIrql);
return count; }
|