1831 lines
60 KiB
C
1831 lines
60 KiB
C
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|