Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

856 lines
27 KiB

  1. /*++
  2. Copyright (c) 1999, 2000 Microsoft Corporation
  3. Module Name:
  4. idle.c
  5. Abstract
  6. Author:
  7. Doron H.
  8. Environment:
  9. Kernel mode only
  10. Revision History:
  11. --*/
  12. #ifdef ALLOC_PRAGMA
  13. #endif
  14. #include "pch.h"
  15. KSPIN_LOCK idleDeviceListSpinLock;
  16. LIST_ENTRY idleDeviceList;
  17. KTIMER idleTimer;
  18. KDPC idleTimerDpc;
  19. LONG numIdleDevices = 0;
  20. #define HID_IDLE_SCAN_INTERVAL 1
  21. typedef struct _HID_IDLE_DEVICE_INFO {
  22. LIST_ENTRY entry;
  23. ULONG idleCount;
  24. ULONG idleTime;
  25. PDEVICE_OBJECT device;
  26. BOOLEAN tryAgain;
  27. } HID_IDLE_DEVICE_INFO, *PHID_IDLE_DEVICE_INFO;
  28. VOID
  29. HidpIdleTimerDpcProc(
  30. IN PKDPC Dpc,
  31. IN PDEVICE_OBJECT DeviceObject,
  32. IN PVOID Context1,
  33. IN PVOID Context2
  34. );
  35. NTSTATUS
  36. HidpRegisterDeviceForIdleDetection(
  37. PDEVICE_OBJECT DeviceObject,
  38. ULONG IdleTime,
  39. PULONG *IdleTimeout
  40. )
  41. {
  42. PHID_IDLE_DEVICE_INFO info = NULL;
  43. KIRQL irql;
  44. PLIST_ENTRY entry = NULL;
  45. static BOOLEAN firstCall = TRUE;
  46. BOOLEAN freeInfo = FALSE;
  47. NTSTATUS status = STATUS_UNSUCCESSFUL;
  48. if (firstCall) {
  49. KeInitializeSpinLock(&idleDeviceListSpinLock);
  50. InitializeListHead(&idleDeviceList);
  51. KeInitializeTimerEx(&idleTimer, NotificationTimer);
  52. KeInitializeDpc(&idleTimerDpc, HidpIdleTimerDpcProc, NULL);
  53. firstCall = FALSE;
  54. }
  55. KeAcquireSpinLock(&idleDeviceListSpinLock, &irql);
  56. if (IdleTime == 0) {
  57. ASSERT(numIdleDevices >= 0);
  58. //
  59. // Remove the device from the list
  60. //
  61. for (entry = idleDeviceList.Flink;
  62. entry != &idleDeviceList;
  63. entry = entry->Flink) {
  64. info = CONTAINING_RECORD(entry, HID_IDLE_DEVICE_INFO, entry);
  65. if (info->device == DeviceObject) {
  66. DBGINFO(("Remove device idle on fdo 0x%x", DeviceObject));
  67. numIdleDevices--;
  68. ObDereferenceObject(DeviceObject);
  69. RemoveEntryList(entry);
  70. status = STATUS_SUCCESS;
  71. ExFreePool(info);
  72. *IdleTimeout = BAD_POINTER;
  73. break;
  74. }
  75. }
  76. if (NT_SUCCESS(status)) {
  77. //
  78. // If there are no more idle devices we can stop the timer
  79. //
  80. if (IsListEmpty(&idleDeviceList)) {
  81. ASSERT(numIdleDevices == 0);
  82. DBGINFO(("Idle detection list empty. Stopping timer."));
  83. KeCancelTimer(&idleTimer);
  84. }
  85. }
  86. } else {
  87. LARGE_INTEGER scanTime;
  88. BOOLEAN empty = FALSE;
  89. DBGINFO(("Register for device idle on fdo 0x%x", DeviceObject));
  90. //
  91. // Check if we've already started this.
  92. //
  93. status = STATUS_SUCCESS;
  94. for (entry = idleDeviceList.Flink;
  95. entry != &idleDeviceList;
  96. entry = entry->Flink) {
  97. info = CONTAINING_RECORD(entry, HID_IDLE_DEVICE_INFO, entry);
  98. if (info->device == DeviceObject) {
  99. DBGWARN(("Device already registered for idle detection. Ignoring."));
  100. ASSERT(*IdleTimeout == &(info->idleCount));
  101. status = STATUS_UNSUCCESSFUL;
  102. }
  103. }
  104. if (NT_SUCCESS(status)) {
  105. info = (PHID_IDLE_DEVICE_INFO)
  106. ALLOCATEPOOL(NonPagedPool, sizeof(HID_IDLE_DEVICE_INFO));
  107. if (info != NULL) {
  108. ObReferenceObject(DeviceObject);
  109. RtlZeroMemory(info, sizeof(HID_IDLE_DEVICE_INFO));
  110. info->device = DeviceObject;
  111. info->idleTime = IdleTime;
  112. if (IsListEmpty(&idleDeviceList)) {
  113. empty = TRUE;
  114. }
  115. InsertTailList(&idleDeviceList, &info->entry);
  116. *IdleTimeout = &(info->idleCount);
  117. numIdleDevices++;
  118. if (empty) {
  119. DBGINFO(("Starting idle detection timer for first time."));
  120. //
  121. // Turn on idle detection
  122. //
  123. scanTime = RtlConvertLongToLargeInteger(-10*1000*1000 * HID_IDLE_SCAN_INTERVAL);
  124. KeSetTimerEx(&idleTimer,
  125. scanTime,
  126. HID_IDLE_SCAN_INTERVAL*1000, // call wants milliseconds
  127. &idleTimerDpc);
  128. }
  129. } else {
  130. status = STATUS_INSUFFICIENT_RESOURCES;
  131. }
  132. }
  133. }
  134. KeReleaseSpinLock(&idleDeviceListSpinLock, irql);
  135. return status;
  136. }
  137. VOID
  138. HidpIdleTimerDpcProc(
  139. IN PKDPC Dpc,
  140. IN PDEVICE_OBJECT DeviceObject,
  141. IN PVOID Context1,
  142. IN PVOID Context2
  143. )
  144. {
  145. PLIST_ENTRY entry;
  146. PHID_IDLE_DEVICE_INFO info;
  147. ULONG oldCount;
  148. KIRQL irql1, irql2;
  149. BOOLEAN ok = FALSE;
  150. PFDO_EXTENSION fdoExt;
  151. LONG idleState;
  152. UNREFERENCED_PARAMETER(Context1);
  153. UNREFERENCED_PARAMETER(Context2);
  154. KeAcquireSpinLock(&idleDeviceListSpinLock, &irql1);
  155. entry = idleDeviceList.Flink;
  156. while (entry != &idleDeviceList) {
  157. info = CONTAINING_RECORD(entry, HID_IDLE_DEVICE_INFO, entry);
  158. fdoExt = &((PHIDCLASS_DEVICE_EXTENSION) info->device->DeviceExtension)->fdoExt;
  159. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql2);
  160. oldCount = InterlockedIncrement(&info->idleCount);
  161. if (info->tryAgain || ((oldCount+1) == info->idleTime)) {
  162. PIO_WORKITEM item = IoAllocateWorkItem(info->device);
  163. if (item) {
  164. info->tryAgain = FALSE;
  165. SS_TRAP;
  166. KeResetEvent(&fdoExt->idleDoneEvent);
  167. ASSERT(fdoExt->idleState != IdleIrpSent);
  168. ASSERT(fdoExt->idleState != IdleCallbackReceived);
  169. ASSERT(fdoExt->idleState != IdleComplete);
  170. idleState = InterlockedCompareExchange(&fdoExt->idleState,
  171. IdleIrpSent,
  172. IdleWaiting);
  173. if (fdoExt->idleState == IdleIrpSent) {
  174. ok = TRUE;
  175. } else {
  176. // We shouldn't get here if we're disabled.
  177. ASSERT(idleState != IdleDisabled);
  178. DBGWARN(("Resetting timer to zero for fdo %x in state %x",
  179. info->device,fdoExt->idleState));
  180. info->idleCount = 0;
  181. }
  182. if (ok) {
  183. IoQueueWorkItem(item,
  184. HidpIdleTimeWorker,
  185. DelayedWorkQueue,
  186. item);
  187. } else {
  188. IoFreeWorkItem(item);
  189. }
  190. } else {
  191. info->tryAgain = TRUE;
  192. }
  193. }
  194. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql2);
  195. entry = entry->Flink;
  196. }
  197. KeReleaseSpinLock(&idleDeviceListSpinLock, irql1);
  198. }
  199. NTSTATUS
  200. HidpIdleNotificationRequestComplete(
  201. PDEVICE_OBJECT DeviceObject,
  202. PIRP Irp,
  203. PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension
  204. )
  205. {
  206. FDO_EXTENSION *fdoExt;
  207. PDO_EXTENSION *pdoExt;
  208. KIRQL irql;
  209. LONG prevIdleState = IdleWaiting;
  210. POWER_STATE powerState;
  211. NTSTATUS status = Irp->IoStatus.Status;
  212. ULONG count, i;
  213. PIRP delayedIrp;
  214. LIST_ENTRY dequeue, *entry;
  215. PIO_STACK_LOCATION stack;
  216. //
  217. // DeviceObject is NULL because we sent the irp
  218. //
  219. UNREFERENCED_PARAMETER(DeviceObject);
  220. fdoExt = &HidDeviceExtension->fdoExt;
  221. DBGVERBOSE(("Idle irp completed status 0x%x for fdo 0x%x",
  222. status, fdoExt->fdo));
  223. //
  224. // Cancel any outstanding WW irp we queued up for the exclusive purpose
  225. // of selective suspend.
  226. //
  227. KeAcquireSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, &irql);
  228. if (IsListEmpty(&fdoExt->collectionWaitWakeIrpQueue) &&
  229. HidpIsWaitWakePending(fdoExt, FALSE)) {
  230. if (ISPTR(fdoExt->waitWakeIrp)) {
  231. DBGINFO(("Cancelling the WW irp that was queued for idle."))
  232. IoCancelIrp(fdoExt->waitWakeIrp);
  233. } else {
  234. TRAP;
  235. }
  236. }
  237. KeReleaseSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, irql);
  238. switch (status) {
  239. case STATUS_SUCCESS:
  240. // we successfully idled the device we are either now back in D0,
  241. // or will be very soon.
  242. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  243. if (fdoExt->devicePowerState == PowerDeviceD0) {
  244. prevIdleState = InterlockedCompareExchange(&fdoExt->idleState,
  245. IdleWaiting,
  246. IdleComplete);
  247. DBGASSERT(fdoExt->idleState == IdleWaiting,
  248. ("IdleCompletion, prev state not IdleWaiting, actually %x",prevIdleState),
  249. TRUE);
  250. if (ISPTR(fdoExt->idleTimeoutValue)) {
  251. InterlockedExchange(fdoExt->idleTimeoutValue, 0);
  252. }
  253. }
  254. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  255. break;
  256. case STATUS_INVALID_DEVICE_REQUEST:
  257. case STATUS_NOT_SUPPORTED:
  258. // the bus below does not support idle timeouts, forget about it
  259. DBGINFO(("Bus does not support idle. Removing for fdo %x",
  260. fdoExt->fdo));
  261. //
  262. // Call to cancel idle notification.
  263. //
  264. ASSERT(fdoExt->idleState == IdleIrpSent);
  265. ASSERT(fdoExt->devicePowerState == PowerDeviceD0);
  266. fdoExt->idleState = IdleWaiting;
  267. HidpCancelIdleNotification(fdoExt, TRUE);
  268. KeSetEvent(&fdoExt->idleDoneEvent, 0, FALSE);
  269. break;
  270. // we cancelled the request
  271. case STATUS_CANCELLED:
  272. DBGINFO(("Idle Irp completed cancelled"));
  273. // transitioned into a power state where we could not idle out
  274. case STATUS_POWER_STATE_INVALID:
  275. // oops, there was already a request in the bus below us
  276. case STATUS_DEVICE_BUSY:
  277. default:
  278. //
  279. // We must reset ourselves.
  280. //
  281. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  282. DBGASSERT((fdoExt->idleState != IdleWaiting),
  283. ("Idle completion, previous state was already waiting."),
  284. FALSE);
  285. prevIdleState = fdoExt->idleState;
  286. if (prevIdleState == IdleIrpSent) {
  287. ASSERT(fdoExt->devicePowerState == PowerDeviceD0);
  288. fdoExt->idleCancelling = FALSE;
  289. if (ISPTR(fdoExt->idleTimeoutValue) &&
  290. prevIdleState != IdleComplete) {
  291. InterlockedExchange(fdoExt->idleTimeoutValue, 0);
  292. }
  293. InterlockedExchange(&fdoExt->idleState, IdleWaiting);
  294. }
  295. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  296. if (prevIdleState == IdleComplete) {
  297. //
  298. // We now have to power up the stack.
  299. //
  300. DBGINFO(("Fully idled. Must power up stack."))
  301. powerState.DeviceState = PowerDeviceD0;
  302. PoRequestPowerIrp(((PHIDCLASS_DEVICE_EXTENSION) fdoExt->fdo->DeviceExtension)->hidExt.PhysicalDeviceObject,
  303. IRP_MN_SET_POWER,
  304. powerState,
  305. HidpDelayedPowerPoRequestComplete,
  306. fdoExt,
  307. NULL);
  308. } else if (prevIdleState == IdleIrpSent) {
  309. //
  310. // Dequeue any enqueued irps and send them on their way.
  311. // This is for the case where we didn't make it to suspend, but
  312. // enqueued irps anyways. I.e. using mouse, set caps lock on
  313. // ps/2 keybd causing write to be sent to usb kbd.
  314. //
  315. if (fdoExt->devicePowerState == PowerDeviceD0) {
  316. for (i = 0; i < fdoExt->deviceRelations->Count; i++) {
  317. pdoExt = &((PHIDCLASS_DEVICE_EXTENSION) fdoExt->deviceRelations->Objects[i]->DeviceExtension)->pdoExt;
  318. //
  319. // Resend all power delayed IRPs
  320. //
  321. count = DequeueAllPdoPowerDelayedIrps(pdoExt, &dequeue);
  322. DBGVERBOSE(("dequeued %d requests\n", count));
  323. while (!IsListEmpty(&dequeue)) {
  324. entry = RemoveHeadList(&dequeue);
  325. delayedIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
  326. stack = IoGetCurrentIrpStackLocation(delayedIrp);
  327. DBGINFO(("resending %x to pdo %x in idle completion.\n", delayedIrp, pdoExt->pdo));
  328. pdoExt->pdo->DriverObject->
  329. MajorFunction[stack->MajorFunction]
  330. (pdoExt->pdo, delayedIrp);
  331. }
  332. }
  333. }
  334. /*
  335. * We cancelled this IRP.
  336. * REGARDLESS of whether this IRP was actually completed by
  337. * the cancel routine or not
  338. * (i.e. regardless of the completion status)
  339. * set this event so that stuff can exit.
  340. * Don't touch the irp again.
  341. */
  342. DBGINFO(("Set done event."))
  343. KeSetEvent(&fdoExt->idleDoneEvent, 0, FALSE);
  344. return STATUS_MORE_PROCESSING_REQUIRED;
  345. }
  346. break;
  347. }
  348. return STATUS_MORE_PROCESSING_REQUIRED;
  349. }
  350. VOID
  351. HidpIdleTimeWorker(
  352. PDEVICE_OBJECT DeviceObject,
  353. PIO_WORKITEM Item
  354. )
  355. {
  356. FDO_EXTENSION *fdoExt;
  357. PIO_STACK_LOCATION stack;
  358. PIRP irp = NULL, irpToCancel = NULL;
  359. NTSTATUS status;
  360. KIRQL irql;
  361. fdoExt = &((PHIDCLASS_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->fdoExt;
  362. DBGINFO(("fdo 0x%x can idle out", fdoExt->fdo));
  363. irp = fdoExt->idleNotificationRequest;
  364. ASSERT(ISPTR(irp));
  365. if (ISPTR(irp)) {
  366. USHORT PacketSize;
  367. CCHAR StackSize;
  368. UCHAR AllocationFlags;
  369. // Did anyone forget to pull their cancel routine?
  370. ASSERT(irp->CancelRoutine == NULL) ;
  371. AllocationFlags = irp->AllocationFlags;
  372. StackSize = irp->StackCount;
  373. PacketSize = IoSizeOfIrp(StackSize);
  374. IoInitializeIrp(irp, PacketSize, StackSize);
  375. irp->AllocationFlags = AllocationFlags;
  376. irp->Cancel = FALSE;
  377. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  378. stack = IoGetNextIrpStackLocation(irp);
  379. stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  380. stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST;
  381. stack->Parameters.DeviceIoControl.InputBufferLength = sizeof(fdoExt->idleCallbackInfo);
  382. stack->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID) &(fdoExt->idleCallbackInfo);
  383. //
  384. // Hook a completion routine for when the device completes.
  385. //
  386. IoSetCompletionRoutine(irp,
  387. HidpIdleNotificationRequestComplete,
  388. DeviceObject->DeviceExtension,
  389. TRUE,
  390. TRUE,
  391. TRUE);
  392. //
  393. // The hub will fail this request if the hub doesn't support selective
  394. // suspend. By returning FALSE we remove ourselves from the
  395. //
  396. status = HidpCallDriver(fdoExt->fdo, irp);
  397. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  398. if (status == STATUS_PENDING &&
  399. fdoExt->idleCancelling) {
  400. irpToCancel = irp;
  401. }
  402. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  403. if (irpToCancel) {
  404. IoCancelIrp(irpToCancel);
  405. }
  406. }
  407. IoFreeWorkItem(Item);
  408. }
  409. BOOLEAN HidpStartIdleTimeout(
  410. FDO_EXTENSION *fdoExt,
  411. BOOLEAN DeviceStart
  412. )
  413. {
  414. DEVICE_POWER_STATE deviceWakeableState = PowerDeviceUnspecified;
  415. USHORT deviceUsagePage, deviceUsage;
  416. USHORT usagePage, usage;
  417. ULONG iList, iDesc, iPdo;
  418. HANDLE hKey;
  419. NTSTATUS status;
  420. ULONG enabled;
  421. ULONG length;
  422. UNICODE_STRING s;
  423. KEY_VALUE_PARTIAL_INFORMATION partial;
  424. PHID_IDLE_DEVICE_INFO info;
  425. PLIST_ENTRY entry = NULL;
  426. PULONG idleTimeoutAddress;
  427. if (fdoExt->idleState != IdleDisabled) {
  428. //
  429. // We're already registered for idle detection.
  430. //
  431. return TRUE;
  432. }
  433. //
  434. // If we can't wake the machine, forget about it
  435. //
  436. if (fdoExt->deviceCapabilities.SystemWake == PowerSystemUnspecified) {
  437. DBGVERBOSE(("Can't wake the system with these caps! Disabling SS."));
  438. return FALSE;
  439. }
  440. //
  441. // If D1Latency, D2Latency, D3Latency are ever filled in, perhaps we should
  442. // let these values help us determine which low power state to go to
  443. //
  444. deviceWakeableState = fdoExt->deviceCapabilities.DeviceWake;
  445. DBGVERBOSE(("DeviceWakeableState is D%d", deviceWakeableState-1));
  446. if (deviceWakeableState == PowerDeviceUnspecified) {
  447. DBGVERBOSE(("Due to devcaps, can't idle wake from any state! Disabling SS."));
  448. return FALSE;
  449. }
  450. if (DeviceStart) {
  451. //
  452. // Open the registry and make sure that the
  453. // SelectiveSuspendEnabled value is set to 1.
  454. //
  455. // predispose to failure.
  456. fdoExt->idleEnabledInRegistry = FALSE;
  457. if (!NT_SUCCESS(IoOpenDeviceRegistryKey(fdoExt->collectionPdoExtensions[0]->hidExt.PhysicalDeviceObject,
  458. PLUGPLAY_REGKEY_DEVICE,
  459. STANDARD_RIGHTS_READ,
  460. &hKey))) {
  461. DBGVERBOSE(("Couldn't open device key to check for idle timeout value. Disabling SS."));
  462. return FALSE;
  463. }
  464. RtlInitUnicodeString(&s, HIDCLASS_SELECTIVE_SUSPEND_ON);
  465. status = ZwQueryValueKey(hKey,
  466. &s,
  467. KeyValuePartialInformation,
  468. &partial,
  469. sizeof(KEY_VALUE_PARTIAL_INFORMATION),
  470. &length);
  471. if (!NT_SUCCESS(status)) {
  472. DBGVERBOSE(("ZwQueryValueKey failed for fdo %x. Default to SS turned on if enabled.", fdoExt->fdo));
  473. fdoExt->idleEnabled = TRUE;
  474. } else if (!partial.Data[0]) {
  475. DBGINFO(("Selective suspend is not turned on for this device."));
  476. fdoExt->idleEnabled = FALSE;
  477. } else {
  478. fdoExt->idleEnabled = TRUE;
  479. }
  480. RtlInitUnicodeString(&s, HIDCLASS_SELECTIVE_SUSPEND_ENABLED);
  481. status = ZwQueryValueKey(hKey,
  482. &s,
  483. KeyValuePartialInformation,
  484. &partial,
  485. sizeof(KEY_VALUE_PARTIAL_INFORMATION),
  486. &length);
  487. ZwClose(hKey);
  488. if (!NT_SUCCESS(status)) {
  489. DBGVERBOSE(("ZwQueryValueKey failed for fdo %x. Disabling SS.", fdoExt->fdo));
  490. return FALSE;
  491. }
  492. DBGASSERT(partial.Type == REG_BINARY, ("Registry key wrong type"), FALSE);
  493. if (!partial.Data[0]) {
  494. DBGINFO(("Selective suspend is not enabled for this device in the hive. Disabling SS."));
  495. return FALSE;
  496. }
  497. fdoExt->idleEnabledInRegistry = TRUE;
  498. status = IoWMIRegistrationControl(fdoExt->fdo,
  499. WMIREG_ACTION_REGISTER);
  500. ASSERT(NT_SUCCESS(status));
  501. }
  502. if (!fdoExt->idleEnabledInRegistry || !fdoExt->idleEnabled) {
  503. return FALSE;
  504. }
  505. DBGVERBOSE(("There are %d PDOs on FDO 0x%x",
  506. fdoExt->deviceDesc.CollectionDescLength,
  507. fdoExt));
  508. ASSERT(ISPTR(fdoExt->deviceRelations));
  509. //
  510. // OK, we can selectively suspend this device.
  511. // Allocate and initialize everything, then register.
  512. //
  513. fdoExt->idleNotificationRequest = IoAllocateIrp(fdoExt->fdo->StackSize, FALSE);
  514. if (fdoExt->idleNotificationRequest == NULL) {
  515. DBGWARN(("Failed to allocate idle notification irp"))
  516. return FALSE;
  517. }
  518. status = HidpRegisterDeviceForIdleDetection(fdoExt->fdo,
  519. HID_DEFAULT_IDLE_TIME,
  520. &fdoExt->idleTimeoutValue);
  521. if (STATUS_SUCCESS == status) {
  522. //
  523. // We have successfully registered all device for idle detection,
  524. // send a WW irp down the FDO stack
  525. //
  526. fdoExt->idleState = IdleWaiting;
  527. return TRUE;
  528. } else {
  529. //
  530. // We're already registered? Or did the alloc fail?
  531. //
  532. DBGSUCCESS(status, TRUE);
  533. return FALSE;
  534. }
  535. }
  536. NTSTATUS
  537. HidpCheckIdleState(
  538. PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension,
  539. PIRP Irp
  540. )
  541. {
  542. KIRQL irql;
  543. LONG idleState;
  544. PFDO_EXTENSION fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
  545. NTSTATUS status = STATUS_SUCCESS;
  546. BOOLEAN cancelIdleIrp = FALSE;
  547. ASSERT(HidDeviceExtension->isClientPdo);
  548. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  549. if (fdoExt->idleState == IdleWaiting ||
  550. fdoExt->idleState == IdleDisabled) {
  551. //
  552. // Done.
  553. //
  554. if (ISPTR(fdoExt->idleTimeoutValue) &&
  555. fdoExt->idleState == IdleWaiting) {
  556. InterlockedExchange(fdoExt->idleTimeoutValue, 0);
  557. }
  558. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  559. return STATUS_SUCCESS;
  560. }
  561. DBGINFO(("CheckIdleState on fdo %x", fdoExt->fdo))
  562. status = EnqueuePowerDelayedIrp(HidDeviceExtension, Irp);
  563. if (STATUS_PENDING != status) {
  564. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  565. return status;
  566. }
  567. fdoExt->idleCancelling = TRUE;
  568. idleState = fdoExt->idleState;
  569. switch (idleState) {
  570. case IdleWaiting:
  571. // bugbug.
  572. // How'd this happen? We already tried this...
  573. TRAP;
  574. break;
  575. case IdleIrpSent:
  576. case IdleCallbackReceived:
  577. case IdleComplete:
  578. cancelIdleIrp = TRUE;
  579. break;
  580. case IdleDisabled:
  581. //
  582. // Shouldn't get here.
  583. //
  584. DBGERR(("Already disabled."));
  585. }
  586. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  587. if (cancelIdleIrp) {
  588. IoCancelIrp(fdoExt->idleNotificationRequest);
  589. }
  590. return status;
  591. }
  592. VOID
  593. HidpSetDeviceBusy(PFDO_EXTENSION fdoExt)
  594. {
  595. KIRQL irql;
  596. BOOLEAN cancelIdleIrp = FALSE;
  597. LONG idleState;
  598. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  599. if (fdoExt->idleState == IdleWaiting ||
  600. fdoExt->idleState == IdleDisabled ||
  601. fdoExt->idleCancelling) {
  602. if (ISPTR(fdoExt->idleTimeoutValue) &&
  603. fdoExt->idleState == IdleWaiting) {
  604. InterlockedExchange(fdoExt->idleTimeoutValue, 0);
  605. fdoExt->idleCancelling = FALSE;
  606. }
  607. //
  608. // Done.
  609. //
  610. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  611. return;
  612. }
  613. fdoExt->idleCancelling = TRUE;
  614. DBGVERBOSE(("HidpSetDeviceBusy on fdo %x", fdoExt->fdo))
  615. idleState = fdoExt->idleState;
  616. switch (idleState) {
  617. case IdleWaiting:
  618. // bugbug.
  619. // How'd this happen? We already tried this...
  620. TRAP;
  621. break;
  622. case IdleIrpSent:
  623. case IdleCallbackReceived:
  624. case IdleComplete:
  625. cancelIdleIrp = TRUE;
  626. break;
  627. case IdleDisabled:
  628. //
  629. // Shouldn't get here.
  630. //
  631. DBGERR(("Already disabled."));
  632. }
  633. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  634. if (cancelIdleIrp) {
  635. IoCancelIrp(fdoExt->idleNotificationRequest);
  636. }
  637. }
  638. VOID
  639. HidpCancelIdleNotification(
  640. PFDO_EXTENSION fdoExt,
  641. BOOLEAN removing // Whether this is happening on a remove device
  642. )
  643. {
  644. KIRQL irql;
  645. BOOLEAN cancelIdleIrp = FALSE;
  646. LONG idleState;
  647. NTSTATUS status;
  648. DBGVERBOSE(("Cancelling idle notification for fdo 0x%x", fdoExt->fdo));
  649. status = HidpRegisterDeviceForIdleDetection(fdoExt->fdo, 0, &fdoExt->idleTimeoutValue);
  650. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  651. InterlockedCompareExchange(&fdoExt->idleState,
  652. IdleDisabled,
  653. IdleWaiting);
  654. if (fdoExt->idleState == IdleDisabled) {
  655. DBGVERBOSE(("Was waiting or already disabled. Exitting."))
  656. //
  657. // Done.
  658. //
  659. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  660. return;
  661. }
  662. fdoExt->idleCancelling = TRUE;
  663. idleState = fdoExt->idleState;
  664. DBGINFO(("Wait routine..."))
  665. switch (idleState) {
  666. case IdleWaiting:
  667. // How'd this happen? We already tried this...
  668. TRAP;
  669. break;
  670. case IdleIrpSent:
  671. case IdleCallbackReceived:
  672. // FUlly idled.
  673. case IdleComplete:
  674. cancelIdleIrp = TRUE;
  675. break;
  676. case IdleDisabled:
  677. //
  678. // Shouldn't get here.
  679. //
  680. TRAP;
  681. }
  682. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  683. if (cancelIdleIrp) {
  684. // Don't need to check the return status of IoCancel, since we'll
  685. // be waiting for the idleDoneEvent.
  686. IoCancelIrp(fdoExt->idleNotificationRequest);
  687. }
  688. if (removing) {
  689. DBGINFO(("Removing fdo %x. Must wait", fdoExt->fdo))
  690. /*
  691. * Cancelling the IRP causes a lower driver to
  692. * complete it (either in a cancel routine or when
  693. * the driver checks Irp->Cancel just before queueing it).
  694. * Wait for the IRP to actually get cancelled.
  695. */
  696. KeWaitForSingleObject( &fdoExt->idleDoneEvent,
  697. Executive, // wait reason
  698. KernelMode,
  699. FALSE, // not alertable
  700. NULL ); // no timeout
  701. }
  702. DBGINFO(("Done cancelling idle notification on fdo %x", fdoExt->fdo))
  703. idleState = InterlockedExchange(&fdoExt->idleState, IdleDisabled);
  704. ASSERT(fdoExt->idleState == IdleDisabled);
  705. }