|
|
/*++
Copyright (c) 1999, 2000 Microsoft Corporation
Module Name:
idle.c
Abstract
Author:
Doron H.
Environment:
Kernel mode only
Revision History:
--*/
#ifdef ALLOC_PRAGMA
#endif
#include "pch.h"
KSPIN_LOCK idleDeviceListSpinLock; LIST_ENTRY idleDeviceList; KTIMER idleTimer; KDPC idleTimerDpc; LONG numIdleDevices = 0;
#define HID_IDLE_SCAN_INTERVAL 1
typedef struct _HID_IDLE_DEVICE_INFO { LIST_ENTRY entry; ULONG idleCount; ULONG idleTime; PDEVICE_OBJECT device; BOOLEAN tryAgain; } HID_IDLE_DEVICE_INFO, *PHID_IDLE_DEVICE_INFO;
VOID HidpIdleTimerDpcProc( IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PVOID Context1, IN PVOID Context2 );
NTSTATUS HidpRegisterDeviceForIdleDetection( PDEVICE_OBJECT DeviceObject, ULONG IdleTime, PULONG *IdleTimeout ) { PHID_IDLE_DEVICE_INFO info = NULL; KIRQL irql; PLIST_ENTRY entry = NULL; static BOOLEAN firstCall = TRUE; BOOLEAN freeInfo = FALSE; NTSTATUS status = STATUS_UNSUCCESSFUL;
if (firstCall) { KeInitializeSpinLock(&idleDeviceListSpinLock); InitializeListHead(&idleDeviceList); KeInitializeTimerEx(&idleTimer, NotificationTimer); KeInitializeDpc(&idleTimerDpc, HidpIdleTimerDpcProc, NULL); firstCall = FALSE; }
KeAcquireSpinLock(&idleDeviceListSpinLock, &irql); if (IdleTime == 0) { ASSERT(numIdleDevices >= 0);
//
// Remove the device from the list
//
for (entry = idleDeviceList.Flink; entry != &idleDeviceList; entry = entry->Flink) {
info = CONTAINING_RECORD(entry, HID_IDLE_DEVICE_INFO, entry); if (info->device == DeviceObject) { DBGINFO(("Remove device idle on fdo 0x%x", DeviceObject)); numIdleDevices--; ObDereferenceObject(DeviceObject); RemoveEntryList(entry); status = STATUS_SUCCESS; ExFreePool(info); *IdleTimeout = BAD_POINTER; break; } }
if (NT_SUCCESS(status)) { //
// If there are no more idle devices we can stop the timer
//
if (IsListEmpty(&idleDeviceList)) { ASSERT(numIdleDevices == 0); DBGINFO(("Idle detection list empty. Stopping timer.")); KeCancelTimer(&idleTimer); } } } else { LARGE_INTEGER scanTime; BOOLEAN empty = FALSE;
DBGINFO(("Register for device idle on fdo 0x%x", DeviceObject)); //
// Check if we've already started this.
//
status = STATUS_SUCCESS; for (entry = idleDeviceList.Flink; entry != &idleDeviceList; entry = entry->Flink) {
info = CONTAINING_RECORD(entry, HID_IDLE_DEVICE_INFO, entry); if (info->device == DeviceObject) { DBGWARN(("Device already registered for idle detection. Ignoring.")); ASSERT(*IdleTimeout == &(info->idleCount)); status = STATUS_UNSUCCESSFUL; } }
if (NT_SUCCESS(status)) { info = (PHID_IDLE_DEVICE_INFO) ALLOCATEPOOL(NonPagedPool, sizeof(HID_IDLE_DEVICE_INFO));
if (info != NULL) { ObReferenceObject(DeviceObject);
RtlZeroMemory(info, sizeof(HID_IDLE_DEVICE_INFO)); info->device = DeviceObject; info->idleTime = IdleTime;
if (IsListEmpty(&idleDeviceList)) { empty = TRUE; } InsertTailList(&idleDeviceList, &info->entry);
*IdleTimeout = &(info->idleCount);
numIdleDevices++;
if (empty) { DBGINFO(("Starting idle detection timer for first time.")); //
// Turn on idle detection
//
scanTime = RtlConvertLongToLargeInteger(-10*1000*1000 * HID_IDLE_SCAN_INTERVAL);
KeSetTimerEx(&idleTimer, scanTime, HID_IDLE_SCAN_INTERVAL*1000, // call wants milliseconds
&idleTimerDpc); } } else { status = STATUS_INSUFFICIENT_RESOURCES; } } } KeReleaseSpinLock(&idleDeviceListSpinLock, irql);
return status; }
VOID HidpIdleTimerDpcProc( IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PVOID Context1, IN PVOID Context2 ) { PLIST_ENTRY entry; PHID_IDLE_DEVICE_INFO info; ULONG oldCount; KIRQL irql1, irql2; BOOLEAN ok = FALSE; PFDO_EXTENSION fdoExt; LONG idleState;
UNREFERENCED_PARAMETER(Context1); UNREFERENCED_PARAMETER(Context2);
KeAcquireSpinLock(&idleDeviceListSpinLock, &irql1);
entry = idleDeviceList.Flink; while (entry != &idleDeviceList) { info = CONTAINING_RECORD(entry, HID_IDLE_DEVICE_INFO, entry); fdoExt = &((PHIDCLASS_DEVICE_EXTENSION) info->device->DeviceExtension)->fdoExt; KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql2); oldCount = InterlockedIncrement(&info->idleCount);
if (info->tryAgain || ((oldCount+1) == info->idleTime)) { PIO_WORKITEM item = IoAllocateWorkItem(info->device); if (item) { info->tryAgain = FALSE; SS_TRAP; KeResetEvent(&fdoExt->idleDoneEvent); ASSERT(fdoExt->idleState != IdleIrpSent); ASSERT(fdoExt->idleState != IdleCallbackReceived); ASSERT(fdoExt->idleState != IdleComplete); idleState = InterlockedCompareExchange(&fdoExt->idleState, IdleIrpSent, IdleWaiting); if (fdoExt->idleState == IdleIrpSent) { ok = TRUE; } else { // We shouldn't get here if we're disabled.
ASSERT(idleState != IdleDisabled); DBGWARN(("Resetting timer to zero for fdo %x in state %x", info->device,fdoExt->idleState)); info->idleCount = 0; } if (ok) { IoQueueWorkItem(item, HidpIdleTimeWorker, DelayedWorkQueue, item); } else { IoFreeWorkItem(item); } } else { info->tryAgain = TRUE; } } KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql2);
entry = entry->Flink; }
KeReleaseSpinLock(&idleDeviceListSpinLock, irql1); }
NTSTATUS HidpIdleNotificationRequestComplete( PDEVICE_OBJECT DeviceObject, PIRP Irp, PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension ) { FDO_EXTENSION *fdoExt; PDO_EXTENSION *pdoExt; KIRQL irql; LONG prevIdleState = IdleWaiting; POWER_STATE powerState; NTSTATUS status = Irp->IoStatus.Status; ULONG count, i; PIRP delayedIrp; LIST_ENTRY dequeue, *entry; PIO_STACK_LOCATION stack;
//
// DeviceObject is NULL because we sent the irp
//
UNREFERENCED_PARAMETER(DeviceObject);
fdoExt = &HidDeviceExtension->fdoExt; DBGVERBOSE(("Idle irp completed status 0x%x for fdo 0x%x", status, fdoExt->fdo)); //
// Cancel any outstanding WW irp we queued up for the exclusive purpose
// of selective suspend.
//
KeAcquireSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, &irql); if (IsListEmpty(&fdoExt->collectionWaitWakeIrpQueue) && HidpIsWaitWakePending(fdoExt, FALSE)) { if (ISPTR(fdoExt->waitWakeIrp)) { DBGINFO(("Cancelling the WW irp that was queued for idle.")) IoCancelIrp(fdoExt->waitWakeIrp); } else { TRAP; } } KeReleaseSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, irql); switch (status) { case STATUS_SUCCESS: // we successfully idled the device we are either now back in D0,
// or will be very soon.
KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql); if (fdoExt->devicePowerState == PowerDeviceD0) { prevIdleState = InterlockedCompareExchange(&fdoExt->idleState, IdleWaiting, IdleComplete); DBGASSERT(fdoExt->idleState == IdleWaiting, ("IdleCompletion, prev state not IdleWaiting, actually %x",prevIdleState), TRUE); if (ISPTR(fdoExt->idleTimeoutValue)) { InterlockedExchange(fdoExt->idleTimeoutValue, 0); } } KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql); break;
case STATUS_INVALID_DEVICE_REQUEST: case STATUS_NOT_SUPPORTED: // the bus below does not support idle timeouts, forget about it
DBGINFO(("Bus does not support idle. Removing for fdo %x", fdoExt->fdo));
//
// Call to cancel idle notification.
//
ASSERT(fdoExt->idleState == IdleIrpSent); ASSERT(fdoExt->devicePowerState == PowerDeviceD0); fdoExt->idleState = IdleWaiting; HidpCancelIdleNotification(fdoExt, TRUE); KeSetEvent(&fdoExt->idleDoneEvent, 0, FALSE);
break;
// we cancelled the request
case STATUS_CANCELLED: DBGINFO(("Idle Irp completed cancelled"));
// transitioned into a power state where we could not idle out
case STATUS_POWER_STATE_INVALID:
// oops, there was already a request in the bus below us
case STATUS_DEVICE_BUSY:
default: //
// We must reset ourselves.
//
KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql); DBGASSERT((fdoExt->idleState != IdleWaiting), ("Idle completion, previous state was already waiting."), FALSE); prevIdleState = fdoExt->idleState; if (prevIdleState == IdleIrpSent) { ASSERT(fdoExt->devicePowerState == PowerDeviceD0); fdoExt->idleCancelling = FALSE; if (ISPTR(fdoExt->idleTimeoutValue) && prevIdleState != IdleComplete) { InterlockedExchange(fdoExt->idleTimeoutValue, 0); } InterlockedExchange(&fdoExt->idleState, IdleWaiting); } KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
if (prevIdleState == IdleComplete) { //
// We now have to power up the stack.
//
DBGINFO(("Fully idled. Must power up stack.")) powerState.DeviceState = PowerDeviceD0; PoRequestPowerIrp(((PHIDCLASS_DEVICE_EXTENSION) fdoExt->fdo->DeviceExtension)->hidExt.PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, HidpDelayedPowerPoRequestComplete, fdoExt, NULL); } else if (prevIdleState == IdleIrpSent) { //
// Dequeue any enqueued irps and send them on their way.
// This is for the case where we didn't make it to suspend, but
// enqueued irps anyways. I.e. using mouse, set caps lock on
// ps/2 keybd causing write to be sent to usb kbd.
//
if (fdoExt->devicePowerState == PowerDeviceD0) { for (i = 0; i < fdoExt->deviceRelations->Count; i++) { pdoExt = &((PHIDCLASS_DEVICE_EXTENSION) fdoExt->deviceRelations->Objects[i]->DeviceExtension)->pdoExt; //
// Resend all power delayed IRPs
//
count = DequeueAllPdoPowerDelayedIrps(pdoExt, &dequeue); DBGVERBOSE(("dequeued %d requests\n", count));
while (!IsListEmpty(&dequeue)) { entry = RemoveHeadList(&dequeue); delayedIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); stack = IoGetCurrentIrpStackLocation(delayedIrp);
DBGINFO(("resending %x to pdo %x in idle completion.\n", delayedIrp, pdoExt->pdo));
pdoExt->pdo->DriverObject-> MajorFunction[stack->MajorFunction] (pdoExt->pdo, delayedIrp); } } } /*
* We cancelled this IRP. * REGARDLESS of whether this IRP was actually completed by * the cancel routine or not * (i.e. regardless of the completion status) * set this event so that stuff can exit. * Don't touch the irp again. */ DBGINFO(("Set done event.")) KeSetEvent(&fdoExt->idleDoneEvent, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } break; }
return STATUS_MORE_PROCESSING_REQUIRED; }
VOID HidpIdleTimeWorker( PDEVICE_OBJECT DeviceObject, PIO_WORKITEM Item ) { FDO_EXTENSION *fdoExt; PIO_STACK_LOCATION stack; PIRP irp = NULL, irpToCancel = NULL; NTSTATUS status; KIRQL irql;
fdoExt = &((PHIDCLASS_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->fdoExt;
DBGINFO(("fdo 0x%x can idle out", fdoExt->fdo));
irp = fdoExt->idleNotificationRequest; ASSERT(ISPTR(irp));
if (ISPTR(irp)) { USHORT PacketSize; CCHAR StackSize; UCHAR AllocationFlags;
// Did anyone forget to pull their cancel routine?
ASSERT(irp->CancelRoutine == NULL) ;
AllocationFlags = irp->AllocationFlags; StackSize = irp->StackCount; PacketSize = IoSizeOfIrp(StackSize); IoInitializeIrp(irp, PacketSize, StackSize); irp->AllocationFlags = AllocationFlags; irp->Cancel = FALSE; irp->IoStatus.Status = STATUS_NOT_SUPPORTED; stack = IoGetNextIrpStackLocation(irp); stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST; stack->Parameters.DeviceIoControl.InputBufferLength = sizeof(fdoExt->idleCallbackInfo); stack->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID) &(fdoExt->idleCallbackInfo);
//
// Hook a completion routine for when the device completes.
//
IoSetCompletionRoutine(irp, HidpIdleNotificationRequestComplete, DeviceObject->DeviceExtension, TRUE, TRUE, TRUE);
//
// The hub will fail this request if the hub doesn't support selective
// suspend. By returning FALSE we remove ourselves from the
//
status = HidpCallDriver(fdoExt->fdo, irp); KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
if (status == STATUS_PENDING && fdoExt->idleCancelling) { irpToCancel = irp; }
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
if (irpToCancel) { IoCancelIrp(irpToCancel); }
}
IoFreeWorkItem(Item); }
BOOLEAN HidpStartIdleTimeout( FDO_EXTENSION *fdoExt, BOOLEAN DeviceStart ) { DEVICE_POWER_STATE deviceWakeableState = PowerDeviceUnspecified; USHORT deviceUsagePage, deviceUsage; USHORT usagePage, usage; ULONG iList, iDesc, iPdo; HANDLE hKey; NTSTATUS status; ULONG enabled; ULONG length; UNICODE_STRING s; KEY_VALUE_PARTIAL_INFORMATION partial; PHID_IDLE_DEVICE_INFO info; PLIST_ENTRY entry = NULL; PULONG idleTimeoutAddress;
if (fdoExt->idleState != IdleDisabled) { //
// We're already registered for idle detection.
//
return TRUE; } //
// If we can't wake the machine, forget about it
//
if (fdoExt->deviceCapabilities.SystemWake == PowerSystemUnspecified) { DBGVERBOSE(("Can't wake the system with these caps! Disabling SS.")); return FALSE; }
//
// If D1Latency, D2Latency, D3Latency are ever filled in, perhaps we should
// let these values help us determine which low power state to go to
//
deviceWakeableState = fdoExt->deviceCapabilities.DeviceWake; DBGVERBOSE(("DeviceWakeableState is D%d", deviceWakeableState-1));
if (deviceWakeableState == PowerDeviceUnspecified) { DBGVERBOSE(("Due to devcaps, can't idle wake from any state! Disabling SS.")); return FALSE; }
if (DeviceStart) { //
// Open the registry and make sure that the
// SelectiveSuspendEnabled value is set to 1.
//
// predispose to failure.
fdoExt->idleEnabledInRegistry = FALSE; if (!NT_SUCCESS(IoOpenDeviceRegistryKey(fdoExt->collectionPdoExtensions[0]->hidExt.PhysicalDeviceObject, PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_READ, &hKey))) { DBGVERBOSE(("Couldn't open device key to check for idle timeout value. Disabling SS.")); return FALSE; }
RtlInitUnicodeString(&s, HIDCLASS_SELECTIVE_SUSPEND_ON); status = ZwQueryValueKey(hKey, &s, KeyValuePartialInformation, &partial, sizeof(KEY_VALUE_PARTIAL_INFORMATION), &length); if (!NT_SUCCESS(status)) { DBGVERBOSE(("ZwQueryValueKey failed for fdo %x. Default to SS turned on if enabled.", fdoExt->fdo)); fdoExt->idleEnabled = TRUE; } else if (!partial.Data[0]) { DBGINFO(("Selective suspend is not turned on for this device.")); fdoExt->idleEnabled = FALSE; } else { fdoExt->idleEnabled = TRUE; }
RtlInitUnicodeString(&s, HIDCLASS_SELECTIVE_SUSPEND_ENABLED); status = ZwQueryValueKey(hKey, &s, KeyValuePartialInformation, &partial, sizeof(KEY_VALUE_PARTIAL_INFORMATION), &length);
ZwClose(hKey);
if (!NT_SUCCESS(status)) { DBGVERBOSE(("ZwQueryValueKey failed for fdo %x. Disabling SS.", fdoExt->fdo)); return FALSE; }
DBGASSERT(partial.Type == REG_BINARY, ("Registry key wrong type"), FALSE);
if (!partial.Data[0]) { DBGINFO(("Selective suspend is not enabled for this device in the hive. Disabling SS.")); return FALSE; } fdoExt->idleEnabledInRegistry = TRUE;
status = IoWMIRegistrationControl(fdoExt->fdo, WMIREG_ACTION_REGISTER); ASSERT(NT_SUCCESS(status));
}
if (!fdoExt->idleEnabledInRegistry || !fdoExt->idleEnabled) { return FALSE; }
DBGVERBOSE(("There are %d PDOs on FDO 0x%x", fdoExt->deviceDesc.CollectionDescLength, fdoExt));
ASSERT(ISPTR(fdoExt->deviceRelations)); //
// OK, we can selectively suspend this device.
// Allocate and initialize everything, then register.
//
fdoExt->idleNotificationRequest = IoAllocateIrp(fdoExt->fdo->StackSize, FALSE); if (fdoExt->idleNotificationRequest == NULL) { DBGWARN(("Failed to allocate idle notification irp")) return FALSE; }
status = HidpRegisterDeviceForIdleDetection(fdoExt->fdo, HID_DEFAULT_IDLE_TIME, &fdoExt->idleTimeoutValue); if (STATUS_SUCCESS == status) { //
// We have successfully registered all device for idle detection,
// send a WW irp down the FDO stack
//
fdoExt->idleState = IdleWaiting; return TRUE; } else { //
// We're already registered? Or did the alloc fail?
//
DBGSUCCESS(status, TRUE); return FALSE; } }
NTSTATUS HidpCheckIdleState( PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, PIRP Irp ) { KIRQL irql; LONG idleState; PFDO_EXTENSION fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt; NTSTATUS status = STATUS_SUCCESS; BOOLEAN cancelIdleIrp = FALSE; ASSERT(HidDeviceExtension->isClientPdo); KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
if (fdoExt->idleState == IdleWaiting || fdoExt->idleState == IdleDisabled) { //
// Done.
//
if (ISPTR(fdoExt->idleTimeoutValue) && fdoExt->idleState == IdleWaiting) { InterlockedExchange(fdoExt->idleTimeoutValue, 0); } KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql); return STATUS_SUCCESS; }
DBGINFO(("CheckIdleState on fdo %x", fdoExt->fdo))
status = EnqueuePowerDelayedIrp(HidDeviceExtension, Irp); if (STATUS_PENDING != status) { KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql); return status; } fdoExt->idleCancelling = TRUE;
idleState = fdoExt->idleState; switch (idleState) { case IdleWaiting: // bugbug.
// How'd this happen? We already tried this...
TRAP; break; case IdleIrpSent: case IdleCallbackReceived: case IdleComplete: cancelIdleIrp = TRUE; break;
case IdleDisabled: //
// Shouldn't get here.
//
DBGERR(("Already disabled.")); }
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
if (cancelIdleIrp) { IoCancelIrp(fdoExt->idleNotificationRequest); }
return status; }
VOID HidpSetDeviceBusy(PFDO_EXTENSION fdoExt) { KIRQL irql; BOOLEAN cancelIdleIrp = FALSE; LONG idleState;
KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
if (fdoExt->idleState == IdleWaiting || fdoExt->idleState == IdleDisabled || fdoExt->idleCancelling) { if (ISPTR(fdoExt->idleTimeoutValue) && fdoExt->idleState == IdleWaiting) { InterlockedExchange(fdoExt->idleTimeoutValue, 0); fdoExt->idleCancelling = FALSE; } //
// Done.
//
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql); return; }
fdoExt->idleCancelling = TRUE;
DBGVERBOSE(("HidpSetDeviceBusy on fdo %x", fdoExt->fdo)) idleState = fdoExt->idleState; switch (idleState) { case IdleWaiting: // bugbug.
// How'd this happen? We already tried this...
TRAP; break; case IdleIrpSent: case IdleCallbackReceived: case IdleComplete: cancelIdleIrp = TRUE; break;
case IdleDisabled: //
// Shouldn't get here.
//
DBGERR(("Already disabled.")); }
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
if (cancelIdleIrp) { IoCancelIrp(fdoExt->idleNotificationRequest); } }
VOID HidpCancelIdleNotification( PFDO_EXTENSION fdoExt, BOOLEAN removing // Whether this is happening on a remove device
) { KIRQL irql; BOOLEAN cancelIdleIrp = FALSE; LONG idleState; NTSTATUS status; DBGVERBOSE(("Cancelling idle notification for fdo 0x%x", fdoExt->fdo)); status = HidpRegisterDeviceForIdleDetection(fdoExt->fdo, 0, &fdoExt->idleTimeoutValue); KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql); InterlockedCompareExchange(&fdoExt->idleState, IdleDisabled, IdleWaiting); if (fdoExt->idleState == IdleDisabled) { DBGVERBOSE(("Was waiting or already disabled. Exitting.")) //
// Done.
//
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql); return; }
fdoExt->idleCancelling = TRUE; idleState = fdoExt->idleState;
DBGINFO(("Wait routine...")) switch (idleState) { case IdleWaiting: // How'd this happen? We already tried this...
TRAP; break; case IdleIrpSent: case IdleCallbackReceived: // FUlly idled.
case IdleComplete: cancelIdleIrp = TRUE; break;
case IdleDisabled: //
// Shouldn't get here.
//
TRAP; }
KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql); if (cancelIdleIrp) { // Don't need to check the return status of IoCancel, since we'll
// be waiting for the idleDoneEvent.
IoCancelIrp(fdoExt->idleNotificationRequest); } if (removing) { DBGINFO(("Removing fdo %x. Must wait", fdoExt->fdo)) /*
* Cancelling the IRP causes a lower driver to * complete it (either in a cancel routine or when * the driver checks Irp->Cancel just before queueing it). * Wait for the IRP to actually get cancelled. */ KeWaitForSingleObject( &fdoExt->idleDoneEvent, Executive, // wait reason
KernelMode, FALSE, // not alertable
NULL ); // no timeout
} DBGINFO(("Done cancelling idle notification on fdo %x", fdoExt->fdo)) idleState = InterlockedExchange(&fdoExt->idleState, IdleDisabled); ASSERT(fdoExt->idleState == IdleDisabled); }
|