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.

1830 lines
60 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. power.c
  5. Abstract
  6. Power handling
  7. Author:
  8. ervinp
  9. Environment:
  10. Kernel mode only
  11. Revision History:
  12. --*/
  13. #include "pch.h"
  14. BOOLEAN HidpIsWaitWakePending(FDO_EXTENSION *fdoExt, BOOLEAN setIfNotPending)
  15. {
  16. KIRQL irql;
  17. BOOLEAN isWaitWakePending;
  18. KeAcquireSpinLock(&fdoExt->waitWakeSpinLock, &irql);
  19. isWaitWakePending = fdoExt->isWaitWakePending;
  20. if (fdoExt->isWaitWakePending == FALSE) {
  21. if (setIfNotPending) {
  22. fdoExt->isWaitWakePending = TRUE;
  23. }
  24. }
  25. KeReleaseSpinLock(&fdoExt->waitWakeSpinLock, irql);
  26. return isWaitWakePending;
  27. }
  28. VOID
  29. HidpPowerDownFdo(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension)
  30. {
  31. POWER_STATE powerState;
  32. FDO_EXTENSION *fdoExt;
  33. DBGVERBOSE(("powering down fdo 0x%x\n", HidDeviceExtension));
  34. fdoExt = &HidDeviceExtension->fdoExt;
  35. powerState.DeviceState = fdoExt->deviceCapabilities.DeviceWake;
  36. PoRequestPowerIrp(HidDeviceExtension->hidExt.PhysicalDeviceObject,
  37. IRP_MN_SET_POWER,
  38. powerState,
  39. NULL, // completion routine
  40. NULL, // completion routine context
  41. NULL);
  42. }
  43. VOID HidpPowerUpPdos(IN PFDO_EXTENSION fdoExt)
  44. {
  45. PDEVICE_OBJECT pdo;
  46. PDO_EXTENSION *pdoExt;
  47. POWER_STATE powerState;
  48. ULONG iPdo;
  49. iPdo = 0;
  50. powerState.DeviceState = PowerDeviceD0;
  51. for (iPdo = 0; iPdo < fdoExt->deviceRelations->Count; iPdo++) {
  52. pdoExt = &fdoExt->collectionPdoExtensions[iPdo]->pdoExt;
  53. pdo = pdoExt->pdo;
  54. DBGVERBOSE(("power up pdos, requesting D0 on pdo #%d %x\n", iPdo, pdo));
  55. //
  56. // We could check // pdoExt->devicePowerState != PowerDeviceD0
  57. // but, if the stack gets 2 D0 irps in a row, nothing bad should happen
  58. //
  59. PoRequestPowerIrp(pdo,
  60. IRP_MN_SET_POWER,
  61. powerState,
  62. NULL, // completion routine
  63. NULL, // context
  64. NULL);
  65. }
  66. HidpSetDeviceBusy(fdoExt);
  67. KeSetEvent(&fdoExt->idleDoneEvent, 0, FALSE);
  68. }
  69. VOID
  70. HidpPdoIdleOutComplete(
  71. IN PDEVICE_OBJECT DeviceObject,
  72. IN UCHAR MinorFunction,
  73. IN POWER_STATE PowerState,
  74. IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension,
  75. IN PIO_STATUS_BLOCK IoStatus
  76. )
  77. {
  78. FDO_EXTENSION *fdoExt = &HidDeviceExtension->fdoExt;
  79. LONG prevIdleState;
  80. BOOLEAN idleCancelling = FALSE;
  81. KIRQL irql;
  82. DBGSUCCESS(IoStatus->Status, TRUE)
  83. if (InterlockedDecrement(&fdoExt->numIdlePdos) == 0) {
  84. HidpPowerDownFdo(HidDeviceExtension);
  85. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  86. prevIdleState = InterlockedCompareExchange(&fdoExt->idleState,
  87. IdleComplete,
  88. IdleCallbackReceived);
  89. if (fdoExt->idleCancelling) {
  90. DBGINFO(("Cancelling idle in pdoidleoutcomplete on 0x%x\n", HidDeviceExtension));
  91. idleCancelling = TRUE;
  92. }
  93. DBGASSERT (prevIdleState == IdleCallbackReceived,
  94. ("Race condition in HidpPdoIdleOutComplete. Prev state = %x",
  95. prevIdleState),
  96. TRUE);
  97. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  98. KeResetEvent(&fdoExt->idleDoneEvent);
  99. if (idleCancelling) {
  100. POWER_STATE powerState;
  101. powerState.DeviceState = PowerDeviceD0;
  102. DBGINFO(("Cancelling idle. Send power irp from pdo idle complete."))
  103. PoRequestPowerIrp(((PHIDCLASS_DEVICE_EXTENSION) fdoExt->fdo->DeviceExtension)->hidExt.PhysicalDeviceObject,
  104. IRP_MN_SET_POWER,
  105. powerState,
  106. HidpDelayedPowerPoRequestComplete,
  107. fdoExt,
  108. NULL);
  109. }
  110. }
  111. }
  112. VOID HidpIdleNotificationCallback(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension)
  113. {
  114. PDEVICE_OBJECT pdo;
  115. FDO_EXTENSION *fdoExt;
  116. POWER_STATE powerState;
  117. ULONG iPdo;
  118. BOOLEAN ok = TRUE;
  119. KIRQL irql;
  120. LONG idleState, prevIdleState;
  121. iPdo = 0;
  122. fdoExt = &HidDeviceExtension->fdoExt;
  123. DBGINFO(("------ IDLE NOTIFICATION on fdo 0x%x\n", fdoExt->fdo));
  124. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  125. if (fdoExt->idleCancelling) {
  126. DBGINFO(("We are cancelling idle on fdo 0x%x", fdoExt->fdo));
  127. fdoExt->idleState = IdleWaiting;
  128. if (ISPTR(fdoExt->idleTimeoutValue)) {
  129. InterlockedExchange(fdoExt->idleTimeoutValue, 0);
  130. }
  131. fdoExt->idleCancelling = FALSE;
  132. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  133. IoCancelIrp(fdoExt->idleNotificationRequest);
  134. return;
  135. }
  136. prevIdleState = InterlockedCompareExchange(&fdoExt->idleState,
  137. IdleCallbackReceived,
  138. IdleIrpSent);
  139. DBGASSERT(prevIdleState == IdleIrpSent,
  140. ("Idle callback in wrong state %x for fdo %x. Exitting.",
  141. prevIdleState, fdoExt->fdo),
  142. FALSE);
  143. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  144. if (prevIdleState != IdleIrpSent) {
  145. return;
  146. }
  147. if (HidpIsWaitWakePending(fdoExt, TRUE) == FALSE) {
  148. SubmitWaitWakeIrp((HIDCLASS_DEVICE_EXTENSION *) fdoExt->fdo->DeviceExtension);
  149. }
  150. powerState.DeviceState = fdoExt->deviceCapabilities.DeviceWake;
  151. fdoExt->numIdlePdos = fdoExt->deviceRelations->Count+1;
  152. for (iPdo = 0; iPdo < fdoExt->deviceRelations->Count; iPdo++) {
  153. pdo = fdoExt->collectionPdoExtensions[iPdo]->pdoExt.pdo;
  154. DBGVERBOSE(("power down pdos, requesting D%d on pdo #%d %x\n",
  155. powerState.DeviceState-1, iPdo, pdo));
  156. //
  157. // We could check // pdoExt->devicePowerState != PowerDeviceD0
  158. // but, if the stack gets 2 D0 irps in a row, nothing bad should happen
  159. //
  160. PoRequestPowerIrp(pdo,
  161. IRP_MN_SET_POWER,
  162. powerState,
  163. HidpPdoIdleOutComplete,
  164. HidDeviceExtension,
  165. NULL);
  166. }
  167. if (InterlockedDecrement(&fdoExt->numIdlePdos) == 0) {
  168. BOOLEAN idleCancelling = FALSE;
  169. HidpPowerDownFdo(HidDeviceExtension);
  170. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  171. prevIdleState = InterlockedCompareExchange(&fdoExt->idleState,
  172. IdleComplete,
  173. IdleCallbackReceived);
  174. idleCancelling = fdoExt->idleCancelling;
  175. DBGASSERT (prevIdleState == IdleCallbackReceived,
  176. ("Race condition in HidpPdoIdleOutComplete. Prev state = %x",
  177. prevIdleState),
  178. FALSE);
  179. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  180. KeResetEvent(&fdoExt->idleDoneEvent);
  181. if (idleCancelling) {
  182. POWER_STATE powerState;
  183. powerState.DeviceState = PowerDeviceD0;
  184. DBGINFO(("Cancelling idle. Send power irp from idle callback."))
  185. PoRequestPowerIrp(((PHIDCLASS_DEVICE_EXTENSION) fdoExt->fdo->DeviceExtension)->hidExt.PhysicalDeviceObject,
  186. IRP_MN_SET_POWER,
  187. powerState,
  188. HidpDelayedPowerPoRequestComplete,
  189. fdoExt,
  190. NULL);
  191. }
  192. }
  193. }
  194. /*
  195. ********************************************************************************
  196. * EnqueueCollectionWaitWakeIrp
  197. ********************************************************************************
  198. *
  199. */
  200. NTSTATUS
  201. EnqueueCollectionWaitWakeIrp(
  202. IN FDO_EXTENSION *FdoExt,
  203. IN PDO_EXTENSION *PdoExt,
  204. IN PIRP WaitWakeIrp)
  205. {
  206. PDRIVER_CANCEL oldCancelRoutine;
  207. KIRQL oldIrql;
  208. NTSTATUS status;
  209. PHIDCLASS_DEVICE_EXTENSION devExt = (PHIDCLASS_DEVICE_EXTENSION)FdoExt->fdo->DeviceExtension;
  210. KeAcquireSpinLock(&FdoExt->collectionWaitWakeIrpQueueSpinLock, &oldIrql);
  211. if (InterlockedCompareExchangePointer(&PdoExt->waitWakeIrp,
  212. WaitWakeIrp,
  213. NULL) != NULL) {
  214. //
  215. // More than one WW irp? Unthinkable!
  216. //
  217. DBGWARN(("Another WW irp was already queued on pdoExt %x", PdoExt))
  218. status = STATUS_INVALID_DEVICE_STATE;
  219. } else {
  220. /*
  221. * Must set a cancel routine before checking the Cancel flag
  222. * (this makes the cancel code path for the IRP have to contend
  223. * for our local spinlock).
  224. */
  225. oldCancelRoutine = IoSetCancelRoutine(WaitWakeIrp, CollectionWaitWakeIrpCancelRoutine);
  226. ASSERT(!oldCancelRoutine);
  227. if (WaitWakeIrp->Cancel){
  228. /*
  229. * This IRP has already been cancelled.
  230. */
  231. oldCancelRoutine = IoSetCancelRoutine(WaitWakeIrp, NULL);
  232. if (oldCancelRoutine){
  233. /*
  234. * Cancel routine was NOT called, so complete the IRP here
  235. * (caller will do this when we return error).
  236. */
  237. ASSERT(oldCancelRoutine == CollectionWaitWakeIrpCancelRoutine);
  238. status = STATUS_CANCELLED;
  239. }
  240. else {
  241. /*
  242. * Cancel routine was called, and it will dequeue and complete the IRP
  243. * as soon as we drop the spinlock.
  244. * Initialize the IRP's listEntry so the dequeue doesn't corrupt the list.
  245. * Then return STATUS_PENDING so we don't touch the IRP
  246. */
  247. InitializeListHead(&WaitWakeIrp->Tail.Overlay.ListEntry);
  248. IoMarkIrpPending(WaitWakeIrp);
  249. status = STATUS_PENDING;
  250. }
  251. }
  252. else {
  253. /*
  254. * IoMarkIrpPending sets a bit in the current stack location
  255. * to indicate that the Irp may complete on a different thread.
  256. */
  257. InsertTailList(&FdoExt->collectionWaitWakeIrpQueue, &WaitWakeIrp->Tail.Overlay.ListEntry);
  258. IoMarkIrpPending(WaitWakeIrp);
  259. status = STATUS_PENDING;
  260. }
  261. }
  262. if (status != STATUS_PENDING) {
  263. //
  264. // The irp was cancelled. Remove it from the extension.
  265. //
  266. InterlockedExchangePointer(&PdoExt->waitWakeIrp, NULL);
  267. }
  268. KeReleaseSpinLock(&FdoExt->collectionWaitWakeIrpQueueSpinLock, oldIrql);
  269. if (status == STATUS_PENDING){
  270. #if WIN95_BUILD
  271. DBGERR(("WaitWake IRP sent by client on Win98 ???"))
  272. #else
  273. if (!HidpIsWaitWakePending(FdoExt, TRUE)){
  274. DBGVERBOSE(("WW 5 %x\n", devExt))
  275. SubmitWaitWakeIrp(devExt);
  276. }
  277. #endif
  278. }
  279. return status;
  280. }
  281. NTSTATUS
  282. HidpPdoPower(
  283. IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension,
  284. IN OUT PIRP Irp
  285. )
  286. {
  287. NTSTATUS status = NO_STATUS;
  288. PIO_STACK_LOCATION irpSp;
  289. FDO_EXTENSION *fdoExt;
  290. PDO_EXTENSION *pdoExt;
  291. KIRQL oldIrql;
  292. UCHAR minorFunction;
  293. LIST_ENTRY dequeue, *entry;
  294. PIO_STACK_LOCATION stack;
  295. PIRP irp;
  296. ULONG count;
  297. POWER_STATE powerState;
  298. SYSTEM_POWER_STATE systemState;
  299. BOOLEAN justReturnPending = FALSE;
  300. BOOLEAN runPowerCode;
  301. irpSp = IoGetCurrentIrpStackLocation(Irp);
  302. /*
  303. * Keep these privately so we still have it after the IRP completes
  304. * or after the device extension is freed on a REMOVE_DEVICE
  305. */
  306. minorFunction = irpSp->MinorFunction;
  307. pdoExt = &HidDeviceExtension->pdoExt;
  308. fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
  309. runPowerCode =
  310. (pdoExt->state == COLLECTION_STATE_RUNNING) ||
  311. (pdoExt->state == COLLECTION_STATE_STOPPED) ||
  312. (pdoExt->state == COLLECTION_STATE_STOPPING);
  313. if (runPowerCode) {
  314. switch (minorFunction){
  315. case IRP_MN_SET_POWER:
  316. PoSetPowerState(pdoExt->pdo,
  317. irpSp->Parameters.Power.Type,
  318. irpSp->Parameters.Power.State);
  319. switch (irpSp->Parameters.Power.Type) {
  320. case SystemPowerState:
  321. systemState = irpSp->Parameters.Power.State.SystemState;
  322. pdoExt->systemPowerState = systemState;
  323. if (systemState == PowerSystemWorking){
  324. powerState.DeviceState = PowerDeviceD0;
  325. }
  326. else {
  327. powerState.DeviceState = PowerDeviceD3;
  328. }
  329. DBGVERBOSE(("S irp, requesting D%d on pdo %x\n",
  330. powerState.DeviceState-1, pdoExt->pdo));
  331. IoMarkIrpPending(Irp);
  332. PoRequestPowerIrp(pdoExt->pdo,
  333. IRP_MN_SET_POWER,
  334. powerState,
  335. CollectionPowerRequestCompletion,
  336. Irp, // context
  337. NULL);
  338. /*
  339. * We want to complete the system-state power Irp
  340. * with the result of the device-state power Irp.
  341. * We'll complete the system-state power Irp when
  342. * the device-state power Irp completes.
  343. *
  344. * Note: this may have ALREADY happened, so don't
  345. * touch the original Irp anymore.
  346. */
  347. status = STATUS_PENDING;
  348. justReturnPending = TRUE;
  349. break;
  350. case DevicePowerState:
  351. switch (irpSp->Parameters.Power.State.DeviceState) {
  352. case PowerDeviceD0:
  353. /*
  354. * Resume from APM Suspend
  355. *
  356. * Do nothing here; Send down the read IRPs in the
  357. * completion routine for this (the power) IRP.
  358. */
  359. DBGVERBOSE(("pdo %x on fdo %x going to D0\n", pdoExt->pdo,
  360. fdoExt->fdo));
  361. pdoExt->devicePowerState =
  362. irpSp->Parameters.Power.State.DeviceState;
  363. status = STATUS_SUCCESS;
  364. //
  365. // Resend all power delayed IRPs
  366. //
  367. count = DequeueAllPdoPowerDelayedIrps(pdoExt, &dequeue);
  368. DBGVERBOSE(("dequeued %d requests\n", count));
  369. while (!IsListEmpty(&dequeue)) {
  370. entry = RemoveHeadList(&dequeue);
  371. irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
  372. stack = IoGetCurrentIrpStackLocation(irp);
  373. DBGINFO(("resending %x to pdo %x in set D0 for pdo.\n", irp, pdoExt->pdo));
  374. pdoExt->pdo->DriverObject->
  375. MajorFunction[stack->MajorFunction]
  376. (pdoExt->pdo, irp);
  377. }
  378. break;
  379. case PowerDeviceD1:
  380. case PowerDeviceD2:
  381. case PowerDeviceD3:
  382. /*
  383. * Suspend
  384. */
  385. DBGVERBOSE(("pdo %x on fdo %x going to D%d\n", pdoExt->pdo,
  386. fdoExt->fdo,
  387. irpSp->Parameters.Power.State.DeviceState-1));
  388. pdoExt->devicePowerState =
  389. irpSp->Parameters.Power.State.DeviceState;
  390. status = STATUS_SUCCESS;
  391. //
  392. // Only manually power down the PDO if the
  393. // machine is not going into low power,
  394. // the PDO is going into a D state we can
  395. // wake out of, and we have idle time out
  396. // enabled.
  397. //
  398. if (pdoExt->systemPowerState == PowerSystemWorking &&
  399. pdoExt->devicePowerState <= fdoExt->deviceCapabilities.DeviceWake &&
  400. fdoExt->idleState != IdleDisabled) {
  401. DBGVERBOSE(("maybe powering down fdo\n"));
  402. HidpPowerDownFdo(HidDeviceExtension->pdoExt.deviceFdoExt);
  403. }
  404. break;
  405. default:
  406. /*
  407. * Do not return STATUS_NOT_SUPPORTED;
  408. * keep the default status
  409. * (this allows filter drivers to work).
  410. */
  411. status = Irp->IoStatus.Status;
  412. break;
  413. }
  414. break;
  415. default:
  416. /*
  417. * Do not return STATUS_NOT_SUPPORTED;
  418. * keep the default status
  419. * (this allows filter drivers to work).
  420. */
  421. status = Irp->IoStatus.Status;
  422. break;
  423. }
  424. break;
  425. case IRP_MN_WAIT_WAKE:
  426. /*
  427. * WaitWake IRPs to the collection-PDO's
  428. * just get queued in the base device's extension;
  429. * when the base device's WaitWake IRP gets
  430. * completed, we'll also complete these collection
  431. * WaitWake IRPs.
  432. */
  433. if (fdoExt->systemPowerState > fdoExt->deviceCapabilities.SystemWake) {
  434. status = STATUS_POWER_STATE_INVALID;
  435. } else {
  436. status = EnqueueCollectionWaitWakeIrp(fdoExt, pdoExt, Irp);
  437. if (status == STATUS_PENDING){
  438. justReturnPending = TRUE;
  439. }
  440. }
  441. break;
  442. case IRP_MN_POWER_SEQUENCE:
  443. TRAP; // client-PDO should never get this
  444. status = Irp->IoStatus.Status;
  445. break;
  446. case IRP_MN_QUERY_POWER:
  447. /*
  448. * We allow all power transitions.
  449. * But make sure that there's no WW down that shouldn't be.
  450. */
  451. DBGVERBOSE(("Query power"));
  452. status = HidpCheckIdleState(HidDeviceExtension, Irp);
  453. if (status != STATUS_SUCCESS) {
  454. justReturnPending = TRUE;
  455. }
  456. break;
  457. default:
  458. /*
  459. * 'fail' the Irp by returning the default status.
  460. * Do not return STATUS_NOT_SUPPORTED;
  461. * keep the default status
  462. * (this allows filter drivers to work).
  463. */
  464. status = Irp->IoStatus.Status;
  465. break;
  466. }
  467. } else {
  468. switch (minorFunction){
  469. case IRP_MN_SET_POWER:
  470. case IRP_MN_QUERY_POWER:
  471. status = STATUS_SUCCESS;
  472. break;
  473. default:
  474. status = Irp->IoStatus.Status;
  475. break;
  476. }
  477. }
  478. if (!justReturnPending) {
  479. /*
  480. * Whether we are completing or relaying this power IRP,
  481. * we must call PoStartNextPowerIrp on Windows NT.
  482. */
  483. PoStartNextPowerIrp(Irp);
  484. ASSERT(status != NO_STATUS);
  485. Irp->IoStatus.Status = status;
  486. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  487. }
  488. DBGSUCCESS(status, FALSE)
  489. return status;
  490. }
  491. NTSTATUS
  492. HidpFdoPower(
  493. IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension,
  494. IN OUT PIRP Irp
  495. )
  496. {
  497. NTSTATUS status = NO_STATUS;
  498. PIO_STACK_LOCATION irpSp;
  499. FDO_EXTENSION *fdoExt;
  500. KIRQL oldIrql;
  501. BOOLEAN completeIrpHere = FALSE;
  502. BOOLEAN returnPending = FALSE;
  503. UCHAR minorFunction;
  504. SYSTEM_POWER_STATE systemState;
  505. BOOLEAN runPowerCode;
  506. irpSp = IoGetCurrentIrpStackLocation(Irp);
  507. /*
  508. * Keep these privately so we still have it after the IRP completes
  509. * or after the device extension is freed on a REMOVE_DEVICE
  510. */
  511. minorFunction = irpSp->MinorFunction;
  512. fdoExt = &HidDeviceExtension->fdoExt;
  513. runPowerCode =
  514. (fdoExt->state == DEVICE_STATE_START_SUCCESS) ||
  515. (fdoExt->state == DEVICE_STATE_STOPPING) ||
  516. (fdoExt->state == DEVICE_STATE_STOPPED);
  517. if (runPowerCode) {
  518. switch (minorFunction){
  519. case IRP_MN_SET_POWER:
  520. PoSetPowerState(fdoExt->fdo,
  521. irpSp->Parameters.Power.Type,
  522. irpSp->Parameters.Power.State);
  523. switch (irpSp->Parameters.Power.Type) {
  524. case SystemPowerState:
  525. systemState = irpSp->Parameters.Power.State.SystemState;
  526. if (systemState < PowerSystemMaximum) {
  527. /*
  528. * For the 'regular' system power states,
  529. * we convert to a device power state
  530. * and request a callback with the device power state.
  531. */
  532. PDEVICE_OBJECT pdo = HidDeviceExtension->hidExt.PhysicalDeviceObject;
  533. POWER_STATE powerState;
  534. KIRQL oldIrql;
  535. BOOLEAN isWaitWakePending;
  536. if (systemState != PowerSystemWorking) {
  537. //
  538. // We don't want to be idling during regular system
  539. // power stuff.
  540. //
  541. HidpCancelIdleNotification(fdoExt, FALSE);
  542. }
  543. fdoExt->systemPowerState = systemState;
  544. isWaitWakePending = HidpIsWaitWakePending(fdoExt, FALSE);
  545. if (isWaitWakePending &&
  546. systemState > fdoExt->deviceCapabilities.SystemWake){
  547. /*
  548. * We're transitioning to a system state from which
  549. * this device cannot perform a wake-up.
  550. * So fail all the WaitWake IRPs.
  551. */
  552. CompleteAllCollectionWaitWakeIrps(fdoExt, STATUS_POWER_STATE_INVALID);
  553. }
  554. returnPending = TRUE;
  555. }
  556. else {
  557. TRAP;
  558. /*
  559. * For the remaining system power states,
  560. * just pass down the IRP.
  561. */
  562. runPowerCode = FALSE;
  563. Irp->IoStatus.Status = STATUS_SUCCESS;
  564. }
  565. break;
  566. case DevicePowerState:
  567. switch (irpSp->Parameters.Power.State.DeviceState) {
  568. case PowerDeviceD0:
  569. /*
  570. * Resume from APM Suspend
  571. *
  572. * Do nothing here; Send down the read IRPs in the
  573. * completion routine for this (the power) IRP.
  574. */
  575. DBGVERBOSE(("fdo powering up to D0\n"));
  576. break;
  577. case PowerDeviceD1:
  578. case PowerDeviceD2:
  579. case PowerDeviceD3:
  580. /*
  581. * Suspend
  582. */
  583. DBGVERBOSE(("fdo going down to D%d\n", fdoExt->devicePowerState-1));
  584. if (fdoExt->state == DEVICE_STATE_START_SUCCESS &&
  585. fdoExt->devicePowerState == PowerDeviceD0){
  586. CancelAllPingPongIrps(fdoExt);
  587. }
  588. fdoExt->devicePowerState =
  589. irpSp->Parameters.Power.State.DeviceState;
  590. break;
  591. }
  592. break;
  593. }
  594. break;
  595. case IRP_MN_WAIT_WAKE:
  596. KeAcquireSpinLock(&fdoExt->waitWakeSpinLock, &oldIrql);
  597. if (fdoExt->waitWakeIrp == BAD_POINTER) {
  598. DBGVERBOSE(("new wait wake irp 0x%x\n", Irp));
  599. fdoExt->waitWakeIrp = Irp;
  600. } else {
  601. DBGVERBOSE(("1+ wait wake irps 0x%x\n", Irp));
  602. completeIrpHere = TRUE;
  603. status = STATUS_POWER_STATE_INVALID;
  604. }
  605. KeReleaseSpinLock(&fdoExt->waitWakeSpinLock, oldIrql);
  606. break;
  607. }
  608. } else {
  609. switch (minorFunction){
  610. case IRP_MN_SET_POWER:
  611. case IRP_MN_QUERY_POWER:
  612. Irp->IoStatus.Status = STATUS_SUCCESS;
  613. break;
  614. default:
  615. // nothing
  616. break;
  617. }
  618. }
  619. /*
  620. * Whether we are completing or relaying this power IRP,
  621. * we must call PoStartNextPowerIrp on Windows NT.
  622. */
  623. PoStartNextPowerIrp(Irp);
  624. /*
  625. * If this is a call for a collection-PDO, we complete it ourselves here.
  626. * Otherwise, we pass it to the minidriver stack for more processing.
  627. */
  628. if (completeIrpHere){
  629. /*
  630. * Note: Don't touch the Irp after completing it.
  631. */
  632. ASSERT(status != NO_STATUS);
  633. Irp->IoStatus.Status = status;
  634. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  635. }
  636. else {
  637. /*
  638. * Call the minidriver with this Irp.
  639. * The rest of our processing will be done in our completion routine.
  640. *
  641. * Note: Don't touch the Irp after sending it down, since it may
  642. * be completed immediately.
  643. */
  644. if (runPowerCode) {
  645. IoCopyCurrentIrpStackLocationToNext(Irp);
  646. IoSetCompletionRoutine(Irp, HidpFdoPowerCompletion, (PVOID)HidDeviceExtension, TRUE, TRUE, TRUE);
  647. } else {
  648. IoSkipCurrentIrpStackLocation(Irp);
  649. }
  650. /*
  651. *
  652. * Want to use PoCallDriver here, but PoCallDriver
  653. * uses IoCallDriver,
  654. * which uses the driverObject->MajorFunction[] array
  655. * instead of the hidDriverExtension->MajorFunction[] functions.
  656. * SHOULD FIX THIS FOR NT -- should use PoCallDriver
  657. *
  658. */
  659. // status = PoCallDriver(HidDeviceExtension->hidExt.NextDeviceObject, Irp);
  660. // status = PoCallDriver(fdoExt->fdo, Irp);
  661. if (returnPending) {
  662. DBGASSERT(runPowerCode, ("We are returning pending, but not running completion routine.\n"), TRUE)
  663. IoMarkIrpPending(Irp);
  664. HidpCallDriver(fdoExt->fdo, Irp);
  665. status = STATUS_PENDING;
  666. } else {
  667. status = HidpCallDriver(fdoExt->fdo, Irp);
  668. }
  669. }
  670. DBGSUCCESS(status, FALSE)
  671. return status;
  672. }
  673. /*
  674. ********************************************************************************
  675. * HidpIrpMajorPower
  676. ********************************************************************************
  677. *
  678. *
  679. * Note: This function cannot be pageable because (on Win98 anyway)
  680. * NTKERN calls it back on the thread of the completion routine
  681. * that returns the "Cntrl-Alt-Del" keystrokes.
  682. * Also, we may or may not have set the DO_POWER_PAGABLE;
  683. * so power IRPs may or may not come in at DISPATCH_LEVEL.
  684. * So we must keep this code locked.
  685. */
  686. NTSTATUS HidpIrpMajorPower(
  687. IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension,
  688. IN OUT PIRP Irp
  689. )
  690. {
  691. PIO_STACK_LOCATION irpSp;
  692. BOOLEAN isClientPdo;
  693. NTSTATUS status;
  694. UCHAR minorFunction;
  695. irpSp = IoGetCurrentIrpStackLocation(Irp);
  696. minorFunction = irpSp->MinorFunction;
  697. isClientPdo = HidDeviceExtension->isClientPdo;
  698. if (minorFunction != IRP_MN_SET_POWER){
  699. DBG_LOG_POWER_IRP(HidDeviceExtension, minorFunction, isClientPdo, FALSE, "", -1, -1)
  700. } else {
  701. switch (irpSp->Parameters.Power.Type) {
  702. case SystemPowerState:
  703. DBG_LOG_POWER_IRP(HidDeviceExtension, minorFunction, isClientPdo, FALSE, "SystemState", irpSp->Parameters.Power.State.SystemState, 0xffffffff);
  704. case DevicePowerState:
  705. DBG_LOG_POWER_IRP(HidDeviceExtension, minorFunction, isClientPdo, FALSE, "DeviceState", irpSp->Parameters.Power.State.DeviceState, 0xffffffff);
  706. }
  707. }
  708. if (isClientPdo){
  709. status = HidpPdoPower(HidDeviceExtension, Irp);
  710. } else {
  711. status = HidpFdoPower(HidDeviceExtension, Irp);
  712. }
  713. DBG_LOG_POWER_IRP(HidDeviceExtension, minorFunction, isClientPdo, TRUE, "", -1, status)
  714. return status;
  715. }
  716. /*
  717. ********************************************************************************
  718. * SubmitWaitWakeIrp
  719. ********************************************************************************
  720. *
  721. *
  722. */
  723. NTSTATUS SubmitWaitWakeIrp(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension)
  724. {
  725. NTSTATUS status;
  726. POWER_STATE powerState;
  727. FDO_EXTENSION *fdoExt;
  728. ASSERT(!HidDeviceExtension->isClientPdo);
  729. fdoExt = &HidDeviceExtension->fdoExt;
  730. powerState.SystemState = fdoExt->deviceCapabilities.SystemWake;
  731. DBGVERBOSE(("SystemWake=%x, submitting waitwake irp.", fdoExt->deviceCapabilities.SystemWake))
  732. status = PoRequestPowerIrp( HidDeviceExtension->hidExt.PhysicalDeviceObject,
  733. IRP_MN_WAIT_WAKE,
  734. powerState,
  735. HidpWaitWakeComplete,
  736. HidDeviceExtension, // context
  737. NULL);
  738. // if (status != STATUS_PENDING){
  739. // fdoExt->waitWakeIrp = BAD_POINTER;
  740. // }
  741. DBGASSERT((status == STATUS_PENDING),
  742. ("Expected STATUS_PENDING when submitting WW, got %x", status),
  743. TRUE)
  744. return status;
  745. }
  746. /*
  747. ********************************************************************************
  748. * HidpFdoPowerCompletion
  749. ********************************************************************************
  750. *
  751. *
  752. */
  753. NTSTATUS HidpFdoPowerCompletion(
  754. IN PDEVICE_OBJECT DeviceObject,
  755. IN PIRP Irp,
  756. IN PVOID Context
  757. )
  758. {
  759. PIO_STACK_LOCATION irpSp;
  760. FDO_EXTENSION *fdoExt;
  761. NTSTATUS status = Irp->IoStatus.Status;
  762. PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)Context;
  763. SYSTEM_POWER_STATE systemState;
  764. if (Irp->PendingReturned) {
  765. IoMarkIrpPending(Irp);
  766. }
  767. ASSERT(ISPTR(HidDeviceExtension));
  768. if (HidDeviceExtension->isClientPdo){
  769. fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
  770. }
  771. else {
  772. fdoExt = &HidDeviceExtension->fdoExt;
  773. }
  774. irpSp = IoGetCurrentIrpStackLocation(Irp);
  775. ASSERT(irpSp->MajorFunction == IRP_MJ_POWER);
  776. if (NT_SUCCESS(status)) {
  777. switch (irpSp->MinorFunction) {
  778. case IRP_MN_SET_POWER:
  779. switch (irpSp->Parameters.Power.Type) {
  780. case DevicePowerState:
  781. switch (irpSp->Parameters.Power.State.DeviceState){
  782. case PowerDeviceD0:
  783. if (fdoExt->devicePowerState != PowerDeviceD0) {
  784. KIRQL irql;
  785. LONG prevIdleState;
  786. fdoExt->devicePowerState = irpSp->Parameters.Power.State.DeviceState;
  787. ASSERT(!HidDeviceExtension->isClientPdo);
  788. //
  789. // Reset the idle stuff if it's not disabled.
  790. //
  791. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  792. if (fdoExt->idleState != IdleDisabled) {
  793. prevIdleState = InterlockedExchange(&fdoExt->idleState, IdleWaiting);
  794. DBGASSERT(prevIdleState == IdleComplete,
  795. ("Previous idle state while completing actually %x",
  796. prevIdleState),
  797. TRUE);
  798. fdoExt->idleCancelling = FALSE;
  799. }
  800. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  801. /*
  802. * On APM resume, restart the ping-pong IRPs
  803. * for interrupt devices.
  804. */
  805. if (!fdoExt->driverExt->DevicesArePolled &&
  806. !fdoExt->isOutputOnlyDevice) {
  807. NTSTATUS ntStatus = HidpStartAllPingPongs(fdoExt);
  808. if (!NT_SUCCESS(ntStatus)) {
  809. fdoExt->state = DEVICE_STATE_START_FAILURE;
  810. }
  811. }
  812. }
  813. break;
  814. }
  815. break;
  816. case SystemPowerState:
  817. ASSERT (!HidDeviceExtension->isClientPdo);
  818. systemState = irpSp->Parameters.Power.State.SystemState;
  819. ASSERT((ULONG)systemState < PowerSystemMaximum);
  820. if (systemState < PowerSystemMaximum){
  821. /*
  822. * For the 'regular' system power states,
  823. * we convert to a device power state
  824. * and request a callback with the device power state.
  825. */
  826. PDEVICE_OBJECT pdo = HidDeviceExtension->hidExt.PhysicalDeviceObject;
  827. POWER_STATE powerState;
  828. KIRQL oldIrql;
  829. BOOLEAN isWaitWakePending;
  830. fdoExt->systemPowerState = systemState;
  831. isWaitWakePending = HidpIsWaitWakePending(fdoExt, FALSE);
  832. if (isWaitWakePending){
  833. if (systemState == PowerSystemWorking){
  834. powerState.DeviceState = PowerDeviceD0;
  835. }
  836. else {
  837. powerState.DeviceState = fdoExt->deviceCapabilities.DeviceState[systemState];
  838. /*
  839. * If the bus does not map the system state to
  840. * a defined device state, request PowerDeviceD3
  841. * and cancel the WaitWake IRP.
  842. */
  843. if (powerState.DeviceState == PowerDeviceUnspecified){
  844. DBGERR(("IRP_MN_SET_POWER: systemState %d mapped not mapped so using device state PowerDeviceD3.", systemState))
  845. powerState.DeviceState = PowerDeviceD3;
  846. }
  847. }
  848. }
  849. else {
  850. /*
  851. * If we don't have a WaitWake IRP pending,
  852. * then every reduced-power system state
  853. * should get mapped to D3.
  854. */
  855. if (systemState == PowerSystemWorking){
  856. powerState.DeviceState = PowerDeviceD0;
  857. }
  858. else {
  859. DBGVERBOSE(("IRP_MN_SET_POWER: no waitWake IRP, so requesting PowerDeviceD3."))
  860. powerState.DeviceState = PowerDeviceD3;
  861. }
  862. }
  863. DBGVERBOSE(("IRP_MN_SET_POWER: mapped systemState %d to device state %d.", systemState, powerState.DeviceState))
  864. IoMarkIrpPending(Irp);
  865. fdoExt->currentSystemStateIrp = Irp;
  866. PoRequestPowerIrp( pdo,
  867. IRP_MN_SET_POWER,
  868. powerState,
  869. DevicePowerRequestCompletion,
  870. fdoExt, // context
  871. NULL);
  872. status = STATUS_MORE_PROCESSING_REQUIRED;
  873. }
  874. else {
  875. TRAP;
  876. /*
  877. * For the remaining system power states,
  878. * just pass down the IRP.
  879. */
  880. }
  881. break;
  882. }
  883. break;
  884. }
  885. }
  886. else if (status == STATUS_CANCELLED){
  887. /*
  888. * Client cancelled the power IRP, probably getting removed.
  889. */
  890. }
  891. else {
  892. DBGWARN(("HidpPowerCompletion: Power IRP %ph (minor function %xh) failed with status %xh.", Irp, irpSp->MinorFunction, Irp->IoStatus.Status))
  893. }
  894. return status;
  895. }
  896. /*
  897. ********************************************************************************
  898. * DevicePowerRequestCompletion
  899. ********************************************************************************
  900. *
  901. * Note: the DeviceObject here is the PDO (e.g. usbhub's PDO), not our FDO,
  902. * so we cannot use its device context.
  903. */
  904. VOID DevicePowerRequestCompletion(
  905. IN PDEVICE_OBJECT DeviceObject,
  906. IN UCHAR MinorFunction,
  907. IN POWER_STATE PowerState,
  908. IN PVOID Context,
  909. IN PIO_STATUS_BLOCK IoStatus
  910. )
  911. {
  912. FDO_EXTENSION *fdoExt = (FDO_EXTENSION *)Context;
  913. PIRP systemStateIrp;
  914. DBG_COMMON_ENTRY()
  915. systemStateIrp = fdoExt->currentSystemStateIrp;
  916. fdoExt->currentSystemStateIrp = BAD_POINTER;
  917. ASSERT(systemStateIrp);
  918. DBGSUCCESS(IoStatus->Status, TRUE)
  919. // systemStateIrp->IoStatus.Status = IoStatus->Status;
  920. PoStartNextPowerIrp(systemStateIrp);
  921. /*
  922. * Complete the system-state IRP.
  923. */
  924. IoCompleteRequest(systemStateIrp, IO_NO_INCREMENT);
  925. if (PowerState.DeviceState == PowerDeviceD0) {
  926. //
  927. // Powering up. Restart the idling.
  928. //
  929. HidpStartIdleTimeout(fdoExt, FALSE);
  930. }
  931. DBG_COMMON_EXIT()
  932. }
  933. /*
  934. ********************************************************************************
  935. * CollectionPowerRequestCompletion
  936. ********************************************************************************
  937. *
  938. *
  939. */
  940. VOID CollectionPowerRequestCompletion(
  941. IN PDEVICE_OBJECT DeviceObject,
  942. IN UCHAR MinorFunction,
  943. IN POWER_STATE PowerState,
  944. IN PVOID Context,
  945. IN PIO_STATUS_BLOCK IoStatus
  946. )
  947. {
  948. PIRP systemStateIrp = (PIRP)Context;
  949. PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension;
  950. PDO_EXTENSION *pdoExt;
  951. IO_STACK_LOCATION *irpSp;
  952. SYSTEM_POWER_STATE systemState;
  953. DBG_COMMON_ENTRY()
  954. hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
  955. pdoExt = &hidDeviceExtension->pdoExt;
  956. ASSERT(systemStateIrp);
  957. /*
  958. * This is the completion routine for the device-state power
  959. * Irp which we've requested. Complete the original system-state
  960. * power Irp with the result of the device-state power Irp.
  961. */
  962. irpSp = IoGetCurrentIrpStackLocation(systemStateIrp);
  963. systemState = irpSp->Parameters.Power.State.SystemState;
  964. systemStateIrp->IoStatus.Status = IoStatus->Status;
  965. PoStartNextPowerIrp(systemStateIrp);
  966. IoCompleteRequest(systemStateIrp, IO_NO_INCREMENT);
  967. //
  968. // If we're powering up, check if we should have a WW irp pending.
  969. //
  970. if (systemState == PowerSystemWorking &&
  971. SHOULD_SEND_WAITWAKE(pdoExt)) {
  972. HidpCreateRemoteWakeIrp(pdoExt);
  973. }
  974. DBG_COMMON_EXIT()
  975. }
  976. /*
  977. ********************************************************************************
  978. * HidpWaitWakePoRequestComplete
  979. ********************************************************************************
  980. *
  981. */
  982. NTSTATUS HidpWaitWakePoRequestComplete(
  983. IN PDEVICE_OBJECT DeviceObject,
  984. IN UCHAR MinorFunction,
  985. IN POWER_STATE PowerState,
  986. IN PVOID Context,
  987. IN PIO_STATUS_BLOCK IoStatus
  988. )
  989. {
  990. PHIDCLASS_DEVICE_EXTENSION hidDevExt = (PHIDCLASS_DEVICE_EXTENSION)Context;
  991. FDO_EXTENSION *fdoExt;
  992. ASSERT(!hidDevExt->isClientPdo);
  993. fdoExt = &hidDevExt->fdoExt;
  994. DBGVERBOSE(("HidpWaitWakePoRequestComplete!, status == %xh", IoStatus->Status))
  995. #if WIN95_BUILD
  996. if (NT_SUCCESS(IoStatus->Status)) {
  997. //
  998. // Resubmit the wait wake irp
  999. //
  1000. HidpPowerUpPdos(fdoExt);
  1001. SubmitWaitWakeIrp(hidDevExt);
  1002. }
  1003. #else
  1004. /*
  1005. * Complete all the collections' WaitWake IRPs with this same status.
  1006. */
  1007. CompleteAllCollectionWaitWakeIrps(fdoExt, IoStatus->Status);
  1008. if (NT_SUCCESS(IoStatus->Status) && fdoExt->idleState != IdleDisabled) {
  1009. HidpPowerUpPdos(fdoExt);
  1010. }
  1011. #endif
  1012. return STATUS_SUCCESS;
  1013. }
  1014. /*
  1015. ********************************************************************************
  1016. * HidpWaitWakeComplete
  1017. ********************************************************************************
  1018. *
  1019. */
  1020. NTSTATUS HidpWaitWakeComplete(
  1021. IN PDEVICE_OBJECT DeviceObject,
  1022. IN UCHAR MinorFunction,
  1023. IN POWER_STATE PowerState,
  1024. IN PVOID Context,
  1025. IN PIO_STATUS_BLOCK IoStatus
  1026. )
  1027. {
  1028. PHIDCLASS_DEVICE_EXTENSION hidDevExt = (PHIDCLASS_DEVICE_EXTENSION)Context;
  1029. FDO_EXTENSION *fdoExt;
  1030. PDO_EXTENSION *pdoExt;
  1031. NTSTATUS status;
  1032. KIRQL oldIrql;
  1033. ASSERT(!hidDevExt->isClientPdo);
  1034. fdoExt = &hidDevExt->fdoExt;
  1035. status = IoStatus->Status;
  1036. DBGVERBOSE(("HidpWaitWakeComplete!, status == %xh", status))
  1037. KeAcquireSpinLock(&fdoExt->waitWakeSpinLock, &oldIrql);
  1038. fdoExt->waitWakeIrp = BAD_POINTER;
  1039. fdoExt->isWaitWakePending = FALSE;
  1040. KeReleaseSpinLock(&fdoExt->waitWakeSpinLock, oldIrql);
  1041. /*
  1042. * Call HidpWaitWakePoRequestComplete (either directly or
  1043. * as a completion routine to the power IRP that we request
  1044. * to wake up the machine); it will complete the clients'
  1045. * WaitWake IRPs with the same status as this device WaitWake IRP.
  1046. */
  1047. PowerState.DeviceState = PowerDeviceD0;
  1048. if (NT_SUCCESS(status)){
  1049. /*
  1050. * Our device is waking up the machine.
  1051. * So request the D0 (working) power state.
  1052. */
  1053. // PowerState is undefined when a wait wake irp is completing
  1054. // ASSERT(PowerState.DeviceState == PowerDeviceD0);
  1055. DBGVERBOSE(("ww irp, requesting D0 on pdo %x\n", DeviceObject))
  1056. PoRequestPowerIrp( DeviceObject,
  1057. IRP_MN_SET_POWER,
  1058. PowerState,
  1059. HidpWaitWakePoRequestComplete,
  1060. Context,
  1061. NULL);
  1062. } else if (status != STATUS_CANCELLED) {
  1063. //
  1064. // If the wait wake failed, then there is no way for us to wake the
  1065. // device when we are in S0. Turn off idle detection.
  1066. //
  1067. // This doesn't need to be guarded by a spin lock because the only
  1068. // places we look at these values is in the power dispatch routine
  1069. // and when an interrupt read completes...
  1070. //
  1071. // 1) no interrupt read will be completing b/c the pingpong engine has
  1072. // been suspended and will not start until we power up the stack
  1073. // 2) I think we are still considered to be handling a power irp. If
  1074. // not, then we need to guard the isIdleTimeoutEnabled field
  1075. //
  1076. // ISSUE! we should also only turn off idle detection if the WW fails in
  1077. // S0. If we hiber, then the WW will fail, but we should not turn off
  1078. // idle detection in this case. I think that checking
  1079. // systemPowerState is not PowerSystemWorking will do the trick,
  1080. // BUT THIS MUST BE CONFIRMED!!!!
  1081. //
  1082. if (fdoExt->idleState != IdleDisabled &&
  1083. fdoExt->systemPowerState == PowerSystemWorking) {
  1084. DBGWARN(("Turning off idle detection due to WW failure, status = %x\n", status))
  1085. ASSERT(ISPTR(fdoExt->idleTimeoutValue));
  1086. //
  1087. // Don't set any state before calling because we may have to power
  1088. // stuff up.
  1089. //
  1090. HidpCancelIdleNotification(fdoExt, FALSE);
  1091. }
  1092. HidpWaitWakePoRequestComplete( DeviceObject,
  1093. MinorFunction,
  1094. PowerState,
  1095. Context,
  1096. IoStatus);
  1097. }
  1098. return STATUS_SUCCESS;
  1099. }
  1100. /*
  1101. ********************************************************************************
  1102. * QueuePowerEventIrp
  1103. ********************************************************************************
  1104. *
  1105. */
  1106. NTSTATUS QueuePowerEventIrp(
  1107. IN PHIDCLASS_COLLECTION hidCollection,
  1108. IN PIRP Irp
  1109. )
  1110. {
  1111. NTSTATUS status;
  1112. KIRQL oldIrql;
  1113. PDRIVER_CANCEL oldCancelRoutine;
  1114. KeAcquireSpinLock(&hidCollection->powerEventSpinLock, &oldIrql);
  1115. /*
  1116. * Must set a cancel routine before checking the Cancel flag.
  1117. */
  1118. oldCancelRoutine = IoSetCancelRoutine(Irp, PowerEventCancelRoutine);
  1119. ASSERT(!oldCancelRoutine);
  1120. if (Irp->Cancel){
  1121. /*
  1122. * This IRP was cancelled. Do not queue it.
  1123. * The calling function will complete the IRP with error.
  1124. */
  1125. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
  1126. if (oldCancelRoutine){
  1127. /*
  1128. * Cancel routine was NOT called.
  1129. * Complete the IRP here.
  1130. */
  1131. ASSERT(oldCancelRoutine == PowerEventCancelRoutine);
  1132. status = STATUS_CANCELLED;
  1133. }
  1134. else {
  1135. /*
  1136. * The cancel routine was called,
  1137. * and it will complete this IRP as soon as we drop the spinlock.
  1138. * Return PENDING so the caller doesn't touch this IRP.
  1139. */
  1140. status = STATUS_PENDING;
  1141. }
  1142. }
  1143. else if (ISPTR(hidCollection->powerEventIrp)){
  1144. /*
  1145. * We already have a power event IRP queued.
  1146. * This shouldn't happen, but we'll handle it.
  1147. */
  1148. DBGWARN(("Already have a power event irp queued."));
  1149. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
  1150. if (oldCancelRoutine){
  1151. /*
  1152. * Cancel routine was NOT called.
  1153. * Complete the IRP here.
  1154. */
  1155. ASSERT(oldCancelRoutine == PowerEventCancelRoutine);
  1156. status = STATUS_UNSUCCESSFUL;
  1157. }
  1158. else {
  1159. /*
  1160. * The irp was cancelled and the cancel routine was called;
  1161. * it will complete this IRP as soon as we drop the spinlock.
  1162. * Return PENDING so the caller doesn't touch this IRP.
  1163. */
  1164. ASSERT(Irp->Cancel);
  1165. status = STATUS_PENDING;
  1166. }
  1167. }
  1168. else {
  1169. /*
  1170. * Save a pointer to this power event IRP and return PENDING.
  1171. * This qualifies as "queuing" the IRP, so we must have
  1172. * a cancel routine.
  1173. */
  1174. hidCollection->powerEventIrp = Irp;
  1175. IoMarkIrpPending(Irp);
  1176. status = STATUS_PENDING;
  1177. }
  1178. KeReleaseSpinLock(&hidCollection->powerEventSpinLock, oldIrql);
  1179. return status;
  1180. }
  1181. /*
  1182. ********************************************************************************
  1183. * PowerEventCancelRoutine
  1184. ********************************************************************************
  1185. *
  1186. */
  1187. VOID PowerEventCancelRoutine(
  1188. IN PDEVICE_OBJECT DeviceObject,
  1189. IN PIRP Irp
  1190. )
  1191. {
  1192. PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
  1193. FDO_EXTENSION *fdoExt;
  1194. PHIDCLASS_COLLECTION hidCollection;
  1195. ULONG collectionIndex;
  1196. KIRQL oldIrql;
  1197. ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG);
  1198. ASSERT(hidDeviceExtension->isClientPdo);
  1199. fdoExt = &hidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
  1200. collectionIndex = hidDeviceExtension->pdoExt.collectionIndex;
  1201. hidCollection = &fdoExt->classCollectionArray[collectionIndex];
  1202. KeAcquireSpinLock(&hidCollection->powerEventSpinLock, &oldIrql);
  1203. ASSERT(Irp == hidCollection->powerEventIrp);
  1204. hidCollection->powerEventIrp = BAD_POINTER;
  1205. KeReleaseSpinLock(&hidCollection->powerEventSpinLock, oldIrql);
  1206. IoReleaseCancelSpinLock(Irp->CancelIrql);
  1207. Irp->IoStatus.Status = STATUS_CANCELLED;
  1208. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  1209. }
  1210. /*
  1211. ********************************************************************************
  1212. * CollectionWaitWakeIrpCancelRoutine
  1213. ********************************************************************************
  1214. *
  1215. */
  1216. VOID CollectionWaitWakeIrpCancelRoutine(
  1217. IN PDEVICE_OBJECT DeviceObject,
  1218. IN PIRP Irp
  1219. )
  1220. {
  1221. PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
  1222. KIRQL oldIrql, oldIrql2;
  1223. PIRP deviceWaitWakeIrpToCancel = NULL;
  1224. FDO_EXTENSION *fdoExt;
  1225. PDO_EXTENSION *pdoExt;
  1226. ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG);
  1227. ASSERT(hidDeviceExtension->isClientPdo);
  1228. pdoExt = &hidDeviceExtension->pdoExt;
  1229. fdoExt = &pdoExt->deviceFdoExt->fdoExt;
  1230. KeAcquireSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, &oldIrql);
  1231. /*
  1232. * Dequeue the client's WaitWake IRP.
  1233. */
  1234. RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
  1235. InterlockedExchangePointer(&pdoExt->waitWakeIrp, NULL);
  1236. /*
  1237. * If the last collection WaitWake IRP just got cancelled,
  1238. * cancel our WaitWake IRP as well.
  1239. *
  1240. * NOTE: we only cancel the FDO wait wake irp if we are not doing idle
  1241. * detection, otherwise, there would be no way for the device to
  1242. * wake up when we put it into low power
  1243. *
  1244. */
  1245. KeAcquireSpinLock(&fdoExt->waitWakeSpinLock, &oldIrql2);
  1246. if (IsListEmpty(&fdoExt->collectionWaitWakeIrpQueue) &&
  1247. fdoExt->isWaitWakePending &&
  1248. fdoExt->idleState == IdleDisabled){
  1249. ASSERT(ISPTR(fdoExt->waitWakeIrp));
  1250. deviceWaitWakeIrpToCancel = fdoExt->waitWakeIrp;
  1251. fdoExt->waitWakeIrp = BAD_POINTER;
  1252. fdoExt->isWaitWakePending = FALSE;
  1253. }
  1254. KeReleaseSpinLock(&fdoExt->waitWakeSpinLock, oldIrql2);
  1255. KeReleaseSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, oldIrql);
  1256. IoReleaseCancelSpinLock(Irp->CancelIrql);
  1257. /*
  1258. * Complete the cancelled IRP only if it was in the list.
  1259. */
  1260. Irp->IoStatus.Status = STATUS_CANCELLED;
  1261. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  1262. if (ISPTR(deviceWaitWakeIrpToCancel)){
  1263. IoCancelIrp(deviceWaitWakeIrpToCancel);
  1264. }
  1265. }
  1266. /*
  1267. ********************************************************************************
  1268. * CompleteAllCollectionWaitWakeIrps
  1269. ********************************************************************************
  1270. *
  1271. * Note: this function cannot be pageable because it is called
  1272. * from a completion routine.
  1273. */
  1274. VOID CompleteAllCollectionWaitWakeIrps(
  1275. IN FDO_EXTENSION *fdoExt,
  1276. IN NTSTATUS status
  1277. )
  1278. {
  1279. LIST_ENTRY irpsToComplete;
  1280. KIRQL oldIrql;
  1281. PLIST_ENTRY listEntry;
  1282. PIRP irp;
  1283. PDO_EXTENSION *pdoExt;
  1284. PIO_STACK_LOCATION irpSp;
  1285. InitializeListHead(&irpsToComplete);
  1286. KeAcquireSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, &oldIrql);
  1287. while (!IsListEmpty(&fdoExt->collectionWaitWakeIrpQueue)){
  1288. PDRIVER_CANCEL oldCancelRoutine;
  1289. listEntry = RemoveHeadList(&fdoExt->collectionWaitWakeIrpQueue);
  1290. InitializeListHead(listEntry); // in case cancel routine tries to dequeue again
  1291. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  1292. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  1293. if (oldCancelRoutine){
  1294. ASSERT(oldCancelRoutine == CollectionWaitWakeIrpCancelRoutine);
  1295. /*
  1296. * We can't complete an IRP while holding a spinlock.
  1297. * Also, we don't want to complete a WaitWake IRP while
  1298. * still processing collectionWaitWakeIrpQueue because a driver
  1299. * may resend an IRP on the same thread, causing us to loop forever.
  1300. * So just move the IRPs to a private queue and we'll complete them later.
  1301. */
  1302. InsertTailList(&irpsToComplete, listEntry);
  1303. irpSp = IoGetCurrentIrpStackLocation(irp);
  1304. pdoExt = &((PHIDCLASS_DEVICE_EXTENSION)irpSp->DeviceObject->DeviceExtension)->pdoExt;
  1305. InterlockedExchangePointer(&pdoExt->waitWakeIrp, NULL);
  1306. }
  1307. else {
  1308. /*
  1309. * This IRP was cancelled and the cancel routine WAS called.
  1310. * The cancel routine will complete the IRP as soon as we drop the spinlock.
  1311. * So don't touch the IRP.
  1312. */
  1313. ASSERT(irp->Cancel);
  1314. }
  1315. }
  1316. KeReleaseSpinLock(&fdoExt->collectionWaitWakeIrpQueueSpinLock, oldIrql);
  1317. while (!IsListEmpty(&irpsToComplete)){
  1318. listEntry = RemoveHeadList(&irpsToComplete);
  1319. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  1320. irp->IoStatus.Status = status;
  1321. IoCompleteRequest(irp, IO_NO_INCREMENT);
  1322. }
  1323. }
  1324. VOID PowerDelayedCancelRoutine(
  1325. IN PDEVICE_OBJECT DeviceObject,
  1326. IN PIRP Irp
  1327. )
  1328. {
  1329. PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
  1330. FDO_EXTENSION *fdoExt;
  1331. KIRQL oldIrql;
  1332. IoReleaseCancelSpinLock(Irp->CancelIrql);
  1333. ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG);
  1334. ASSERT(hidDeviceExtension->isClientPdo);
  1335. fdoExt = &hidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
  1336. KeAcquireSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, &oldIrql);
  1337. RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
  1338. ASSERT(Irp->Tail.Overlay.DriverContext[0] == (PVOID) hidDeviceExtension);
  1339. Irp->Tail.Overlay.DriverContext[0] = NULL;
  1340. ASSERT(fdoExt->numPendingPowerDelayedIrps > 0);
  1341. fdoExt->numPendingPowerDelayedIrps--;
  1342. KeReleaseSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, oldIrql);
  1343. Irp->IoStatus.Status = STATUS_CANCELLED;
  1344. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  1345. }
  1346. NTSTATUS HidpDelayedPowerPoRequestComplete(
  1347. IN PDEVICE_OBJECT DeviceObject,
  1348. IN UCHAR MinorFunction,
  1349. IN POWER_STATE PowerState,
  1350. IN PVOID Context,
  1351. IN PIO_STATUS_BLOCK IoStatus)
  1352. {
  1353. KIRQL irql;
  1354. LONG prevIdleState;
  1355. PFDO_EXTENSION fdoExt = (PFDO_EXTENSION) Context;
  1356. DBGINFO(("powering up all pdos due to delayed request, 0x%x\n", IoStatus->Status))
  1357. DBGVERBOSE(("HidpDelayedPowerPoRequestComplete!, status == %xh", IoStatus->Status))
  1358. if (NT_SUCCESS(IoStatus->Status)) {
  1359. HidpPowerUpPdos(fdoExt);
  1360. } else {
  1361. //
  1362. // All bets are off.
  1363. //
  1364. KeAcquireSpinLock(&fdoExt->idleNotificationSpinLock, &irql);
  1365. prevIdleState = InterlockedExchange(&fdoExt->idleState, IdleDisabled);
  1366. fdoExt->idleCancelling = FALSE;
  1367. KeReleaseSpinLock(&fdoExt->idleNotificationSpinLock, irql);
  1368. KeSetEvent(&fdoExt->idleDoneEvent, 0, FALSE);
  1369. }
  1370. return STATUS_SUCCESS;
  1371. }
  1372. NTSTATUS
  1373. EnqueuePowerDelayedIrp(
  1374. IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension,
  1375. IN PIRP Irp
  1376. )
  1377. {
  1378. FDO_EXTENSION *fdoExt;
  1379. NTSTATUS status;
  1380. KIRQL oldIrql;
  1381. PDRIVER_CANCEL oldCancelRoutine;
  1382. ASSERT(HidDeviceExtension->isClientPdo);
  1383. fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
  1384. DBGINFO(("enqueuing irp %x (mj %x, mn %x)\n", Irp,
  1385. (ULONG) IoGetCurrentIrpStackLocation(Irp)->MajorFunction,
  1386. (ULONG) IoGetCurrentIrpStackLocation(Irp)->MinorFunction))
  1387. KeAcquireSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, &oldIrql);
  1388. /*
  1389. * Must set a cancel routine before
  1390. * checking the Cancel flag.
  1391. */
  1392. oldCancelRoutine = IoSetCancelRoutine(Irp, PowerDelayedCancelRoutine);
  1393. ASSERT(!oldCancelRoutine);
  1394. /*
  1395. * Make sure this Irp wasn't just cancelled.
  1396. * Note that there is NO RACE CONDITION here
  1397. * because we are holding the fileExtension lock.
  1398. */
  1399. if (Irp->Cancel){
  1400. /*
  1401. * This IRP was cancelled.
  1402. */
  1403. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
  1404. if (oldCancelRoutine){
  1405. /*
  1406. * The cancel routine was NOT called.
  1407. * Return error so that caller completes the IRP.
  1408. */
  1409. ASSERT(oldCancelRoutine == PowerDelayedCancelRoutine);
  1410. status = STATUS_CANCELLED;
  1411. }
  1412. else {
  1413. /*
  1414. * The cancel routine was called.
  1415. * As soon as we drop the spinlock it will dequeue
  1416. * and complete the IRP.
  1417. * Initialize the IRP's listEntry so that the dequeue
  1418. * doesn't cause corruption.
  1419. * Then don't touch the irp.
  1420. */
  1421. InitializeListHead(&Irp->Tail.Overlay.ListEntry);
  1422. fdoExt->numPendingPowerDelayedIrps++; // because cancel routine will decrement
  1423. //
  1424. // We assert that this value is set in the cancel routine
  1425. //
  1426. Irp->Tail.Overlay.DriverContext[0] = (PVOID) HidDeviceExtension;
  1427. IoMarkIrpPending(Irp);
  1428. status = Irp->IoStatus.Status = STATUS_PENDING;
  1429. }
  1430. }
  1431. else {
  1432. /*
  1433. * Queue this irp onto the fdo's power delayed queue
  1434. */
  1435. InsertTailList(&fdoExt->collectionPowerDelayedIrpQueue,
  1436. &Irp->Tail.Overlay.ListEntry);
  1437. fdoExt->numPendingPowerDelayedIrps++;
  1438. Irp->Tail.Overlay.DriverContext[0] = (PVOID) HidDeviceExtension;
  1439. IoMarkIrpPending(Irp);
  1440. status = Irp->IoStatus.Status = STATUS_PENDING;
  1441. }
  1442. KeReleaseSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, oldIrql);
  1443. return status;
  1444. }
  1445. PIRP DequeuePowerDelayedIrp(FDO_EXTENSION *fdoExt)
  1446. {
  1447. KIRQL oldIrql;
  1448. PIRP irp = NULL;
  1449. KeAcquireSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, &oldIrql);
  1450. while (!irp && !IsListEmpty(&fdoExt->collectionPowerDelayedIrpQueue)){
  1451. PDRIVER_CANCEL oldCancelRoutine;
  1452. PLIST_ENTRY listEntry = RemoveHeadList(&fdoExt->collectionPowerDelayedIrpQueue);
  1453. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  1454. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  1455. if (oldCancelRoutine){
  1456. ASSERT(oldCancelRoutine == PowerDelayedCancelRoutine);
  1457. ASSERT(fdoExt->numPendingPowerDelayedIrps > 0);
  1458. fdoExt->numPendingPowerDelayedIrps--;
  1459. }
  1460. else {
  1461. /*
  1462. * IRP was cancelled and cancel routine was called.
  1463. * As soon as we drop the spinlock,
  1464. * the cancel routine will dequeue and complete this IRP.
  1465. * Initialize the IRP's listEntry so that the dequeue doesn't cause corruption.
  1466. * Then, don't touch the IRP.
  1467. */
  1468. ASSERT(irp->Cancel);
  1469. InitializeListHead(&irp->Tail.Overlay.ListEntry);
  1470. irp = NULL;
  1471. }
  1472. }
  1473. KeReleaseSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, oldIrql);
  1474. return irp;
  1475. }
  1476. ULONG DequeueAllPdoPowerDelayedIrps(
  1477. PDO_EXTENSION *pdoExt,
  1478. PLIST_ENTRY dequeue
  1479. )
  1480. {
  1481. PDRIVER_CANCEL oldCancelRoutine;
  1482. FDO_EXTENSION *fdoExt;
  1483. PDO_EXTENSION *irpPdoExt;
  1484. PLIST_ENTRY entry;
  1485. KIRQL oldIrql;
  1486. PIRP irp;
  1487. ULONG count = 0;
  1488. InitializeListHead(dequeue);
  1489. fdoExt = &pdoExt->deviceFdoExt->fdoExt;
  1490. KeAcquireSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, &oldIrql);
  1491. for (entry = fdoExt->collectionPowerDelayedIrpQueue.Flink;
  1492. entry != &fdoExt->collectionPowerDelayedIrpQueue;
  1493. ) {
  1494. irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
  1495. irpPdoExt =
  1496. &((PHIDCLASS_DEVICE_EXTENSION) irp->Tail.Overlay.DriverContext[0])->pdoExt;
  1497. entry = entry->Flink;
  1498. if (irpPdoExt == pdoExt) {
  1499. //
  1500. // Remove the entry from the linked list and then either queue it
  1501. // in the dequeue or init the entry so it is valid for the cancel
  1502. // routine
  1503. //
  1504. RemoveEntryList(&irp->Tail.Overlay.ListEntry);
  1505. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  1506. if (oldCancelRoutine != NULL) {
  1507. InsertTailList(dequeue, &irp->Tail.Overlay.ListEntry);
  1508. fdoExt->numPendingPowerDelayedIrps--;
  1509. count++;
  1510. }
  1511. else {
  1512. /*
  1513. * This IRP was cancelled and the cancel routine WAS called.
  1514. * The cancel routine will complete the IRP as soon as we drop the spinlock.
  1515. * So don't touch the IRP.
  1516. */
  1517. ASSERT(irp->Cancel);
  1518. InitializeListHead(&irp->Tail.Overlay.ListEntry); // in case cancel routine tries to dequeue again
  1519. }
  1520. }
  1521. }
  1522. KeReleaseSpinLock(&fdoExt->collectionPowerDelayedIrpQueueSpinLock, oldIrql);
  1523. return count;
  1524. }