Leaked source code of windows server 2003
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.

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