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.

3807 lines
115 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. HUBPWR.C
  5. Abstract:
  6. This module contains functions to handle power irps
  7. to the hub PDOs and FDOs.
  8. Author:
  9. jdunn
  10. Environment:
  11. kernel mode only
  12. Notes:
  13. Revision History:
  14. 7-1-97 : created
  15. --*/
  16. #include <wdm.h>
  17. #ifdef WMI_SUPPORT
  18. #include <wmilib.h>
  19. #endif /* WMI_SUPPORT */
  20. #include "usbhub.h"
  21. #ifdef PAGE_CODE
  22. #ifdef ALLOC_PRAGMA
  23. #pragma alloc_text(PAGE, USBH_SetPowerD0)
  24. #pragma alloc_text(PAGE, USBH_SetPowerD1orD2)
  25. #pragma alloc_text(PAGE, USBH_PdoSetPower)
  26. #pragma alloc_text(PAGE, USBH_PdoPower)
  27. #pragma alloc_text(PAGE, USBH_IdleCompletePowerHubWorker)
  28. #pragma alloc_text(PAGE, USBH_CompletePortIdleIrpsWorker)
  29. #pragma alloc_text(PAGE, USBH_CompletePortWakeIrpsWorker)
  30. #pragma alloc_text(PAGE, USBH_HubAsyncPowerWorker)
  31. #pragma alloc_text(PAGE, USBH_IdleCancelPowerHubWorker)
  32. #endif
  33. #endif
  34. VOID
  35. USBH_CompletePowerIrp(
  36. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  37. IN PIRP Irp,
  38. IN NTSTATUS NtStatus)
  39. /* ++
  40. *
  41. * Description:
  42. *
  43. * This function complete the specified Irp with no priority boost. It also
  44. * sets up the IoStatusBlock.
  45. *
  46. * Arguments:
  47. *
  48. * Irp - the Irp to be completed by us NtStatus - the status code we want to
  49. * return
  50. *
  51. * Return:
  52. *
  53. * None
  54. *
  55. * -- */
  56. {
  57. Irp->IoStatus.Status = NtStatus;
  58. PoStartNextPowerIrp(Irp);
  59. USBH_DEC_PENDING_IO_COUNT(DeviceExtensionHub);
  60. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  61. return;
  62. }
  63. NTSTATUS
  64. USBH_SetPowerD3(
  65. IN PIRP Irp,
  66. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort
  67. )
  68. /*++
  69. Routine Description:
  70. Put the PDO in D3
  71. Arguments:
  72. DeviceExtensionPort - port PDO deviceExtension
  73. Irp - Power Irp.
  74. Return Value:
  75. The function value is the final status from the operation.
  76. --*/
  77. {
  78. NTSTATUS ntStatus = STATUS_SUCCESS;
  79. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  80. USHORT portNumber;
  81. KIRQL irql;
  82. PIRP hubWaitWake = NULL;
  83. LONG pendingPortWWs;
  84. PIRP idleIrp = NULL;
  85. PIRP waitWakeIrp = NULL;
  86. USBH_KdPrint((2,"'PdoSetPower D3\n"));
  87. deviceExtensionHub = DeviceExtensionPort->DeviceExtensionHub;
  88. portNumber = DeviceExtensionPort->PortNumber;
  89. LOGENTRY(LOG_PNP, "spD3", deviceExtensionHub, DeviceExtensionPort->DeviceState, 0);
  90. if (DeviceExtensionPort->DeviceState == PowerDeviceD3) {
  91. // already in D3
  92. USBH_KdPrint((0,"'PDO is already in D3\n"));
  93. ntStatus = STATUS_SUCCESS;
  94. goto USBH_SetPowerD3_Done;
  95. }
  96. //
  97. // Keep track of what PNP thinks is the current power state of the
  98. // port is. Do this now so that we will refuse another WW IRP that may be
  99. // posted after the cancel below.
  100. //
  101. DeviceExtensionPort->DeviceState = PowerDeviceD3;
  102. //
  103. // kill our wait wake irp
  104. //
  105. // we take the cancel spinlock here to ensure our cancel routine does
  106. // not complete the irp for us.
  107. //
  108. IoAcquireCancelSpinLock(&irql);
  109. if (DeviceExtensionPort->IdleNotificationIrp) {
  110. idleIrp = DeviceExtensionPort->IdleNotificationIrp;
  111. DeviceExtensionPort->IdleNotificationIrp = NULL;
  112. DeviceExtensionPort->PortPdoFlags &= ~PORTPDO_IDLE_NOTIFIED;
  113. IoSetCancelRoutine(idleIrp, NULL);
  114. LOGENTRY(LOG_PNP, "IdlX", deviceExtensionHub, DeviceExtensionPort, idleIrp);
  115. USBH_KdPrint((1,"'PDO %x going to D3, failing idle notification request IRP %x\n",
  116. DeviceExtensionPort->PortPhysicalDeviceObject, idleIrp));
  117. }
  118. if (DeviceExtensionPort->PortPdoFlags &
  119. PORTPDO_REMOTE_WAKEUP_ENABLED) {
  120. LOGENTRY(LOG_PNP, "cmWW", deviceExtensionHub, DeviceExtensionPort->WaitWakeIrp, 0);
  121. USBH_KdPrint((1,"'Power state is incompatible with wakeup\n"));
  122. if (DeviceExtensionPort->WaitWakeIrp) {
  123. waitWakeIrp = DeviceExtensionPort->WaitWakeIrp;
  124. DeviceExtensionPort->WaitWakeIrp = NULL;
  125. DeviceExtensionPort->PortPdoFlags &=
  126. ~PORTPDO_REMOTE_WAKEUP_ENABLED;
  127. if (waitWakeIrp->Cancel || IoSetCancelRoutine(waitWakeIrp, NULL) == NULL) {
  128. waitWakeIrp = NULL;
  129. // Must decrement pending request count here because
  130. // we don't complete the IRP below and USBH_WaitWakeCancel
  131. // won't either because we have cleared the IRP pointer
  132. // in the device extension above.
  133. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  134. }
  135. pendingPortWWs =
  136. InterlockedDecrement(&deviceExtensionHub->NumberPortWakeIrps);
  137. if (0 == pendingPortWWs && deviceExtensionHub->PendingWakeIrp) {
  138. hubWaitWake = deviceExtensionHub->PendingWakeIrp;
  139. deviceExtensionHub->PendingWakeIrp = NULL;
  140. }
  141. }
  142. }
  143. //
  144. // Finally, release the cancel spin lock
  145. //
  146. IoReleaseCancelSpinLock(irql);
  147. if (idleIrp) {
  148. idleIrp->IoStatus.Status = STATUS_POWER_STATE_INVALID;
  149. IoCompleteRequest(idleIrp, IO_NO_INCREMENT);
  150. }
  151. if (waitWakeIrp) {
  152. USBH_CompletePowerIrp(deviceExtensionHub, waitWakeIrp,
  153. STATUS_POWER_STATE_INVALID);
  154. }
  155. //
  156. // If there are no more outstanding WW irps, we need to cancel the WW
  157. // to the hub.
  158. //
  159. if (hubWaitWake) {
  160. USBH_HubCancelWakeIrp(deviceExtensionHub, hubWaitWake);
  161. }
  162. //
  163. // first suspend the port, this will cause the
  164. // device to draw minimum power.
  165. //
  166. // we don't turn the port off because if we do we
  167. // won't be able to detect plug/unplug.
  168. //
  169. USBH_SyncSuspendPort(deviceExtensionHub,
  170. portNumber);
  171. //
  172. // note that powering off the port disables connect/disconnect
  173. // detection by the hub and effectively removes the device from
  174. // the bus.
  175. //
  176. DeviceExtensionPort->PortPdoFlags |= PORTPDO_NEED_RESET;
  177. RtlCopyMemory(&DeviceExtensionPort->OldDeviceDescriptor,
  178. &DeviceExtensionPort->DeviceDescriptor,
  179. sizeof(DeviceExtensionPort->DeviceDescriptor));
  180. USBH_KdPrint((1, "'Setting HU pdo(%x) to D3, status = %x complt\n",
  181. DeviceExtensionPort->PortPhysicalDeviceObject, ntStatus));
  182. USBH_SetPowerD3_Done:
  183. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  184. return ntStatus;
  185. }
  186. NTSTATUS
  187. USBH_HubSetD0Completion(
  188. IN PDEVICE_OBJECT DeviceObject,
  189. IN UCHAR MinorFunction,
  190. IN POWER_STATE PowerState,
  191. IN PVOID Context,
  192. IN PIO_STATUS_BLOCK IoStatus
  193. )
  194. /*++
  195. Routine Description:
  196. Arguments:
  197. DeviceObject - Pointer to the device object for the class device.
  198. Irp - Irp completed.
  199. Context - Driver defined context.
  200. Return Value:
  201. The function value is the final status from the operation.
  202. --*/
  203. {
  204. NTSTATUS ntStatus;
  205. PKEVENT pEvent = Context;
  206. KeSetEvent(pEvent, 1, FALSE);
  207. ntStatus = IoStatus->Status;
  208. return ntStatus;
  209. }
  210. NTSTATUS
  211. USBH_HubSetD0(
  212. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  213. )
  214. /*++
  215. Routine Description:
  216. Set the hub to power state D0
  217. Arguments:
  218. DeviceExtensionPort - Hub FDO deviceExtension
  219. Return Value:
  220. The function value is the final status from the operation.
  221. --*/
  222. {
  223. PDEVICE_EXTENSION_HUB rootHubDevExt;
  224. KEVENT event;
  225. POWER_STATE powerState;
  226. NTSTATUS ntStatus;
  227. rootHubDevExt = USBH_GetRootHubDevExt(DeviceExtensionHub);
  228. // Skip powering up the hub if the system is not at S0.
  229. if (rootHubDevExt->CurrentSystemPowerState != PowerSystemWorking) {
  230. USBH_KdPrint((1,"'HubSetD0, skip power up hub %x because system not at S0\n",
  231. DeviceExtensionHub));
  232. return STATUS_INVALID_DEVICE_STATE;
  233. }
  234. USBH_KdPrint((1,"'HubSetD0, power up hub %x\n", DeviceExtensionHub));
  235. LOGENTRY(LOG_PNP, "H!D0", DeviceExtensionHub,
  236. DeviceExtensionHub->CurrentPowerState,
  237. rootHubDevExt->CurrentSystemPowerState);
  238. // If the parent hub is currently in the process of idling out,
  239. // wait until that is done.
  240. if (DeviceExtensionHub->HubFlags & HUBFLAG_PENDING_IDLE_IRP) {
  241. USBH_KdPrint((2,"'Wait for single object\n"));
  242. ntStatus = KeWaitForSingleObject(&DeviceExtensionHub->SubmitIdleEvent,
  243. Suspended,
  244. KernelMode,
  245. FALSE,
  246. NULL);
  247. USBH_KdPrint((2,"'Wait for single object, returned %x\n", ntStatus));
  248. }
  249. // Now, send the actual power up request.
  250. KeInitializeEvent(&event, NotificationEvent, FALSE);
  251. powerState.DeviceState = PowerDeviceD0;
  252. // Power up the hub.
  253. ntStatus = PoRequestPowerIrp(DeviceExtensionHub->PhysicalDeviceObject,
  254. IRP_MN_SET_POWER,
  255. powerState,
  256. USBH_HubSetD0Completion,
  257. &event,
  258. NULL);
  259. USBH_ASSERT(ntStatus == STATUS_PENDING);
  260. if (ntStatus == STATUS_PENDING) {
  261. USBH_KdPrint((2,"'Wait for single object\n"));
  262. ntStatus = KeWaitForSingleObject(&event,
  263. Suspended,
  264. KernelMode,
  265. FALSE,
  266. NULL);
  267. USBH_KdPrint((2,"'Wait for single object, returned %x\n", ntStatus));
  268. }
  269. return ntStatus;
  270. }
  271. NTSTATUS
  272. USBH_SetPowerD0(
  273. IN PIRP Irp,
  274. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort
  275. )
  276. /*++
  277. Routine Description:
  278. Put the PDO in D0
  279. Arguments:
  280. DeviceExtensionPort - port PDO deviceExtension
  281. Irp - Power Irp.
  282. Return Value:
  283. The function value is the final status from the operation.
  284. --*/
  285. {
  286. NTSTATUS ntStatus = STATUS_SUCCESS;
  287. PIO_STACK_LOCATION irpStack;
  288. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  289. USHORT portNumber;
  290. PPORT_DATA portData;
  291. PORT_STATE state;
  292. PAGED_CODE();
  293. irpStack = IoGetCurrentIrpStackLocation(Irp);
  294. deviceExtensionHub = DeviceExtensionPort->DeviceExtensionHub;
  295. portNumber = DeviceExtensionPort->PortNumber;
  296. portData = &deviceExtensionHub->PortData[portNumber - 1];
  297. USBH_KdPrint((2,"'PdoSetPower D0\n"));
  298. LOGENTRY(LOG_PNP, "P>D0", deviceExtensionHub, DeviceExtensionPort,
  299. DeviceExtensionPort->DeviceState);
  300. if (DeviceExtensionPort->DeviceState == PowerDeviceD3) {
  301. //
  302. // device was in D3, port may be off or suspended
  303. // we will need to reset the port state in any case
  304. //
  305. // get port state
  306. ntStatus = USBH_SyncGetPortStatus(deviceExtensionHub,
  307. portNumber,
  308. (PUCHAR) &state,
  309. sizeof(state));
  310. // refresh our internal port state.
  311. portData->PortState = state;
  312. LOGENTRY(LOG_PNP, "PD0s", deviceExtensionHub, *((PULONG) &state), ntStatus);
  313. if (NT_SUCCESS(ntStatus)) {
  314. // port state should be suspended or OFF
  315. // if the hub was powered off then the port
  316. // state will be powered but disabled
  317. if ((state.PortStatus & PORT_STATUS_SUSPEND)) {
  318. //
  319. // resume the port if it was suspended
  320. //
  321. ntStatus = USBH_SyncResumePort(deviceExtensionHub,
  322. portNumber);
  323. } else if (!(state.PortStatus & PORT_STATUS_POWER)) {
  324. //
  325. // probably some kind of selective OFF by the device
  326. // driver -- we just need to power on the port
  327. //
  328. // this requires a hub with individual port power
  329. // switching.
  330. //
  331. ntStatus = USBH_SyncPowerOnPort(deviceExtensionHub,
  332. portNumber,
  333. TRUE);
  334. }
  335. } else {
  336. // the hub driver will notify thru WMI
  337. USBH_KdPrint((0, "'Hub failed after power change from D3\n"));
  338. // USBH_ASSERT(FALSE);
  339. }
  340. //
  341. // if port power switched on this is just like plugging
  342. // in the device for the first time.
  343. // NOTE:
  344. // ** the driver should know that the device needs to be
  345. // re-initialized since it allowed it's PDO to go in to
  346. // the D3 state.
  347. //
  348. // We always call restore device even though we don't need
  349. // to if the port was only suspended, we do this so that
  350. // drivers don't relay on the suspend behavior by mistake.
  351. //
  352. if (NT_SUCCESS(ntStatus)) {
  353. //
  354. // if we still have a device connected attempt to
  355. // restore it.
  356. //
  357. //
  358. // Note: we check here to see if the device object still
  359. // exists in case a change is asserted during the resume.
  360. //
  361. // Note also that we now ignore the connect status bit because
  362. // some machines (e.g. Compaq Armada 7800) are slow to power
  363. // up the ports on the resume and thus port status can show
  364. // no device connected when in fact one is. It shouldn't
  365. // hurt to try to restore the device if it had been removed
  366. // during the suspend/hibernate. In fact, this code handled the
  367. // case where the device had been swapped for another, so this
  368. // is really no different.
  369. if (portData->DeviceObject) {
  370. //
  371. // if this fails the device must have changed
  372. // during power off, in that case we succeed the
  373. // power on.
  374. //
  375. // it will be tossed on the next enumeration
  376. // and relaced with this new device
  377. //
  378. if (USBH_RestoreDevice(DeviceExtensionPort, TRUE) != STATUS_SUCCESS) {
  379. PDEVICE_OBJECT pdo = portData->DeviceObject;
  380. LOGENTRY(LOG_PNP, "PD0!", DeviceExtensionPort, 0, pdo);
  381. USBH_KdPrint((1,"'Device appears to have been swapped during power off\n"));
  382. USBH_KdPrint((1,"'Marking PDO %x for removal\n", portData->DeviceObject));
  383. // leave ref to hub since device data wll need to be
  384. // deleted on remove.
  385. portData->DeviceObject = NULL;
  386. portData->ConnectionStatus = NoDeviceConnected;
  387. // track the Pdo so we know to remove it after we tell PnP it
  388. // is gone
  389. // device should be present if we do this
  390. USBH_ASSERT(PDO_EXT(pdo)->PnPFlags & PDO_PNPFLAG_DEVICE_PRESENT);
  391. InsertTailList(&deviceExtensionHub->DeletePdoList,
  392. &PDO_EXT(pdo)->DeletePdoLink);
  393. }
  394. }
  395. DeviceExtensionPort->DeviceState =
  396. irpStack->Parameters.Power.State.DeviceState;
  397. }
  398. } else if (DeviceExtensionPort->DeviceState == PowerDeviceD2 ||
  399. DeviceExtensionPort->DeviceState == PowerDeviceD1) {
  400. // get port state
  401. ntStatus = USBH_SyncGetPortStatus(deviceExtensionHub,
  402. portNumber,
  403. (PUCHAR) &state,
  404. sizeof(state));
  405. //
  406. // if we got an error assume then the hub is hosed
  407. // just set our flag and bail
  408. //
  409. if (NT_SUCCESS(ntStatus)) {
  410. // see if suspeneded (according to spec). Otherwise only
  411. // try to resume if the port is really suspended.
  412. //
  413. if (state.PortStatus & PORT_STATUS_OVER_CURRENT) {
  414. //
  415. // overcurrent condition indicates this port
  416. // (and hub) are hosed
  417. ntStatus = STATUS_UNSUCCESSFUL;
  418. } else if (state.PortStatus & PORT_STATUS_SUSPEND) {
  419. ntStatus = USBH_SyncResumePort(deviceExtensionHub,
  420. portNumber);
  421. } else {
  422. //
  423. // Most OHCI controllers enable all the ports after a usb
  424. // resume on any port (in violation of the USB spec), in this
  425. // case we should detect that the port is no longer in suspend
  426. // and not try to resume it.
  427. //
  428. // Also, if the device where removed while suspended or the HC
  429. // lost power we should end up here.
  430. //
  431. ntStatus = STATUS_SUCCESS;
  432. }
  433. } else {
  434. USBH_KdPrint((0, "'Hub failed after power change from D2/D1\n"));
  435. LOGENTRY(LOG_PNP, "d0f!", deviceExtensionHub,
  436. 0, 0);
  437. // USBH_ASSERT(FALSE);
  438. }
  439. //
  440. // port is now in D0
  441. //
  442. DeviceExtensionPort->DeviceState =
  443. irpStack->Parameters.Power.State.DeviceState;
  444. USBH_CompletePortIdleNotification(DeviceExtensionPort);
  445. if (NT_SUCCESS(ntStatus)) {
  446. if (DeviceExtensionPort->PortPdoFlags &
  447. PORTPDO_NEED_CLEAR_REMOTE_WAKEUP) {
  448. NTSTATUS status;
  449. //
  450. // disable remote wakeup
  451. //
  452. status = USBH_SyncFeatureRequest(DeviceExtensionPort->PortPhysicalDeviceObject,
  453. USB_FEATURE_REMOTE_WAKEUP,
  454. 0,
  455. TO_USB_DEVICE,
  456. TRUE);
  457. DeviceExtensionPort->PortPdoFlags &=
  458. ~PORTPDO_NEED_CLEAR_REMOTE_WAKEUP;
  459. }
  460. }
  461. }
  462. if (!NT_SUCCESS(ntStatus)) {
  463. USBH_KdPrint((1,"'Set D0 Failure, status = %x\n", ntStatus));
  464. LOGENTRY(LOG_PNP, "d0!!", deviceExtensionHub,
  465. 0, ntStatus);
  466. // we return success to PNP, we will let
  467. // the driver handle the fact that the
  468. // device has lost its brains
  469. //
  470. // NB: This can result in a redundant suspend request for this port
  471. // later on. (Since if we fail here port will remain suspended,
  472. // but our state will indicate that we are in D0.)
  473. ntStatus = STATUS_SUCCESS;
  474. }
  475. DeviceExtensionPort->DeviceState =
  476. irpStack->Parameters.Power.State.DeviceState;
  477. USBH_KdPrint((1, "'Setting HU pdo(%x) to D0, status = %x complt IRP (%x)\n",
  478. DeviceExtensionPort->PortPhysicalDeviceObject, ntStatus, Irp));
  479. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  480. return ntStatus;
  481. }
  482. VOID
  483. USBH_IdleCancelPowerHubWorker(
  484. IN PVOID Context)
  485. /* ++
  486. *
  487. * Description:
  488. *
  489. * Work item scheduled to power up a hub on completion of an Idle request
  490. * for the hub.
  491. *
  492. *
  493. * Arguments:
  494. *
  495. * Return:
  496. *
  497. * -- */
  498. {
  499. PUSBH_PORT_IDLE_POWER_WORK_ITEM workItemIdlePower;
  500. PIRP irp;
  501. PAGED_CODE();
  502. workItemIdlePower = Context;
  503. USBH_HubSetD0(workItemIdlePower->DeviceExtensionHub);
  504. irp = workItemIdlePower->Irp;
  505. irp->IoStatus.Status = STATUS_CANCELLED;
  506. IoCompleteRequest(irp, IO_NO_INCREMENT);
  507. USBH_DEC_PENDING_IO_COUNT(workItemIdlePower->DeviceExtensionHub);
  508. UsbhExFreePool(workItemIdlePower);
  509. }
  510. VOID
  511. USBH_PortIdleNotificationCancelRoutine(
  512. IN PDEVICE_OBJECT DeviceObject,
  513. IN PIRP Irp
  514. )
  515. /*++
  516. Routine Description:
  517. Arguments:
  518. DeviceObject -
  519. Irp - Power Irp.
  520. Return Value:
  521. --*/
  522. {
  523. PUSBH_PORT_IDLE_POWER_WORK_ITEM workItemIdlePower;
  524. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  525. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  526. PIRP irpToCancel = NULL;
  527. USBH_KdPrint((1,"'Idle notification IRP %x cancelled\n", Irp));
  528. deviceExtensionPort = DeviceObject->DeviceExtension;
  529. USBH_ASSERT(deviceExtensionPort->IdleNotificationIrp == NULL ||
  530. deviceExtensionPort->IdleNotificationIrp == Irp);
  531. deviceExtensionPort->IdleNotificationIrp = NULL;
  532. deviceExtensionPort->PortPdoFlags &= ~PORTPDO_IDLE_NOTIFIED;
  533. deviceExtensionHub = deviceExtensionPort->DeviceExtensionHub;
  534. if (deviceExtensionHub &&
  535. deviceExtensionHub->HubFlags & HUBFLAG_PENDING_IDLE_IRP) {
  536. irpToCancel = deviceExtensionHub->PendingIdleIrp;
  537. deviceExtensionHub->PendingIdleIrp = NULL;
  538. } else {
  539. ASSERT(!deviceExtensionHub->PendingIdleIrp);
  540. }
  541. IoReleaseCancelSpinLock(Irp->CancelIrql);
  542. // Cancel the Idle request to the hub if there is one.
  543. if (irpToCancel) {
  544. USBH_HubCancelIdleIrp(deviceExtensionHub, irpToCancel);
  545. }
  546. // Also, power up the hub here before we complete this Idle IRP.
  547. //
  548. // (HID will start to send requests immediately upon its completion,
  549. // which may be before the hub's Idle IRP cancel routine is called
  550. // which powers up the hub.)
  551. if (deviceExtensionHub->CurrentPowerState != PowerDeviceD0) {
  552. // Since we are at DPC we must use a work item to power up the
  553. // hub synchronously, because that function yields and we can't
  554. // yield at DPC level.
  555. workItemIdlePower = UsbhExAllocatePool(NonPagedPool,
  556. sizeof(USBH_PORT_IDLE_POWER_WORK_ITEM));
  557. if (workItemIdlePower) {
  558. workItemIdlePower->DeviceExtensionHub = deviceExtensionHub;
  559. workItemIdlePower->Irp = Irp;
  560. ExInitializeWorkItem(&workItemIdlePower->WorkQueueItem,
  561. USBH_IdleCancelPowerHubWorker,
  562. workItemIdlePower);
  563. LOGENTRY(LOG_PNP, "icIT", deviceExtensionHub,
  564. &workItemIdlePower->WorkQueueItem, 0);
  565. USBH_INC_PENDING_IO_COUNT(deviceExtensionHub);
  566. ExQueueWorkItem(&workItemIdlePower->WorkQueueItem,
  567. DelayedWorkQueue);
  568. // The WorkItem is freed by USBH_IdleCancelPowerHubWorker()
  569. // Don't try to access the WorkItem after it is queued.
  570. }
  571. } else {
  572. Irp->IoStatus.Status = STATUS_CANCELLED;
  573. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  574. }
  575. }
  576. VOID
  577. USBH_CompletePortIdleNotification(
  578. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort
  579. )
  580. {
  581. NTSTATUS status;
  582. KIRQL irql;
  583. PIRP irp = NULL;
  584. PDRIVER_CANCEL oldCancelRoutine;
  585. IoAcquireCancelSpinLock(&irql);
  586. if (DeviceExtensionPort->IdleNotificationIrp) {
  587. irp = DeviceExtensionPort->IdleNotificationIrp;
  588. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  589. if (oldCancelRoutine) {
  590. USBH_ASSERT(oldCancelRoutine == USBH_PortIdleNotificationCancelRoutine);
  591. DeviceExtensionPort->IdleNotificationIrp = NULL;
  592. DeviceExtensionPort->PortPdoFlags &= ~PORTPDO_IDLE_NOTIFIED;
  593. }
  594. #if DBG
  595. else {
  596. USBH_ASSERT(irp->Cancel);
  597. }
  598. #endif
  599. }
  600. IoReleaseCancelSpinLock(irql);
  601. if (irp) {
  602. USBH_KdPrint((1,"'Completing idle request IRP %x\n", irp));
  603. irp->IoStatus.Status = STATUS_SUCCESS;
  604. IoCompleteRequest(irp, IO_NO_INCREMENT);
  605. }
  606. }
  607. NTSTATUS
  608. USBH_SetPowerD1orD2(
  609. IN PIRP Irp,
  610. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort
  611. )
  612. /*++
  613. Routine Description:
  614. Put the PDO in D1/D2 ie suspend
  615. Arguments:
  616. DeviceExtensionPort - port PDO deviceExtension
  617. Irp - Worker Irp.
  618. Return Value:
  619. The function value is the final status from the operation.
  620. --*/
  621. {
  622. NTSTATUS ntStatus = STATUS_SUCCESS;
  623. PIO_STACK_LOCATION irpStack;
  624. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  625. USHORT portNumber;
  626. PAGED_CODE();
  627. irpStack = IoGetCurrentIrpStackLocation(Irp);
  628. deviceExtensionHub = DeviceExtensionPort->DeviceExtensionHub;
  629. portNumber = DeviceExtensionPort->PortNumber;
  630. USBH_KdPrint((2,"'PdoSetPower D1/D2\n"));
  631. if (DeviceExtensionPort->DeviceState == PowerDeviceD1 ||
  632. DeviceExtensionPort->DeviceState == PowerDeviceD2) {
  633. return STATUS_SUCCESS;
  634. }
  635. //
  636. // Enable the device for remote wakeup if necessary
  637. //
  638. if (DeviceExtensionPort->PortPdoFlags &
  639. PORTPDO_REMOTE_WAKEUP_ENABLED) {
  640. NTSTATUS status;
  641. status = USBH_SyncFeatureRequest(DeviceExtensionPort->PortPhysicalDeviceObject,
  642. USB_FEATURE_REMOTE_WAKEUP,
  643. 0,
  644. TO_USB_DEVICE,
  645. FALSE);
  646. DeviceExtensionPort->PortPdoFlags |= PORTPDO_NEED_CLEAR_REMOTE_WAKEUP;
  647. #if DBG
  648. // With the new Selective Suspend support, people are complaining
  649. // about this noise. Let's display it only if debug trace level
  650. // is 1 or higher.
  651. if (USBH_Debug_Trace_Level > 0) {
  652. UsbhWarning(DeviceExtensionPort,
  653. "Device is Enabled for REMOTE WAKEUP\n",
  654. FALSE);
  655. }
  656. #endif
  657. // what do we do about an error here?
  658. // perhaps signal the waitwake irp??
  659. }
  660. ntStatus = USBH_SyncSuspendPort(deviceExtensionHub,
  661. portNumber);
  662. //
  663. // keep track of what OS thinks is the current power state of the
  664. // the device on this port.
  665. //
  666. DeviceExtensionPort->DeviceState =
  667. irpStack->Parameters.Power.State.DeviceState;
  668. DeviceExtensionPort->PortPdoFlags |= PORTPDO_USB_SUSPEND;
  669. USBH_KdPrint((2,"'DeviceExtensionPort->DeviceState = %x\n",
  670. DeviceExtensionPort->DeviceState));
  671. if (!NT_SUCCESS(ntStatus)) {
  672. USBH_KdPrint((1,"'Set D1/D2 Failure, status = %x\n", ntStatus));
  673. // don't pass an error to PnP
  674. ntStatus = STATUS_SUCCESS;
  675. }
  676. USBH_KdPrint((1, "'Setting HU pdo(%x) to D%d, status = %x complt\n",
  677. DeviceExtensionPort->PortPhysicalDeviceObject,
  678. irpStack->Parameters.Power.State.DeviceState - 1,
  679. ntStatus));
  680. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  681. return ntStatus;
  682. }
  683. NTSTATUS
  684. USBH_PdoQueryPower(
  685. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort,
  686. IN PIRP Irp
  687. )
  688. /* ++
  689. *
  690. * Description:
  691. *
  692. * Handles a power irp to a hub PDO
  693. *
  694. * Arguments:
  695. *
  696. * Return:
  697. *
  698. * NTSTATUS
  699. *
  700. * -- */
  701. {
  702. NTSTATUS ntStatus;
  703. PDEVICE_OBJECT deviceObject;
  704. PIO_STACK_LOCATION irpStack;
  705. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  706. USHORT portNumber;
  707. PPORT_DATA portData;
  708. POWER_STATE powerState;
  709. PAGED_CODE();
  710. irpStack = IoGetCurrentIrpStackLocation(Irp);
  711. deviceObject = DeviceExtensionPort->PortPhysicalDeviceObject;
  712. deviceExtensionHub = DeviceExtensionPort->DeviceExtensionHub;
  713. USBH_ASSERT( DeviceExtensionPort->PortNumber < 1000);
  714. portNumber = DeviceExtensionPort->PortNumber;
  715. portData = &deviceExtensionHub->PortData[portNumber - 1];
  716. USBH_KdPrint((2,"'USBH_PdoQueryPower pdo(%x)\n", deviceObject));
  717. switch (irpStack->Parameters.Power.Type) {
  718. case SystemPowerState:
  719. {
  720. //
  721. // We are currently faced with the decision to fail or allow the
  722. // transition to the given S power state. In order to make an
  723. // informed decision, we must first calculate the maximum amount
  724. // of D power allowed in the given S state, and then see if this
  725. // conflicts with a pending Wait Wake IRP.
  726. //
  727. //
  728. // The maximum amount of D power allowed in this S state.
  729. //
  730. powerState.DeviceState =
  731. deviceExtensionHub->DeviceState[irpStack->Parameters.Power.State.SystemState];
  732. //
  733. // These tables should have already been fixed up by the root hub
  734. // (usbd.sys) to not contain an entry of unspecified.
  735. //
  736. ASSERT (PowerDeviceUnspecified != powerState.DeviceState);
  737. //
  738. // The presence of a pending wait wake irp together with a D state that
  739. // will not support waking of the machine means we should fail this
  740. // query.
  741. //
  742. // However, if we are going into Hibernate (or power off) then we
  743. // should not fail this query.
  744. //
  745. if (powerState.DeviceState == PowerDeviceD3 &&
  746. DeviceExtensionPort->WaitWakeIrp &&
  747. irpStack->Parameters.Power.State.SystemState < PowerSystemHibernate) {
  748. ntStatus = STATUS_UNSUCCESSFUL;
  749. USBH_KdPrint(
  750. (1, "'IRP_MJ_POWER HU pdo(%x) MN_QUERY_POWER Failing Query\n", deviceObject));
  751. } else {
  752. ntStatus = STATUS_SUCCESS;
  753. }
  754. LOGENTRY(LOG_PNP, "QPWR", DeviceExtensionPort->PortPhysicalDeviceObject,
  755. irpStack->Parameters.Power.State.SystemState,
  756. powerState.DeviceState);
  757. USBH_KdPrint(
  758. (1, "'IRP_MJ_POWER HU pdo(%x) MN_QUERY_POWER(S%x -> D%x), complt %x\n",
  759. DeviceExtensionPort->PortPhysicalDeviceObject,
  760. irpStack->Parameters.Power.State.SystemState - 1,
  761. powerState.DeviceState - 1,
  762. ntStatus));
  763. #if DBG
  764. if (!NT_SUCCESS(ntStatus)) {
  765. LOGENTRY(LOG_PNP, "QPW!", deviceExtensionHub,
  766. DeviceExtensionPort->WaitWakeIrp,
  767. ntStatus);
  768. }
  769. #endif
  770. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  771. }
  772. break;
  773. case DevicePowerState:
  774. // Return success on this one or NDIS will choke on the suspend.
  775. ntStatus = STATUS_SUCCESS;
  776. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  777. break;
  778. default:
  779. TEST_TRAP();
  780. ntStatus = STATUS_INVALID_PARAMETER;
  781. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  782. } /* power type */
  783. return ntStatus;
  784. }
  785. NTSTATUS
  786. USBH_PdoSetPower(
  787. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort,
  788. IN PIRP Irp
  789. )
  790. /* ++
  791. *
  792. * Description:
  793. *
  794. * Handles a power irp to a hub PDO
  795. *
  796. * Arguments:
  797. *
  798. * Return:
  799. *
  800. * NTSTATUS
  801. *
  802. * -- */
  803. {
  804. NTSTATUS ntStatus;
  805. PDEVICE_OBJECT deviceObject;
  806. PIO_STACK_LOCATION irpStack;
  807. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  808. USHORT portNumber;
  809. PPORT_DATA portData;
  810. PAGED_CODE();
  811. irpStack = IoGetCurrentIrpStackLocation(Irp);
  812. deviceObject = DeviceExtensionPort->PortPhysicalDeviceObject;
  813. deviceExtensionHub = DeviceExtensionPort->DeviceExtensionHub;
  814. USBH_ASSERT( DeviceExtensionPort->PortNumber < 1000);
  815. portNumber = DeviceExtensionPort->PortNumber;
  816. portData = &deviceExtensionHub->PortData[portNumber - 1];
  817. USBH_KdPrint((2,"'USBH_PdoSetPower pdo(%x)\n", deviceObject));
  818. switch (irpStack->Parameters.Power.Type) {
  819. case SystemPowerState:
  820. {
  821. //
  822. // see if the current state of this pdo is valid for the
  823. // system state , if is not then we will need to set the
  824. // pdo to a valid D state.
  825. //
  826. ntStatus = STATUS_SUCCESS;
  827. USBH_KdPrint(
  828. (1, "'IRP_MJ_POWER HU pdo(%x) MN_SET_POWER(SystemPowerState S%x), complt\n",
  829. DeviceExtensionPort->PortPhysicalDeviceObject,
  830. irpStack->Parameters.Power.State.DeviceState - 1,
  831. ntStatus));
  832. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  833. }
  834. break;
  835. case DevicePowerState:
  836. USBH_KdPrint(
  837. (1, "'IRP_MJ_POWER HU pdo(%x) MN_SET_POWER(DevicePowerState D%x)\n",
  838. DeviceExtensionPort->PortPhysicalDeviceObject,
  839. irpStack->Parameters.Power.State.DeviceState - 1));
  840. LOGENTRY(LOG_PNP, "P>Dx", deviceExtensionHub,
  841. DeviceExtensionPort->PortPhysicalDeviceObject,
  842. irpStack->Parameters.Power.State.DeviceState);
  843. // If we are already in the requested power state,
  844. // just complete the request.
  845. if (DeviceExtensionPort->DeviceState ==
  846. irpStack->Parameters.Power.State.DeviceState) {
  847. // If we are skipping this set power request and it is a SetD0
  848. // request, assert that the parent hub is in D0.
  849. USBH_ASSERT(DeviceExtensionPort->DeviceState != PowerDeviceD0 ||
  850. deviceExtensionHub->CurrentPowerState == PowerDeviceD0);
  851. ntStatus = STATUS_SUCCESS;
  852. goto PdoSetPowerCompleteIrp;
  853. }
  854. // USBH_ASSERT(deviceExtensionHub->CurrentPowerState == PowerDeviceD0);
  855. switch (irpStack->Parameters.Power.State.DeviceState) {
  856. case PowerDeviceD0:
  857. ntStatus = USBH_SetPowerD0(Irp, DeviceExtensionPort);
  858. break;
  859. case PowerDeviceD1:
  860. case PowerDeviceD2:
  861. ntStatus = USBH_SetPowerD1orD2(Irp, DeviceExtensionPort);
  862. break;
  863. case PowerDeviceD3:
  864. //
  865. // In the case of D3 we need to complete any pending WaitWake
  866. // Irps with the status code STATUS_POWER_STATE_INVALID.
  867. // This is done in USBH_SetPowerD3.
  868. //
  869. ntStatus = USBH_SetPowerD3(Irp, DeviceExtensionPort);
  870. break;
  871. default:
  872. USBH_KdTrap(("Bad Power State\n"));
  873. ntStatus = STATUS_INVALID_PARAMETER;
  874. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  875. }
  876. break;
  877. default:
  878. TEST_TRAP();
  879. ntStatus = STATUS_INVALID_PARAMETER;
  880. PdoSetPowerCompleteIrp:
  881. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  882. } /* power type */
  883. return ntStatus;
  884. }
  885. VOID
  886. USBH_WaitWakeCancel(
  887. IN PDEVICE_OBJECT DeviceObject,
  888. IN PIRP Irp
  889. )
  890. /*++
  891. Routine Description:
  892. Arguments:
  893. Return Value:
  894. NT status code.
  895. --*/
  896. {
  897. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  898. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  899. NTSTATUS ntStatus = STATUS_CANCELLED;
  900. LONG pendingPortWWs;
  901. PIRP hubWaitWake = NULL;
  902. USBH_KdPrint((1,"'WaitWake Irp %x for PDO cancelled\n", Irp));
  903. USBH_ASSERT(DeviceObject);
  904. deviceExtensionPort = (PDEVICE_EXTENSION_PORT) Irp->IoStatus.Information;
  905. deviceExtensionHub = deviceExtensionPort->DeviceExtensionHub;
  906. LOGENTRY(LOG_PNP, "WWca", Irp, deviceExtensionPort, deviceExtensionHub);
  907. if (Irp != deviceExtensionPort->WaitWakeIrp) {
  908. //
  909. // Nothing to do
  910. // This Irp has already been taken care of.
  911. // We are in the process of completing this IRP in
  912. // USBH_HubCompletePortWakeIrps.
  913. //
  914. TEST_TRAP();
  915. IoReleaseCancelSpinLock(Irp->CancelIrql);
  916. } else {
  917. deviceExtensionPort->WaitWakeIrp = NULL;
  918. deviceExtensionPort->PortPdoFlags &=
  919. ~PORTPDO_REMOTE_WAKEUP_ENABLED;
  920. IoSetCancelRoutine(Irp, NULL);
  921. pendingPortWWs = InterlockedDecrement(&deviceExtensionHub->NumberPortWakeIrps);
  922. if (0 == pendingPortWWs && deviceExtensionHub->PendingWakeIrp) {
  923. // Set PendingWakeIrp to NULL since we cancel it below.
  924. hubWaitWake = deviceExtensionHub->PendingWakeIrp;
  925. deviceExtensionHub->PendingWakeIrp = NULL;
  926. }
  927. IoReleaseCancelSpinLock(Irp->CancelIrql);
  928. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  929. //
  930. // If there are no more outstanding WW irps, we need to cancel the WW
  931. // to the hub.
  932. //
  933. if (hubWaitWake) {
  934. USBH_HubCancelWakeIrp(deviceExtensionHub, hubWaitWake);
  935. }
  936. // else {
  937. // This assert is no longer valid as I now clear the PendingWakeIrp
  938. // pointer for the hub in USBH_FdoWaitWakeIrpCompletion, instead
  939. // of waiting to do it here when NumberPortWakeIrps reaches zero.
  940. // So it is completely normal to arrive here with no port wake
  941. // IRP's and a NULL PendingWakeIrp for the hub.
  942. // ASSERT (0 < pendingPortWWs);
  943. // }
  944. }
  945. }
  946. NTSTATUS
  947. USBH_PdoWaitWake(
  948. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort,
  949. IN PIRP Irp
  950. )
  951. /* ++
  952. *
  953. * Description:
  954. *
  955. * Arguments:
  956. *
  957. * Return:
  958. *
  959. * NTSTATUS
  960. *
  961. * -- */
  962. {
  963. NTSTATUS ntStatus = STATUS_SUCCESS;
  964. PDEVICE_OBJECT deviceObject;
  965. PIO_STACK_LOCATION irpStack;
  966. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  967. USHORT portNumber;
  968. PPORT_DATA portData;
  969. KIRQL irql;
  970. PDRIVER_CANCEL oldCancel;
  971. LONG pendingPortWWs = 0;
  972. irpStack = IoGetCurrentIrpStackLocation(Irp);
  973. deviceObject = DeviceExtensionPort->PortPhysicalDeviceObject;
  974. deviceExtensionHub = DeviceExtensionPort->DeviceExtensionHub;
  975. USBH_ASSERT( DeviceExtensionPort->PortNumber < 1000);
  976. portNumber = (USHORT) DeviceExtensionPort->PortNumber;
  977. portData = &deviceExtensionHub->PortData[portNumber - 1];
  978. USBH_KdPrint((2,"'PnP WaitWake Irp passed to PDO %x\n", deviceObject));
  979. LOGENTRY(LOG_PNP, "PWW_", deviceObject, DeviceExtensionPort, deviceExtensionHub);
  980. if (DeviceExtensionPort->DeviceState != PowerDeviceD0 ||
  981. deviceExtensionHub->HubFlags & HUBFLAG_DEVICE_STOPPING) {
  982. LOGENTRY(LOG_PNP, "!WWh", DeviceExtensionPort, deviceExtensionHub, 0);
  983. UsbhWarning(NULL,
  984. "Client driver should not be submitting WW IRPs at this time.\n",
  985. TRUE);
  986. ntStatus = STATUS_INVALID_DEVICE_STATE;
  987. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  988. return ntStatus;
  989. }
  990. //
  991. // First verify that there is not already a WaitWake Irp pending for
  992. // this PDO.
  993. //
  994. //
  995. // make sure that this device can support remote wakeup.
  996. //
  997. // NOTE: that we treat all hubs as capable of remote
  998. // wakeup regardless of what the device reports. The reason
  999. // is that all hubs must propagate resume signalling regardless
  1000. // of their abilty to generate resume signalling on a
  1001. // plug-in/out event.
  1002. //
  1003. #if DBG
  1004. if (UsbhPnpTest & PNP_TEST_FAIL_WAKE_REQUEST) {
  1005. DeviceExtensionPort->PortPdoFlags &=
  1006. ~PORTPDO_REMOTE_WAKEUP_SUPPORTED;
  1007. }
  1008. #endif
  1009. if (DeviceExtensionPort->PortPdoFlags &
  1010. PORTPDO_REMOTE_WAKEUP_SUPPORTED) {
  1011. IoAcquireCancelSpinLock(&irql);
  1012. if (DeviceExtensionPort->WaitWakeIrp != NULL) {
  1013. LOGENTRY(LOG_PNP, "PWWx", deviceObject, DeviceExtensionPort,
  1014. DeviceExtensionPort->WaitWakeIrp);
  1015. ntStatus = STATUS_DEVICE_BUSY;
  1016. IoReleaseCancelSpinLock(irql);
  1017. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  1018. } else {
  1019. // set a cancel routine
  1020. oldCancel = IoSetCancelRoutine(Irp, USBH_WaitWakeCancel);
  1021. USBH_ASSERT (NULL == oldCancel);
  1022. if (Irp->Cancel) {
  1023. oldCancel = IoSetCancelRoutine(Irp, NULL);
  1024. if (oldCancel) {
  1025. //
  1026. // Cancel routine hasn't fired.
  1027. //
  1028. ASSERT(oldCancel == USBH_WaitWakeCancel);
  1029. ntStatus = STATUS_CANCELLED;
  1030. IoReleaseCancelSpinLock(irql);
  1031. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  1032. } else {
  1033. //
  1034. // Cancel routine WAS called
  1035. //
  1036. IoMarkIrpPending(Irp);
  1037. ntStatus = Irp->IoStatus.Status = STATUS_PENDING;
  1038. IoReleaseCancelSpinLock(irql);
  1039. }
  1040. } else {
  1041. USBH_KdPrint(
  1042. (1, "'enabling remote wakeup for USB device PDO (%x)\n",
  1043. DeviceExtensionPort->PortPhysicalDeviceObject));
  1044. // flag this device as "enabled for wakeup"
  1045. DeviceExtensionPort->WaitWakeIrp = Irp;
  1046. DeviceExtensionPort->PortPdoFlags |=
  1047. PORTPDO_REMOTE_WAKEUP_ENABLED;
  1048. Irp->IoStatus.Information = (ULONG_PTR) DeviceExtensionPort;
  1049. pendingPortWWs =
  1050. InterlockedIncrement(&deviceExtensionHub->NumberPortWakeIrps);
  1051. IoMarkIrpPending(Irp);
  1052. LOGENTRY(LOG_PNP, "PWW+", DeviceExtensionPort, Irp, pendingPortWWs);
  1053. IoReleaseCancelSpinLock(irql);
  1054. ntStatus = STATUS_PENDING;
  1055. }
  1056. }
  1057. //
  1058. // Now we must enable the hub for wakeup.
  1059. //
  1060. // We may already have a WW IRP pending if this hub were previously
  1061. // selective suspended, but we had to power it back on (USBH_HubSetD0)
  1062. // for a PnP request. Don't post a new WW IRP if there is already
  1063. // one pending.
  1064. //
  1065. if (ntStatus == STATUS_PENDING && 1 == pendingPortWWs &&
  1066. !(deviceExtensionHub->HubFlags & HUBFLAG_PENDING_WAKE_IRP)) {
  1067. USBH_FdoSubmitWaitWakeIrp(deviceExtensionHub);
  1068. }
  1069. } else {
  1070. ntStatus = STATUS_NOT_SUPPORTED;
  1071. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  1072. }
  1073. return ntStatus;
  1074. }
  1075. VOID
  1076. USBH_HubAsyncPowerWorker(
  1077. IN PVOID Context)
  1078. /* ++
  1079. *
  1080. * Description:
  1081. *
  1082. * Work item scheduled to handle a hub ESD failure.
  1083. *
  1084. *
  1085. * Arguments:
  1086. *
  1087. * Return:
  1088. *
  1089. * -- */
  1090. {
  1091. PUSBH_HUB_ASYNC_POWER_WORK_ITEM context;
  1092. NTSTATUS ntStatus;
  1093. PAGED_CODE();
  1094. context = Context;
  1095. if (context->Irp->PendingReturned) {
  1096. IoMarkIrpPending(context->Irp);
  1097. }
  1098. switch (context->MinorFunction) {
  1099. case IRP_MN_SET_POWER:
  1100. ntStatus = USBH_PdoSetPower(context->DeviceExtensionPort,
  1101. context->Irp);
  1102. break;
  1103. case IRP_MN_QUERY_POWER:
  1104. ntStatus = USBH_PdoQueryPower(context->DeviceExtensionPort,
  1105. context->Irp);
  1106. break;
  1107. default:
  1108. // Should never get here.
  1109. USBH_ASSERT(FALSE);
  1110. }
  1111. UsbhExFreePool(context);
  1112. }
  1113. NTSTATUS
  1114. USBH_HubAsyncPowerSetD0Completion(
  1115. IN PDEVICE_OBJECT DeviceObject,
  1116. IN UCHAR MinorFunction,
  1117. IN POWER_STATE PowerState,
  1118. IN PVOID Context,
  1119. IN PIO_STATUS_BLOCK IoStatus
  1120. )
  1121. /*++
  1122. Routine Description:
  1123. Arguments:
  1124. DeviceObject - Pointer to the device object for the class device.
  1125. Irp - Irp completed.
  1126. Context - Driver defined context.
  1127. Return Value:
  1128. The function value is the final status from the operation.
  1129. --*/
  1130. {
  1131. PUSBH_HUB_ASYNC_POWER_WORK_ITEM context;
  1132. NTSTATUS ntStatus, status;
  1133. context = Context;
  1134. ntStatus = IoStatus->Status;
  1135. // We schedule the work item regardless of whether the hub power up
  1136. // request was successful or not.
  1137. ExInitializeWorkItem(&context->WorkQueueItem,
  1138. USBH_HubAsyncPowerWorker,
  1139. context);
  1140. LOGENTRY(LOG_PNP, "HAPW", context->DeviceExtensionPort,
  1141. &context->WorkQueueItem, 0);
  1142. // critical saves time on resume
  1143. ExQueueWorkItem(&context->WorkQueueItem,
  1144. CriticalWorkQueue);
  1145. // The WorkItem is freed by USBH_HubAsyncPowerWorker()
  1146. // Don't try to access the WorkItem after it is queued.
  1147. return ntStatus;
  1148. }
  1149. NTSTATUS
  1150. USBH_PdoPower(
  1151. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort,
  1152. IN PIRP Irp,
  1153. IN UCHAR MinorFunction
  1154. )
  1155. /* ++
  1156. *
  1157. * Description:
  1158. *
  1159. * This function responds to IoControl Power for the PDO. This function is
  1160. * synchronous.
  1161. *
  1162. * Arguments:
  1163. *
  1164. * DeviceExtensionPort - the PDO extension Irp - the request packet
  1165. * uchMinorFunction - the minor function of the PnP Power request.
  1166. *
  1167. * Return:
  1168. *
  1169. * NTSTATUS
  1170. *
  1171. * -- */
  1172. {
  1173. NTSTATUS ntStatus;
  1174. PDEVICE_OBJECT deviceObject;
  1175. PIO_STACK_LOCATION irpStack;
  1176. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  1177. POWER_STATE powerState;
  1178. PUSBH_HUB_ASYNC_POWER_WORK_ITEM context;
  1179. PAGED_CODE();
  1180. deviceExtensionHub = DeviceExtensionPort->DeviceExtensionHub;
  1181. irpStack = IoGetCurrentIrpStackLocation(Irp);
  1182. deviceObject = DeviceExtensionPort->PortPhysicalDeviceObject;
  1183. USBH_KdPrint((2,"'USBH_PdoPower pdo(%x)\n", deviceObject));
  1184. // special case device removed
  1185. if (deviceExtensionHub == NULL) {
  1186. // if there is no backpointer to the parent hub then there
  1187. // is a delete/remove comming. just complete this power
  1188. // request with success
  1189. USBH_KdPrint((1,"'complete power on orphan Pdo %x\n", deviceObject));
  1190. if (MinorFunction == IRP_MN_SET_POWER ||
  1191. MinorFunction == IRP_MN_QUERY_POWER) {
  1192. Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS;
  1193. PoStartNextPowerIrp(Irp);
  1194. } else {
  1195. Irp->IoStatus.Status = ntStatus = STATUS_NOT_SUPPORTED;
  1196. }
  1197. PoStartNextPowerIrp(Irp);
  1198. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  1199. return ntStatus;
  1200. }
  1201. USBH_ASSERT(deviceExtensionHub);
  1202. // specail case device not in D0
  1203. // one more item pending in the hub
  1204. USBH_INC_PENDING_IO_COUNT(deviceExtensionHub);
  1205. // If the hub has been selectively suspended, then we need to power it up
  1206. // to service QUERY or SET POWER requests. However, we can't block on
  1207. // this power IRP waiting for the parent hub to power up, so we need to
  1208. // power up the parent hub asynchronously and handle this IRP after the
  1209. // hub power up request has completed. Major PITA.
  1210. if (deviceExtensionHub->CurrentPowerState != PowerDeviceD0 &&
  1211. (MinorFunction == IRP_MN_SET_POWER ||
  1212. MinorFunction == IRP_MN_QUERY_POWER)) {
  1213. // Allocate buffer for context.
  1214. context = UsbhExAllocatePool(NonPagedPool,
  1215. sizeof(USBH_HUB_ASYNC_POWER_WORK_ITEM));
  1216. if (context) {
  1217. context->DeviceExtensionPort = DeviceExtensionPort;
  1218. context->Irp = Irp;
  1219. context->MinorFunction = MinorFunction;
  1220. // We'll complete this IRP in the completion routine for the hub's
  1221. // Set D0 IRP.
  1222. IoMarkIrpPending(Irp);
  1223. powerState.DeviceState = PowerDeviceD0;
  1224. // Power up the hub.
  1225. ntStatus = PoRequestPowerIrp(deviceExtensionHub->PhysicalDeviceObject,
  1226. IRP_MN_SET_POWER,
  1227. powerState,
  1228. USBH_HubAsyncPowerSetD0Completion,
  1229. context,
  1230. NULL);
  1231. // We need to return STATUS_PENDING here because we marked the
  1232. // IRP pending above with IoMarkIrpPending.
  1233. USBH_ASSERT(ntStatus == STATUS_PENDING);
  1234. // In the case where an allocation failed, PoRequestPowerIrp can
  1235. // return a status code other than STATUS_PENDING. In this case,
  1236. // we need to complete the IRP passed to us, but we still need
  1237. // to return STATUS_PENDING from this routine.
  1238. if (ntStatus != STATUS_PENDING) {
  1239. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  1240. }
  1241. ntStatus = STATUS_PENDING;
  1242. } else {
  1243. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  1244. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  1245. }
  1246. } else switch (MinorFunction) {
  1247. case IRP_MN_SET_POWER:
  1248. ntStatus = USBH_PdoSetPower(DeviceExtensionPort, Irp);
  1249. break;
  1250. case IRP_MN_WAIT_WAKE:
  1251. ntStatus = USBH_PdoWaitWake(DeviceExtensionPort, Irp);
  1252. USBH_KdPrint((1, "'IRP_MN_WAIT_WAKE pdo(%x), status = 0x%x\n",
  1253. DeviceExtensionPort->PortPhysicalDeviceObject, ntStatus));
  1254. break;
  1255. case IRP_MN_QUERY_POWER:
  1256. ntStatus = USBH_PdoQueryPower(DeviceExtensionPort, Irp);
  1257. break;
  1258. default:
  1259. ntStatus = Irp->IoStatus.Status;
  1260. USBH_KdPrint((1, "'IRP_MN_[%d](%x), status = 0x%x (not handled)\n",
  1261. MinorFunction,
  1262. DeviceExtensionPort->PortPhysicalDeviceObject,
  1263. ntStatus));
  1264. USBH_KdBreak(("PdoPower unknown\n"));
  1265. //
  1266. // return the original status passed to us
  1267. //
  1268. USBH_CompletePowerIrp(deviceExtensionHub, Irp, ntStatus);
  1269. }
  1270. USBH_KdPrint((2,"'USBH_PdoPower pdo exit %x\n", ntStatus));
  1271. return ntStatus;
  1272. }
  1273. VOID
  1274. USBH_SetPowerD0Worker(
  1275. IN PVOID Context)
  1276. /* ++
  1277. *
  1278. * Description:
  1279. *
  1280. * Work item scheduled to handle a Set Power D0 IRP for the hub.
  1281. *
  1282. *
  1283. * Arguments:
  1284. *
  1285. * Return:
  1286. *
  1287. * -- */
  1288. {
  1289. PUSBH_SET_POWER_D0_WORK_ITEM workItemSetPowerD0;
  1290. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  1291. PIRP irp;
  1292. PPORT_DATA portData;
  1293. ULONG p, numberOfPorts;
  1294. NTSTATUS ntStatus = STATUS_SUCCESS;
  1295. workItemSetPowerD0 = Context;
  1296. deviceExtensionHub = workItemSetPowerD0->DeviceExtensionHub;
  1297. irp = workItemSetPowerD0->Irp;
  1298. USBH_KdPrint((2,"'Hub Set Power D0 work item\n"));
  1299. LOGENTRY(LOG_PNP, "HD0W", deviceExtensionHub, irp, 0);
  1300. // restore the hub from OFF
  1301. // the device has lost its brains, we need to go thru the
  1302. // init process again
  1303. // our ports will be indicating status changes at this
  1304. // point. We need to flush out any change indications
  1305. // before we re-enable the hub
  1306. // first clear out our port status info
  1307. portData = deviceExtensionHub->PortData;
  1308. if (portData &&
  1309. deviceExtensionHub->HubDescriptor) {
  1310. numberOfPorts = deviceExtensionHub->HubDescriptor->bNumberOfPorts;
  1311. // first clear out our port status info
  1312. for (p = 1;
  1313. p <= numberOfPorts;
  1314. p++, portData++) {
  1315. portData->PortState.PortChange = 0;
  1316. portData->PortState.PortStatus = 0;
  1317. }
  1318. portData = deviceExtensionHub->PortData;
  1319. // power up the hub
  1320. ntStatus = USBH_SyncPowerOnPorts(deviceExtensionHub);
  1321. // Probably need to enable this code for Mike Mangum's bug.
  1322. // UsbhWait(500); // Allow USB storage devices some time to power up.
  1323. // flush out any change indications
  1324. if (NT_SUCCESS(ntStatus)) {
  1325. for (p = 1;
  1326. p <= numberOfPorts;
  1327. p++, portData++) {
  1328. if (portData->DeviceObject) {
  1329. ntStatus = USBH_FlushPortChange(deviceExtensionHub,
  1330. portData->DeviceObject->DeviceExtension);
  1331. if (NT_ERROR(ntStatus)) {
  1332. LOGENTRY(LOG_PNP, "flsX", deviceExtensionHub, p,
  1333. portData->DeviceObject);
  1334. USBH_KdPrint((1,"'USBH_FlushPortChange failed!\n"));
  1335. }
  1336. }
  1337. }
  1338. }
  1339. // Since we just flushed all port changes we now don't
  1340. // know if there were any real port changes (e.g. a
  1341. // device was unplugged). We must call
  1342. // IoInvalidateDeviceRelations to trigger a QBR
  1343. // so that we can see if the devices are still there.
  1344. USBH_IoInvalidateDeviceRelations(deviceExtensionHub->PhysicalDeviceObject,
  1345. BusRelations);
  1346. }
  1347. if (!(deviceExtensionHub->HubFlags &
  1348. HUBFLAG_HUB_STOPPED)) {
  1349. USBH_SubmitInterruptTransfer(deviceExtensionHub);
  1350. }
  1351. // Tell ACPI that we are ready for another power IRP and complete
  1352. // the IRP.
  1353. irp->IoStatus.Status = ntStatus;
  1354. PoStartNextPowerIrp(irp);
  1355. IoCompleteRequest(irp, IO_NO_INCREMENT);
  1356. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1357. UsbhExFreePool(workItemSetPowerD0);
  1358. }
  1359. NTSTATUS
  1360. USBH_PowerIrpCompletion(
  1361. IN PDEVICE_OBJECT DeviceObject,
  1362. IN PIRP Irp,
  1363. IN PVOID Context
  1364. )
  1365. /*++
  1366. Routine Description:
  1367. This routine is called when the port driver completes an IRP.
  1368. Arguments:
  1369. DeviceObject - Pointer to the device object for the class device.
  1370. Irp - Irp completed.
  1371. Context - Driver defined context.
  1372. Return Value:
  1373. The function value is the final status from the operation.
  1374. --*/
  1375. {
  1376. NTSTATUS ntStatus;
  1377. PIO_STACK_LOCATION irpStack;
  1378. PDEVICE_EXTENSION_HUB deviceExtensionHub = Context;
  1379. DEVICE_POWER_STATE oldPowerState;
  1380. PUSBH_SET_POWER_D0_WORK_ITEM workItemSetPowerD0;
  1381. irpStack = IoGetCurrentIrpStackLocation(Irp);
  1382. ntStatus = Irp->IoStatus.Status;
  1383. USBH_ASSERT(irpStack->Parameters.Power.Type == DevicePowerState);
  1384. LOGENTRY(LOG_PNP, "PwrC", deviceExtensionHub, Irp,
  1385. irpStack->Parameters.Power.State.DeviceState);
  1386. if (Irp->PendingReturned) {
  1387. IoMarkIrpPending(Irp);
  1388. }
  1389. if (NT_SUCCESS(ntStatus)) {
  1390. switch (irpStack->Parameters.Power.State.DeviceState) {
  1391. case PowerDeviceD0:
  1392. oldPowerState = deviceExtensionHub->CurrentPowerState;
  1393. deviceExtensionHub->CurrentPowerState =
  1394. irpStack->Parameters.Power.State.DeviceState;
  1395. deviceExtensionHub->HubFlags &= ~HUBFLAG_SET_D0_PENDING;
  1396. if ((deviceExtensionHub->HubFlags & HUBFLAG_HIBER) &&
  1397. oldPowerState != PowerDeviceD3) {
  1398. ULONG p, numberOfPorts;
  1399. PPORT_DATA portData;
  1400. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  1401. // we are going to d0 from hibernate, we may
  1402. // have been in D2 but we want to always go
  1403. // thru the D3->D0 codepath since the bus was reset.
  1404. oldPowerState = PowerDeviceD3;
  1405. // modify children
  1406. numberOfPorts = deviceExtensionHub->HubDescriptor->bNumberOfPorts;
  1407. portData = deviceExtensionHub->PortData;
  1408. for (p = 1;
  1409. p <= numberOfPorts;
  1410. p++, portData++) {
  1411. if (portData->DeviceObject) {
  1412. deviceExtensionPort =
  1413. portData->DeviceObject->DeviceExtension;
  1414. deviceExtensionPort->DeviceState = PowerDeviceD3;
  1415. deviceExtensionPort->PortPdoFlags |= PORTPDO_NEED_RESET;
  1416. }
  1417. }
  1418. }
  1419. deviceExtensionHub->HubFlags &= ~HUBFLAG_HIBER;
  1420. if (oldPowerState == PowerDeviceD3) {
  1421. //
  1422. // Schedule a work item to process this.
  1423. //
  1424. workItemSetPowerD0 = UsbhExAllocatePool(NonPagedPool,
  1425. sizeof(USBH_SET_POWER_D0_WORK_ITEM));
  1426. if (workItemSetPowerD0) {
  1427. workItemSetPowerD0->DeviceExtensionHub = deviceExtensionHub;
  1428. workItemSetPowerD0->Irp = Irp;
  1429. ExInitializeWorkItem(&workItemSetPowerD0->WorkQueueItem,
  1430. USBH_SetPowerD0Worker,
  1431. workItemSetPowerD0);
  1432. LOGENTRY(LOG_PNP, "HD0Q", deviceExtensionHub,
  1433. &workItemSetPowerD0->WorkQueueItem, 0);
  1434. USBH_INC_PENDING_IO_COUNT(deviceExtensionHub);
  1435. // critical saves time on resume
  1436. ExQueueWorkItem(&workItemSetPowerD0->WorkQueueItem,
  1437. CriticalWorkQueue);
  1438. // The WorkItem is freed by USBH_SetPowerD0Worker()
  1439. // Don't try to access the WorkItem after it is queued.
  1440. ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1441. } else {
  1442. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  1443. }
  1444. } else {
  1445. if (!(deviceExtensionHub->HubFlags &
  1446. HUBFLAG_HUB_STOPPED)) {
  1447. USBH_SubmitInterruptTransfer(deviceExtensionHub);
  1448. }
  1449. }
  1450. // If we're not going to complete the PowerDeviceD0 request later
  1451. // in USBH_SetPowerD0Worker(), start the next power IRP here now.
  1452. //
  1453. if (ntStatus != STATUS_MORE_PROCESSING_REQUIRED) {
  1454. PoStartNextPowerIrp(Irp);
  1455. }
  1456. break;
  1457. case PowerDeviceD1:
  1458. case PowerDeviceD2:
  1459. case PowerDeviceD3:
  1460. deviceExtensionHub->CurrentPowerState =
  1461. irpStack->Parameters.Power.State.DeviceState;
  1462. break;
  1463. }
  1464. USBH_KdPrint((1, "'Setting HU fdo(%x) to D%d, status = %x\n",
  1465. deviceExtensionHub->FunctionalDeviceObject,
  1466. irpStack->Parameters.Power.State.DeviceState - 1,
  1467. ntStatus));
  1468. } else {
  1469. if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) {
  1470. // Don't forget to start the next power IRP if this is set D0
  1471. // and it failed.
  1472. PoStartNextPowerIrp(Irp);
  1473. deviceExtensionHub->HubFlags &= ~HUBFLAG_SET_D0_PENDING;
  1474. }
  1475. }
  1476. return ntStatus;
  1477. }
  1478. NTSTATUS
  1479. USBH_FdoDeferPoRequestCompletion(
  1480. IN PDEVICE_OBJECT DeviceObject,
  1481. IN UCHAR MinorFunction,
  1482. IN POWER_STATE PowerState,
  1483. IN PVOID Context,
  1484. IN PIO_STATUS_BLOCK IoStatus
  1485. )
  1486. /*++
  1487. Routine Description:
  1488. This routine is called when the port driver completes an IRP.
  1489. Arguments:
  1490. DeviceObject - Pointer to the device object for the class device.
  1491. Context - Driver defined context.
  1492. Return Value:
  1493. The function value is the final status from the operation.
  1494. --*/
  1495. {
  1496. PIRP irp;
  1497. PDEVICE_EXTENSION_FDO deviceExtension;
  1498. PDEVICE_EXTENSION_HUB deviceExtensionHub = NULL;
  1499. NTSTATUS ntStatus;
  1500. PIO_STACK_LOCATION irpStack;
  1501. deviceExtension = Context;
  1502. irp = deviceExtension->PowerIrp;
  1503. // return the status of this operation
  1504. ntStatus = IoStatus->Status;
  1505. USBH_KdPrint((2,"'USBH_FdoDeferPoRequestCompletion, ntStatus = %x\n",
  1506. ntStatus));
  1507. // It is normal for the power IRP to fail if a hub was removed during
  1508. // hibernate.
  1509. //
  1510. //#if DBG
  1511. // if (NT_ERROR(ntStatus)) {
  1512. // USBH_KdTrap(("Device Power Irp Failed (%x)\n", ntStatus));
  1513. // }
  1514. //#endif
  1515. if (deviceExtension->ExtensionType == EXTENSION_TYPE_HUB) {
  1516. deviceExtensionHub = Context;
  1517. }
  1518. irpStack = IoGetCurrentIrpStackLocation(irp);
  1519. if (irpStack->Parameters.Power.State.SystemState == PowerSystemWorking &&
  1520. deviceExtensionHub != NULL &&
  1521. IS_ROOT_HUB(deviceExtensionHub)) {
  1522. // Allow selective suspend once again now that the root hub has
  1523. // been powered up.
  1524. LOGENTRY(LOG_PNP, "ESus", deviceExtensionHub, 0, 0);
  1525. USBH_KdPrint((1,"'Selective Suspend possible again because Root Hub is now at D0\n"));
  1526. // We know this is the root hub so we don't need to call
  1527. // USBH_GetRootHubDevExt to get it.
  1528. deviceExtensionHub->CurrentSystemPowerState =
  1529. irpStack->Parameters.Power.State.SystemState;
  1530. }
  1531. USBH_KdPrint((2,"'irp = %x devobj = %x\n",
  1532. irp, deviceExtension->TopOfStackDeviceObject));
  1533. IoCopyCurrentIrpStackLocationToNext(irp);
  1534. PoStartNextPowerIrp(irp);
  1535. PoCallDriver(deviceExtension->TopOfStackDeviceObject,
  1536. irp);
  1537. return ntStatus;
  1538. }
  1539. VOID
  1540. USBH_HubQueuePortWakeIrps(
  1541. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1542. IN PLIST_ENTRY IrpsToComplete
  1543. )
  1544. /*++
  1545. Routine Description:
  1546. Called to queue all the pending child port WW IRPs of a given
  1547. hub into a private queue.
  1548. Arguments:
  1549. Return Value:
  1550. The function value is the final status from the operation.
  1551. --*/
  1552. {
  1553. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  1554. PUSB_HUB_DESCRIPTOR hubDescriptor;
  1555. PPORT_DATA portData;
  1556. PIRP irp;
  1557. KIRQL irql;
  1558. ULONG numberOfPorts, i;
  1559. LONG pendingPortWWs;
  1560. hubDescriptor = DeviceExtensionHub->HubDescriptor;
  1561. USBH_ASSERT(NULL != hubDescriptor);
  1562. numberOfPorts = hubDescriptor->bNumberOfPorts;
  1563. InitializeListHead(IrpsToComplete);
  1564. // First, queue all the port wake IRPs into a local list while
  1565. // the cancel spinlock is held. This will prevent new WW IRPs for
  1566. // these ports from being submitted while we are traversing the list.
  1567. // Once we have queued them all we will release the spinlock (because
  1568. // the list no longer needs protection), then complete the IRPs.
  1569. IoAcquireCancelSpinLock(&irql);
  1570. for (i=0; i<numberOfPorts; i++) {
  1571. portData = &DeviceExtensionHub->PortData[i];
  1572. if (portData->DeviceObject) {
  1573. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  1574. irp = deviceExtensionPort->WaitWakeIrp;
  1575. deviceExtensionPort->WaitWakeIrp = NULL;
  1576. // signal the waitwake irp if we have one
  1577. if (irp) {
  1578. IoSetCancelRoutine(irp, NULL);
  1579. deviceExtensionPort->PortPdoFlags &=
  1580. ~PORTPDO_REMOTE_WAKEUP_ENABLED;
  1581. pendingPortWWs =
  1582. InterlockedDecrement(&DeviceExtensionHub->NumberPortWakeIrps);
  1583. InsertTailList(IrpsToComplete, &irp->Tail.Overlay.ListEntry);
  1584. }
  1585. }
  1586. }
  1587. USBH_ASSERT(DeviceExtensionHub->PendingWakeIrp == NULL);
  1588. IoReleaseCancelSpinLock(irql);
  1589. }
  1590. VOID
  1591. USBH_HubCompleteQueuedPortWakeIrps(
  1592. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1593. IN PLIST_ENTRY IrpsToComplete,
  1594. IN NTSTATUS NtStatus
  1595. )
  1596. /*++
  1597. Routine Description:
  1598. Called to complete all the pending child port WW IRPs in the given
  1599. private queue.
  1600. Arguments:
  1601. Return Value:
  1602. The function value is the final status from the operation.
  1603. --*/
  1604. {
  1605. PIRP irp;
  1606. PLIST_ENTRY listEntry;
  1607. while (!IsListEmpty(IrpsToComplete)) {
  1608. listEntry = RemoveHeadList(IrpsToComplete);
  1609. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  1610. USBH_KdPrint((1,"'Signaling WaitWake IRP (%x)\n", irp));
  1611. USBH_CompletePowerIrp(DeviceExtensionHub, irp, NtStatus);
  1612. }
  1613. }
  1614. VOID
  1615. USBH_HubCompletePortWakeIrps(
  1616. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1617. IN NTSTATUS NtStatus
  1618. )
  1619. /*++
  1620. Routine Description:
  1621. Called when a wake irp completes for a hub
  1622. Propagates the wake irp completion to all the ports.
  1623. Arguments:
  1624. DeviceExtensionHub
  1625. Return Value:
  1626. The function value is the final status from the operation.
  1627. --*/
  1628. {
  1629. LIST_ENTRY irpsToComplete;
  1630. LOGENTRY(LOG_PNP, "pWWc", DeviceExtensionHub, NtStatus, 0);
  1631. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_NEED_CLEANUP)) {
  1632. // Hub has already been removed and child WW IRP's should have already
  1633. // been completed.
  1634. LOGENTRY(LOG_PNP, "WWcl", DeviceExtensionHub, 0, 0);
  1635. return;
  1636. }
  1637. USBH_HubQueuePortWakeIrps(DeviceExtensionHub, &irpsToComplete);
  1638. // Ok, we have queued all the port wake IRPs and have released the
  1639. // cancel spinlock. Let's complete all the IRPs.
  1640. USBH_HubCompleteQueuedPortWakeIrps(DeviceExtensionHub, &irpsToComplete,
  1641. NtStatus);
  1642. }
  1643. VOID
  1644. USBH_HubQueuePortIdleIrps(
  1645. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1646. IN PLIST_ENTRY IrpsToComplete
  1647. )
  1648. /*++
  1649. Routine Description:
  1650. Called to queue all the pending child port Idle IRPs of a given
  1651. hub into a private queue.
  1652. Arguments:
  1653. Return Value:
  1654. The function value is the final status from the operation.
  1655. --*/
  1656. {
  1657. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  1658. PUSB_HUB_DESCRIPTOR hubDescriptor;
  1659. PPORT_DATA portData;
  1660. PIRP irp;
  1661. PDRIVER_CANCEL oldCancelRoutine;
  1662. KIRQL irql;
  1663. ULONG numberOfPorts, i;
  1664. hubDescriptor = DeviceExtensionHub->HubDescriptor;
  1665. USBH_ASSERT(NULL != hubDescriptor);
  1666. numberOfPorts = hubDescriptor->bNumberOfPorts;
  1667. InitializeListHead(IrpsToComplete);
  1668. // First, queue all the port idle IRPs into a local list while
  1669. // the cancel spinlock is held. This will prevent new WW IRPs for
  1670. // these ports from being submitted while we are traversing the list.
  1671. // Once we have queued them all we will release the spinlock (because
  1672. // the list no longer needs protection), then complete the IRPs.
  1673. IoAcquireCancelSpinLock(&irql);
  1674. for (i=0; i<numberOfPorts; i++) {
  1675. portData = &DeviceExtensionHub->PortData[i];
  1676. if (portData->DeviceObject) {
  1677. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  1678. irp = deviceExtensionPort->IdleNotificationIrp;
  1679. deviceExtensionPort->IdleNotificationIrp = NULL;
  1680. // Complete the Idle IRP if we have one.
  1681. if (irp) {
  1682. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  1683. if (oldCancelRoutine) {
  1684. deviceExtensionPort->PortPdoFlags &= ~PORTPDO_IDLE_NOTIFIED;
  1685. InsertTailList(IrpsToComplete, &irp->Tail.Overlay.ListEntry);
  1686. }
  1687. #if DBG
  1688. else {
  1689. //
  1690. // The IRP was cancelled and the cancel routine was called.
  1691. // The cancel routine will dequeue and complete the IRP,
  1692. // so don't do it here.
  1693. USBH_ASSERT(irp->Cancel);
  1694. }
  1695. #endif
  1696. }
  1697. }
  1698. }
  1699. if (DeviceExtensionHub->HubFlags & HUBFLAG_PENDING_IDLE_IRP) {
  1700. irp = DeviceExtensionHub->PendingIdleIrp;
  1701. DeviceExtensionHub->PendingIdleIrp = NULL;
  1702. } else {
  1703. irp = NULL;
  1704. ASSERT(!DeviceExtensionHub->PendingIdleIrp);
  1705. }
  1706. IoReleaseCancelSpinLock(irql);
  1707. if (irp) {
  1708. USBH_HubCancelIdleIrp(DeviceExtensionHub, irp);
  1709. }
  1710. }
  1711. VOID
  1712. USBH_HubCompleteQueuedPortIdleIrps(
  1713. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1714. IN PLIST_ENTRY IrpsToComplete,
  1715. IN NTSTATUS NtStatus
  1716. )
  1717. /*++
  1718. Routine Description:
  1719. Called to complete all the pending child port Idle IRPs in the given
  1720. private queue.
  1721. Arguments:
  1722. Return Value:
  1723. The function value is the final status from the operation.
  1724. --*/
  1725. {
  1726. PIRP irp;
  1727. PLIST_ENTRY listEntry;
  1728. while (!IsListEmpty(IrpsToComplete)) {
  1729. listEntry = RemoveHeadList(IrpsToComplete);
  1730. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  1731. USBH_KdPrint((1,"'Completing port Idle IRP (%x)\n", irp));
  1732. irp->IoStatus.Status = NtStatus;
  1733. IoCompleteRequest(irp, IO_NO_INCREMENT);
  1734. }
  1735. }
  1736. VOID
  1737. USBH_HubCompletePortIdleIrps(
  1738. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1739. IN NTSTATUS NtStatus
  1740. )
  1741. /*++
  1742. Routine Description:
  1743. Complete all the Idle IRPs for the given hub.
  1744. Arguments:
  1745. DeviceExtensionHub
  1746. Return Value:
  1747. The function value is the final status from the operation.
  1748. --*/
  1749. {
  1750. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  1751. PUSB_HUB_DESCRIPTOR hubDescriptor;
  1752. PPORT_DATA portData;
  1753. PIRP irp;
  1754. PDRIVER_CANCEL oldCancelRoutine;
  1755. LIST_ENTRY irpsToComplete;
  1756. PLIST_ENTRY listEntry;
  1757. KIRQL irql;
  1758. ULONG numberOfPorts, i;
  1759. LOGENTRY(LOG_PNP, "pIIc", DeviceExtensionHub, NtStatus, 0);
  1760. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_NEED_CLEANUP)) {
  1761. // Hub has already been removed and child Idle IRP's should have already
  1762. // been completed.
  1763. return;
  1764. }
  1765. USBH_HubQueuePortIdleIrps(DeviceExtensionHub, &irpsToComplete);
  1766. // Ok, we have queued all the port idle IRPs and have released the
  1767. // cancel spinlock. Let's complete all the IRPs.
  1768. USBH_HubCompleteQueuedPortIdleIrps(DeviceExtensionHub, &irpsToComplete,
  1769. NtStatus);
  1770. }
  1771. VOID
  1772. USBH_HubCancelWakeIrp(
  1773. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1774. IN PIRP Irp
  1775. )
  1776. /*++
  1777. Routine Description:
  1778. Called to cancel the pending WaitWake IRP for a hub.
  1779. This routine safely cancels the IRP. Note that the pending WaitWake
  1780. IRP pointer in the hub's device extension should have been already
  1781. cleared before calling this function.
  1782. Arguments:
  1783. Irp - Irp to cancel.
  1784. Return Value:
  1785. --*/
  1786. {
  1787. IoCancelIrp(Irp);
  1788. if (InterlockedExchange(&DeviceExtensionHub->WaitWakeIrpCancelFlag, 1)) {
  1789. // This IRP has been completed on another thread and the other thread
  1790. // did not complete the IRP. So, we must complete it here.
  1791. //
  1792. // Note that we do not use USBH_CompletePowerIrp as the hub's pending
  1793. // I/O counter was already decremented on the other thread in the
  1794. // completion routine.
  1795. PoStartNextPowerIrp(Irp);
  1796. Irp->IoStatus.Status = STATUS_CANCELLED;
  1797. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  1798. }
  1799. }
  1800. VOID
  1801. USBH_HubCancelIdleIrp(
  1802. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1803. IN PIRP Irp
  1804. )
  1805. /*++
  1806. Routine Description:
  1807. Called to cancel the pending Idle IRP for a hub.
  1808. This routine safely cancels the IRP. Note that the pending Idle
  1809. IRP pointer in the hub's device extension should have been already
  1810. cleared before calling this function.
  1811. Arguments:
  1812. Irp - Irp to cancel.
  1813. Return Value:
  1814. --*/
  1815. {
  1816. IoCancelIrp(Irp);
  1817. if (InterlockedExchange(&DeviceExtensionHub->IdleIrpCancelFlag, 1)) {
  1818. // This IRP has been completed on another thread and the other thread
  1819. // did not free the IRP. So, we must free it here.
  1820. IoFreeIrp(Irp);
  1821. }
  1822. }
  1823. NTSTATUS
  1824. USBH_FdoPoRequestD0Completion(
  1825. IN PDEVICE_OBJECT DeviceObject,
  1826. IN UCHAR MinorFunction,
  1827. IN POWER_STATE PowerState,
  1828. IN PVOID Context,
  1829. IN PIO_STATUS_BLOCK IoStatus
  1830. )
  1831. /*++
  1832. Routine Description:
  1833. Called when the hub has entered D0 as a result of a
  1834. wake irp completeing
  1835. Arguments:
  1836. DeviceObject - Pointer to the device object for the class device.
  1837. Irp - Irp completed.
  1838. Context - Driver defined context.
  1839. Return Value:
  1840. The function value is the final status from the operation.
  1841. --*/
  1842. {
  1843. NTSTATUS ntStatus;
  1844. PDEVICE_EXTENSION_HUB deviceExtensionHub = Context;
  1845. ntStatus = IoStatus->Status;
  1846. USBH_KdPrint((1,"'WaitWake D0 completion(%x) for HUB VID %x, PID %x\n",
  1847. ntStatus,
  1848. deviceExtensionHub->DeviceDescriptor.idVendor, \
  1849. deviceExtensionHub->DeviceDescriptor.idProduct));
  1850. LOGENTRY(LOG_PNP, "hWD0", deviceExtensionHub,
  1851. deviceExtensionHub->PendingWakeIrp,
  1852. 0);
  1853. // Since we can't easily determine which ports are asserting resume
  1854. // signalling we complete the WW IRPs for all of them.
  1855. //
  1856. // Ken says that we will need to determine what caused the hub WW
  1857. // to complete and then only complete the WW Irp for that port, if any.
  1858. // It is possible for more than one port to assert WW (e.g. user bumped
  1859. // the mouse at the same time a pressing a key), and it is also possible
  1860. // for a port with no device to have caused the hub WW to complete (e.g.
  1861. // device insertion or removal).
  1862. USBH_HubCompletePortWakeIrps(deviceExtensionHub, STATUS_SUCCESS);
  1863. // Ok to idle hub again.
  1864. deviceExtensionHub->HubFlags &= ~HUBFLAG_WW_SET_D0_PENDING;
  1865. // Also ok to remove hub.
  1866. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1867. return ntStatus;
  1868. }
  1869. NTSTATUS
  1870. USBH_FdoWaitWakeIrpCompletion(
  1871. IN PDEVICE_OBJECT DeviceObject,
  1872. IN UCHAR MinorFunction,
  1873. IN POWER_STATE PowerState,
  1874. IN PVOID Context,
  1875. IN PIO_STATUS_BLOCK IoStatus
  1876. )
  1877. /*++
  1878. Routine Description:
  1879. Called when a wake irp completes for a hub
  1880. Arguments:
  1881. DeviceObject - Pointer to the device object for the class device.
  1882. Irp - Irp completed.
  1883. Context - Driver defined context.
  1884. Return Value:
  1885. The function value is the final status from the operation.
  1886. --*/
  1887. {
  1888. NTSTATUS ntStatus;
  1889. ntStatus = IoStatus->Status;
  1890. return ntStatus;
  1891. }
  1892. NTSTATUS
  1893. USBH_FdoWWIrpIoCompletion(
  1894. IN PDEVICE_OBJECT DeviceObject,
  1895. IN PIRP Irp,
  1896. IN PVOID Context
  1897. )
  1898. /*++
  1899. Routine Description:
  1900. This is the IoCompletionRoutine for the WW IRP for the hub, not to be
  1901. confused with the PoRequestCompletionRoutine.
  1902. Arguments:
  1903. DeviceObject - Pointer to the device object for the class device.
  1904. Irp - Irp completed.
  1905. Context - Driver defined context.
  1906. Return Value:
  1907. The function value is the final status from the operation.
  1908. --*/
  1909. {
  1910. PUSBH_COMPLETE_PORT_IRPS_WORK_ITEM workItemCompletePortIrps;
  1911. NTSTATUS ntStatus;
  1912. PDEVICE_EXTENSION_HUB deviceExtensionHub = Context;
  1913. POWER_STATE powerState;
  1914. KIRQL irql;
  1915. PIRP irp;
  1916. ntStatus = Irp->IoStatus.Status;
  1917. USBH_KdPrint((1,"'WaitWake completion(%x) for HUB VID %x, PID %x\n",
  1918. ntStatus,
  1919. deviceExtensionHub->DeviceDescriptor.idVendor, \
  1920. deviceExtensionHub->DeviceDescriptor.idProduct));
  1921. LOGENTRY(LOG_PNP, "hWWc", deviceExtensionHub,
  1922. ntStatus,
  1923. deviceExtensionHub->PendingWakeIrp);
  1924. // We have to clear the PendingWakeIrp pointer here because in the case
  1925. // where a device is unplugged between here and when the port loop is
  1926. // processed in HubCompletePortWakeIrps, we will miss one of the port
  1927. // WW IRP's, NumberPortWakeIrps will not decrement to zero, and we will
  1928. // not clear the PendingWakeIrp pointer. This is bad because the IRP
  1929. // has been completed and the pointer is no longer valid.
  1930. //
  1931. // Hopefully the WW IRP for the port will be completed and
  1932. // NumberPortWakeIrps adjusted properly when the device processes MN_REMOVE.
  1933. //
  1934. // BUT: Make sure that we have a PendingWakeIrp first before clearing
  1935. // because it may have already been cleared when the last port WW was
  1936. // canceled in USBH_WaitWakeCancel.
  1937. IoAcquireCancelSpinLock(&irql);
  1938. // We clear the flag regardless of whether PendingWakeIrp is present or
  1939. // not because if the WW IRP request in FdoSubmitWaitWakeIrp fails
  1940. // immediately, PendingWakeIrp will be NULL.
  1941. deviceExtensionHub->HubFlags &= ~HUBFLAG_PENDING_WAKE_IRP;
  1942. irp = InterlockedExchangePointer(&deviceExtensionHub->PendingWakeIrp, NULL);
  1943. // deref the hub, no wake irp is pending
  1944. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1945. IoReleaseCancelSpinLock(irql);
  1946. if (NT_SUCCESS(ntStatus)) {
  1947. //
  1948. // this means that either we were the source for
  1949. // the wakeup or a device attached to one of our
  1950. // ports is.
  1951. //
  1952. // our mission now is to discover what caused the
  1953. // wakeup
  1954. //
  1955. USBH_KdPrint((1,"'Remote Wakeup Detected for HUB VID %x, PID %x\n",
  1956. deviceExtensionHub->DeviceDescriptor.idVendor, \
  1957. deviceExtensionHub->DeviceDescriptor.idProduct));
  1958. // Prevent idling hub until this Set D0 request completes.
  1959. deviceExtensionHub->HubFlags |= HUBFLAG_WW_SET_D0_PENDING;
  1960. // Also prevent hub from being removed before Set D0 is complete.
  1961. USBH_INC_PENDING_IO_COUNT(deviceExtensionHub);
  1962. powerState.DeviceState = PowerDeviceD0;
  1963. // first we need to power up the hub
  1964. PoRequestPowerIrp(deviceExtensionHub->PhysicalDeviceObject,
  1965. IRP_MN_SET_POWER,
  1966. powerState,
  1967. USBH_FdoPoRequestD0Completion,
  1968. deviceExtensionHub,
  1969. NULL);
  1970. ntStatus = STATUS_SUCCESS;
  1971. } else {
  1972. // We complete the port Wake IRPs in a workitem on another
  1973. // thread so that we don't fail a new Wake IRP for the hub
  1974. // which might arrive in the same context, before we've
  1975. // finished completing the old one.
  1976. workItemCompletePortIrps = UsbhExAllocatePool(NonPagedPool,
  1977. sizeof(USBH_COMPLETE_PORT_IRPS_WORK_ITEM));
  1978. if (workItemCompletePortIrps) {
  1979. workItemCompletePortIrps->DeviceExtensionHub = deviceExtensionHub;
  1980. workItemCompletePortIrps->ntStatus = ntStatus;
  1981. USBH_HubQueuePortWakeIrps(deviceExtensionHub,
  1982. &workItemCompletePortIrps->IrpsToComplete);
  1983. ExInitializeWorkItem(&workItemCompletePortIrps->WorkQueueItem,
  1984. USBH_CompletePortWakeIrpsWorker,
  1985. workItemCompletePortIrps);
  1986. LOGENTRY(LOG_PNP, "wITM", deviceExtensionHub,
  1987. &workItemCompletePortIrps->WorkQueueItem, 0);
  1988. USBH_INC_PENDING_IO_COUNT(deviceExtensionHub);
  1989. // critical saves time on resume
  1990. ExQueueWorkItem(&workItemCompletePortIrps->WorkQueueItem,
  1991. CriticalWorkQueue);
  1992. // The WorkItem is freed by USBH_CompletePortWakeIrpsWorker()
  1993. // Don't try to access the WorkItem after it is queued.
  1994. }
  1995. }
  1996. if (!irp) {
  1997. // If we have no IRP here this means that another thread wants to
  1998. // cancel the IRP. Handle accordingly.
  1999. if (!InterlockedExchange(&deviceExtensionHub->WaitWakeIrpCancelFlag, 1)) {
  2000. // We got the cancel flag before the other thread did. Hold
  2001. // on to the IRP here and let the cancel routine complete it.
  2002. ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
  2003. }
  2004. }
  2005. IoMarkIrpPending(Irp);
  2006. if (ntStatus != STATUS_MORE_PROCESSING_REQUIRED) {
  2007. PoStartNextPowerIrp(Irp);
  2008. }
  2009. return ntStatus;
  2010. }
  2011. NTSTATUS
  2012. USBH_FdoSubmitWaitWakeIrp(
  2013. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  2014. )
  2015. /*++
  2016. Routine Description:
  2017. called when a child Pdo is enabled for wakeup, this
  2018. function allocates a wait wake irp and passes it to
  2019. the parents PDO.
  2020. Arguments:
  2021. Return Value:
  2022. --*/
  2023. {
  2024. PIRP irp;
  2025. KIRQL irql;
  2026. NTSTATUS ntStatus;
  2027. POWER_STATE powerState;
  2028. USBH_ASSERT(DeviceExtensionHub->PendingWakeIrp == NULL);
  2029. USBH_KdPrint((1,"'USBH_FdoSubmitWaitWakeIrp (%x)\n", DeviceExtensionHub));
  2030. LOGENTRY(LOG_PNP, "hWW_", DeviceExtensionHub, 0, 0);
  2031. powerState.DeviceState = DeviceExtensionHub->SystemWake;
  2032. DeviceExtensionHub->HubFlags |= HUBFLAG_PENDING_WAKE_IRP;
  2033. USBH_INC_PENDING_IO_COUNT(DeviceExtensionHub);
  2034. InterlockedExchange(&DeviceExtensionHub->WaitWakeIrpCancelFlag, 0);
  2035. ntStatus = PoRequestPowerIrp(DeviceExtensionHub->PhysicalDeviceObject,
  2036. IRP_MN_WAIT_WAKE,
  2037. powerState,
  2038. USBH_FdoWaitWakeIrpCompletion,
  2039. DeviceExtensionHub,
  2040. &irp);
  2041. USBH_ASSERT(ntStatus == STATUS_PENDING);
  2042. IoAcquireCancelSpinLock(&irql);
  2043. if (ntStatus == STATUS_PENDING) {
  2044. // Must check flag here because in the case where the WW IRP failed
  2045. // immediately, this flag will be cleared in the completion routine
  2046. // and if that happens we don't want to save this IRP because it
  2047. // will soon be invalid if it isn't already.
  2048. if (DeviceExtensionHub->HubFlags & HUBFLAG_PENDING_WAKE_IRP) {
  2049. // Successfully posted a Wake IRP.
  2050. // This hub is now enabled for wakeup.
  2051. LOGENTRY(LOG_PNP, "hWW+", DeviceExtensionHub, irp, 0);
  2052. DeviceExtensionHub->PendingWakeIrp = irp;
  2053. }
  2054. } else {
  2055. USBH_ASSERT(FALSE); // Want to know if we ever hit this.
  2056. DeviceExtensionHub->HubFlags &= ~HUBFLAG_PENDING_WAKE_IRP;
  2057. USBH_DEC_PENDING_IO_COUNT(DeviceExtensionHub);
  2058. }
  2059. IoReleaseCancelSpinLock(irql);
  2060. return ntStatus;
  2061. }
  2062. VOID
  2063. USBH_FdoIdleNotificationCallback(
  2064. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  2065. )
  2066. /*++
  2067. Routine Description:
  2068. Called when it is time to idle out the hub device itself.
  2069. Arguments:
  2070. Return Value:
  2071. --*/
  2072. {
  2073. PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
  2074. PDEVICE_EXTENSION_PORT childDeviceExtensionPort;
  2075. KIRQL irql;
  2076. PIRP idleIrp;
  2077. PIRP irpToCancel = NULL;
  2078. POWER_STATE powerState;
  2079. NTSTATUS ntStatus;
  2080. ULONG i;
  2081. BOOLEAN bIdleOk = TRUE;
  2082. LOGENTRY(LOG_PNP, "hId!", DeviceExtensionHub, DeviceExtensionHub->HubFlags, 0);
  2083. USBH_KdPrint((1,"'Hub %x going idle!\n", DeviceExtensionHub));
  2084. if (DeviceExtensionHub->HubFlags &
  2085. (HUBFLAG_DEVICE_STOPPING |
  2086. HUBFLAG_HUB_GONE |
  2087. HUBFLAG_HUB_FAILURE |
  2088. HUBFLAG_CHILD_DELETES_PENDING |
  2089. HUBFLAG_WW_SET_D0_PENDING |
  2090. HUBFLAG_POST_ESD_ENUM_PENDING |
  2091. HUBFLAG_HUB_HAS_LOST_BRAINS)) {
  2092. // Don't idle this hub if it was just disconnected or otherwise
  2093. // being stopped.
  2094. LOGENTRY(LOG_PNP, "hId.", DeviceExtensionHub, DeviceExtensionHub->HubFlags, 0);
  2095. USBH_KdPrint((1,"'Hub %x being stopped, in low power, etc., abort idle\n", DeviceExtensionHub));
  2096. return;
  2097. }
  2098. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_PENDING_WAKE_IRP)) {
  2099. // If there is not already a WW IRP pending for the hub, submit
  2100. // one now. This will ensure that the hub will wakeup on connect
  2101. // change events while it is suspended.
  2102. ntStatus = USBH_FdoSubmitWaitWakeIrp(DeviceExtensionHub);
  2103. if (ntStatus != STATUS_PENDING) {
  2104. LOGENTRY(LOG_PNP, "hIdX", DeviceExtensionHub, ntStatus, 0);
  2105. UsbhWarning(NULL,
  2106. "Could not post WW IRP for hub, aborting IDLE.\n",
  2107. FALSE);
  2108. return;
  2109. }
  2110. }
  2111. // Ensure that child port configuration does not change while in this
  2112. // function, i.e. don't allow QBR.
  2113. USBH_KdPrint((2,"'***WAIT reset device mutex %x\n", DeviceExtensionHub));
  2114. USBH_INC_PENDING_IO_COUNT(DeviceExtensionHub);
  2115. KeWaitForSingleObject(&DeviceExtensionHub->ResetDeviceMutex,
  2116. Executive,
  2117. KernelMode,
  2118. FALSE,
  2119. NULL);
  2120. USBH_KdPrint((2,"'***WAIT reset device mutex done %x\n", DeviceExtensionHub));
  2121. for (i = 0; i < DeviceExtensionHub->HubDescriptor->bNumberOfPorts; i++) {
  2122. if (DeviceExtensionHub->PortData[i].DeviceObject) {
  2123. USBH_KdPrint((1,"'idleCB child PDO %x\n", DeviceExtensionHub->PortData[i].DeviceObject));
  2124. childDeviceExtensionPort = DeviceExtensionHub->PortData[i].DeviceObject->DeviceExtension;
  2125. idleIrp = childDeviceExtensionPort->IdleNotificationIrp;
  2126. if (idleIrp) {
  2127. idleCallbackInfo = (PUSB_IDLE_CALLBACK_INFO)
  2128. IoGetCurrentIrpStackLocation(idleIrp)->\
  2129. Parameters.DeviceIoControl.Type3InputBuffer;
  2130. USBH_ASSERT(idleCallbackInfo && idleCallbackInfo->IdleCallback);
  2131. if (idleCallbackInfo && idleCallbackInfo->IdleCallback) {
  2132. // Here we actually call the driver's callback routine,
  2133. // telling the driver that it is OK to suspend their
  2134. // device now.
  2135. LOGENTRY(LOG_PNP, "IdCB", childDeviceExtensionPort,
  2136. idleCallbackInfo, idleCallbackInfo->IdleCallback);
  2137. USBH_KdPrint((1,"'FdoIdleNotificationCallback: Calling driver's idle callback routine! %x %x\n",
  2138. idleCallbackInfo, idleCallbackInfo->IdleCallback));
  2139. idleCallbackInfo->IdleCallback(idleCallbackInfo->IdleContext);
  2140. // Be sure that the child actually powered down.
  2141. // This is important in the case where the child is also
  2142. // a hub. Abort if the child aborted.
  2143. if (childDeviceExtensionPort->DeviceState == PowerDeviceD0) {
  2144. LOGENTRY(LOG_PNP, "IdAb", childDeviceExtensionPort,
  2145. idleCallbackInfo, idleCallbackInfo->IdleCallback);
  2146. USBH_KdPrint((1,"'FdoIdleNotificationCallback: Driver's idle callback routine did not power down! %x %x\n",
  2147. idleCallbackInfo, idleCallbackInfo->IdleCallback));
  2148. bIdleOk = FALSE;
  2149. break;
  2150. }
  2151. } else {
  2152. // No callback
  2153. bIdleOk = FALSE;
  2154. break;
  2155. }
  2156. } else {
  2157. // No Idle IRP
  2158. bIdleOk = FALSE;
  2159. break;
  2160. }
  2161. }
  2162. }
  2163. USBH_KdPrint((2,"'***RELEASE reset device mutex %x\n", DeviceExtensionHub));
  2164. KeReleaseSemaphore(&DeviceExtensionHub->ResetDeviceMutex,
  2165. LOW_REALTIME_PRIORITY,
  2166. 1,
  2167. FALSE);
  2168. USBH_DEC_PENDING_IO_COUNT(DeviceExtensionHub);
  2169. if (bIdleOk) {
  2170. // If all the port PDOs have been powered down,
  2171. // it is time to power down the hub.
  2172. powerState.DeviceState = DeviceExtensionHub->DeviceWake;
  2173. PoRequestPowerIrp(DeviceExtensionHub->PhysicalDeviceObject,
  2174. IRP_MN_SET_POWER,
  2175. powerState,
  2176. NULL,
  2177. NULL,
  2178. NULL);
  2179. } else {
  2180. // One or more of the port PDOs did not have an Idle IRP
  2181. // (i.e. it was just cancelled), or the Idle IRP did not have a
  2182. // callback function pointer. Abort this Idle procedure and cancel
  2183. // the Idle IRP to the hub.
  2184. LOGENTRY(LOG_PNP, "hIdA", DeviceExtensionHub, DeviceExtensionHub->HubFlags, 0);
  2185. USBH_KdPrint((1,"'Aborting Idle for Hub %x\n", DeviceExtensionHub));
  2186. IoAcquireCancelSpinLock(&irql);
  2187. if (DeviceExtensionHub && DeviceExtensionHub->PendingIdleIrp) {
  2188. irpToCancel = DeviceExtensionHub->PendingIdleIrp;
  2189. DeviceExtensionHub->PendingIdleIrp = NULL;
  2190. }
  2191. IoReleaseCancelSpinLock(irql);
  2192. // Cancel the Idle request to the hub if there is one.
  2193. if (irpToCancel) {
  2194. USBH_HubCancelIdleIrp(DeviceExtensionHub, irpToCancel);
  2195. }
  2196. USBH_HubCompletePortIdleIrps(DeviceExtensionHub, STATUS_CANCELLED);
  2197. }
  2198. }
  2199. VOID
  2200. USBH_IdleCompletePowerHubWorker(
  2201. IN PVOID Context)
  2202. /* ++
  2203. *
  2204. * Description:
  2205. *
  2206. * Work item scheduled to power up a hub on completion of an Idle request
  2207. * for the hub.
  2208. *
  2209. *
  2210. * Arguments:
  2211. *
  2212. * Return:
  2213. *
  2214. * -- */
  2215. {
  2216. PUSBH_HUB_IDLE_POWER_WORK_ITEM workItemIdlePower;
  2217. PAGED_CODE();
  2218. workItemIdlePower = Context;
  2219. USBH_HubSetD0(workItemIdlePower->DeviceExtensionHub);
  2220. USBH_HubCompletePortIdleIrps(workItemIdlePower->DeviceExtensionHub,
  2221. workItemIdlePower->ntStatus);
  2222. USBH_DEC_PENDING_IO_COUNT(workItemIdlePower->DeviceExtensionHub);
  2223. UsbhExFreePool(workItemIdlePower);
  2224. }
  2225. VOID
  2226. USBH_CompletePortIdleIrpsWorker(
  2227. IN PVOID Context)
  2228. /* ++
  2229. *
  2230. * Description:
  2231. *
  2232. * Work item scheduled to complete the child port Idle IRPs
  2233. * for the hub.
  2234. *
  2235. *
  2236. * Arguments:
  2237. *
  2238. * Return:
  2239. *
  2240. * -- */
  2241. {
  2242. PUSBH_COMPLETE_PORT_IRPS_WORK_ITEM workItemCompletePortIrps;
  2243. PAGED_CODE();
  2244. workItemCompletePortIrps = Context;
  2245. USBH_HubCompleteQueuedPortIdleIrps(
  2246. workItemCompletePortIrps->DeviceExtensionHub,
  2247. &workItemCompletePortIrps->IrpsToComplete,
  2248. workItemCompletePortIrps->ntStatus);
  2249. USBH_DEC_PENDING_IO_COUNT(workItemCompletePortIrps->DeviceExtensionHub);
  2250. UsbhExFreePool(workItemCompletePortIrps);
  2251. }
  2252. VOID
  2253. USBH_CompletePortWakeIrpsWorker(
  2254. IN PVOID Context)
  2255. /* ++
  2256. *
  2257. * Description:
  2258. *
  2259. * Work item scheduled to complete the child port Idle IRPs
  2260. * for the hub.
  2261. *
  2262. *
  2263. * Arguments:
  2264. *
  2265. * Return:
  2266. *
  2267. * -- */
  2268. {
  2269. PUSBH_COMPLETE_PORT_IRPS_WORK_ITEM workItemCompletePortIrps;
  2270. PAGED_CODE();
  2271. workItemCompletePortIrps = Context;
  2272. USBH_HubCompleteQueuedPortWakeIrps(
  2273. workItemCompletePortIrps->DeviceExtensionHub,
  2274. &workItemCompletePortIrps->IrpsToComplete,
  2275. workItemCompletePortIrps->ntStatus);
  2276. USBH_DEC_PENDING_IO_COUNT(workItemCompletePortIrps->DeviceExtensionHub);
  2277. UsbhExFreePool(workItemCompletePortIrps);
  2278. }
  2279. NTSTATUS
  2280. USBH_FdoIdleNotificationRequestComplete(
  2281. PDEVICE_OBJECT DeviceObject,
  2282. PIRP Irp,
  2283. PDEVICE_EXTENSION_HUB DeviceExtensionHub
  2284. )
  2285. /*++
  2286. Routine Description:
  2287. Completion routine for the Idle request IRP for the hub device.
  2288. Arguments:
  2289. Return Value:
  2290. --*/
  2291. {
  2292. PUSBH_HUB_IDLE_POWER_WORK_ITEM workItemIdlePower;
  2293. PUSBH_COMPLETE_PORT_IRPS_WORK_ITEM workItemCompletePortIrps;
  2294. NTSTATUS ntStatus;
  2295. KIRQL irql;
  2296. PIRP irp;
  2297. BOOLEAN bHoldIrp = FALSE;
  2298. //
  2299. // DeviceObject is NULL because we sent the irp
  2300. //
  2301. UNREFERENCED_PARAMETER(DeviceObject);
  2302. LOGENTRY(LOG_PNP, "hIdC", DeviceExtensionHub, Irp, Irp->IoStatus.Status);
  2303. USBH_KdPrint((1,"'Idle notification IRP for hub %x completed %x\n",
  2304. DeviceExtensionHub, Irp->IoStatus.Status));
  2305. USBH_ASSERT(Irp->IoStatus.Status != STATUS_DEVICE_BUSY);
  2306. IoAcquireCancelSpinLock(&irql);
  2307. irp = InterlockedExchangePointer(&DeviceExtensionHub->PendingIdleIrp, NULL);
  2308. DeviceExtensionHub->HubFlags &= ~HUBFLAG_PENDING_IDLE_IRP;
  2309. USBH_DEC_PENDING_IO_COUNT(DeviceExtensionHub);
  2310. IoReleaseCancelSpinLock(irql);
  2311. ntStatus = Irp->IoStatus.Status;
  2312. // Complete port Idle IRPs w/error if hub Idle IRP failed.
  2313. //
  2314. // Skip this if the hub is stopping or has been removed as HubDescriptor
  2315. // might have already been freed and FdoCleanup will complete these anyway.
  2316. if (!NT_SUCCESS(ntStatus) && (ntStatus != STATUS_POWER_STATE_INVALID) &&
  2317. !(DeviceExtensionHub->HubFlags & (HUBFLAG_HUB_GONE | HUBFLAG_HUB_STOPPED))) {
  2318. if (DeviceExtensionHub->CurrentPowerState != PowerDeviceD0) {
  2319. // Since we are at DPC we must use a work item to power up the
  2320. // hub synchronously, because that function yields and we can't
  2321. // yield at DPC level.
  2322. workItemIdlePower = UsbhExAllocatePool(NonPagedPool,
  2323. sizeof(USBH_HUB_IDLE_POWER_WORK_ITEM));
  2324. if (workItemIdlePower) {
  2325. workItemIdlePower->DeviceExtensionHub = DeviceExtensionHub;
  2326. workItemIdlePower->ntStatus = ntStatus;
  2327. ExInitializeWorkItem(&workItemIdlePower->WorkQueueItem,
  2328. USBH_IdleCompletePowerHubWorker,
  2329. workItemIdlePower);
  2330. LOGENTRY(LOG_PNP, "iITM", DeviceExtensionHub,
  2331. &workItemIdlePower->WorkQueueItem, 0);
  2332. USBH_INC_PENDING_IO_COUNT(DeviceExtensionHub);
  2333. ExQueueWorkItem(&workItemIdlePower->WorkQueueItem,
  2334. DelayedWorkQueue);
  2335. // The WorkItem is freed by USBH_IdleCompletePowerHubWorker()
  2336. // Don't try to access the WorkItem after it is queued.
  2337. }
  2338. } else {
  2339. // We complete the port Idle IRPs in a workitem on another
  2340. // thread so that we don't fail a new Idle IRP for the hub
  2341. // which might arrive in the same context, before we've
  2342. // finished completing the old one.
  2343. workItemCompletePortIrps = UsbhExAllocatePool(NonPagedPool,
  2344. sizeof(USBH_COMPLETE_PORT_IRPS_WORK_ITEM));
  2345. if (workItemCompletePortIrps) {
  2346. workItemCompletePortIrps->DeviceExtensionHub = DeviceExtensionHub;
  2347. workItemCompletePortIrps->ntStatus = ntStatus;
  2348. USBH_HubQueuePortIdleIrps(DeviceExtensionHub,
  2349. &workItemCompletePortIrps->IrpsToComplete);
  2350. ExInitializeWorkItem(&workItemCompletePortIrps->WorkQueueItem,
  2351. USBH_CompletePortIdleIrpsWorker,
  2352. workItemCompletePortIrps);
  2353. LOGENTRY(LOG_PNP, "iIT2", DeviceExtensionHub,
  2354. &workItemCompletePortIrps->WorkQueueItem, 0);
  2355. USBH_INC_PENDING_IO_COUNT(DeviceExtensionHub);
  2356. ExQueueWorkItem(&workItemCompletePortIrps->WorkQueueItem,
  2357. DelayedWorkQueue);
  2358. // The WorkItem is freed by USBH_CompletePortIdleIrpsWorker()
  2359. // Don't try to access the WorkItem after it is queued.
  2360. }
  2361. }
  2362. }
  2363. if (!irp) {
  2364. // If we have no IRP here this means that another thread wants to
  2365. // cancel the IRP. Handle accordingly.
  2366. if (!InterlockedExchange(&DeviceExtensionHub->IdleIrpCancelFlag, 1)) {
  2367. // We got the cancel flag before the other thread did. Hold
  2368. // on to the IRP here and let the cancel routine complete it.
  2369. bHoldIrp = TRUE;
  2370. }
  2371. }
  2372. // Since we allocated the IRP we must free it, but return
  2373. // STATUS_MORE_PROCESSING_REQUIRED so the kernel does not try to touch
  2374. // the IRP after we've freed it.
  2375. if (!bHoldIrp) {
  2376. IoFreeIrp(Irp);
  2377. }
  2378. return STATUS_MORE_PROCESSING_REQUIRED;
  2379. }
  2380. NTSTATUS
  2381. USBH_FdoSubmitIdleRequestIrp(
  2382. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  2383. )
  2384. /*++
  2385. Routine Description:
  2386. Called when all children PDO's are idled (or there are no children).
  2387. This function allocates an idle request IOCTL IRP and passes it to
  2388. the parent's PDO.
  2389. Arguments:
  2390. Return Value:
  2391. --*/
  2392. {
  2393. PIRP irp = NULL;
  2394. PIO_STACK_LOCATION nextStack;
  2395. KIRQL irql;
  2396. NTSTATUS ntStatus;
  2397. USBH_KdPrint((1,"'USBH_FdoSubmitIdleRequestIrp %x\n", DeviceExtensionHub));
  2398. LOGENTRY(LOG_PNP, "hId_", DeviceExtensionHub, 0, 0);
  2399. USBH_ASSERT(DeviceExtensionHub->PendingIdleIrp == NULL);
  2400. if (DeviceExtensionHub->PendingIdleIrp) {
  2401. // Probably don't want to clear the flag here because an Idle IRP
  2402. // is pending.
  2403. LOGENTRY(LOG_PNP, "hIb_", DeviceExtensionHub, 0, 0);
  2404. KeSetEvent(&DeviceExtensionHub->SubmitIdleEvent, 1, FALSE);
  2405. return STATUS_DEVICE_BUSY;
  2406. }
  2407. DeviceExtensionHub->IdleCallbackInfo.IdleCallback = USBH_FdoIdleNotificationCallback;
  2408. DeviceExtensionHub->IdleCallbackInfo.IdleContext = (PVOID)DeviceExtensionHub;
  2409. irp = IoAllocateIrp(DeviceExtensionHub->PhysicalDeviceObject->StackSize,
  2410. FALSE);
  2411. if (irp == NULL) {
  2412. // Be sure to set the event and clear the flag on error before exiting.
  2413. DeviceExtensionHub->HubFlags &= ~HUBFLAG_PENDING_IDLE_IRP;
  2414. KeSetEvent(&DeviceExtensionHub->SubmitIdleEvent, 1, FALSE);
  2415. return STATUS_INSUFFICIENT_RESOURCES;
  2416. }
  2417. nextStack = IoGetNextIrpStackLocation(irp);
  2418. nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  2419. nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
  2420. nextStack->Parameters.DeviceIoControl.Type3InputBuffer = &DeviceExtensionHub->IdleCallbackInfo;
  2421. nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(struct _USB_IDLE_CALLBACK_INFO);
  2422. IoSetCompletionRoutine(irp,
  2423. USBH_FdoIdleNotificationRequestComplete,
  2424. DeviceExtensionHub,
  2425. TRUE,
  2426. TRUE,
  2427. TRUE);
  2428. USBH_INC_PENDING_IO_COUNT(DeviceExtensionHub);
  2429. InterlockedExchange(&DeviceExtensionHub->IdleIrpCancelFlag, 0);
  2430. ntStatus = IoCallDriver(DeviceExtensionHub->PhysicalDeviceObject, irp);
  2431. LOGENTRY(LOG_PNP, "hI>>", DeviceExtensionHub, ntStatus, 0);
  2432. IoAcquireCancelSpinLock(&irql);
  2433. if (ntStatus == STATUS_PENDING) {
  2434. // Must check flag here because in the case where the Idle IRP failed
  2435. // immediately, this flag will be cleared in the completion routine
  2436. // and if that happens we don't want to save this IRP because it
  2437. // will soon be invalid if it isn't already.
  2438. LOGENTRY(LOG_PNP, "hIpp", DeviceExtensionHub, irp, 0);
  2439. if (DeviceExtensionHub->HubFlags & HUBFLAG_PENDING_IDLE_IRP) {
  2440. // Successfully posted an Idle IRP.
  2441. LOGENTRY(LOG_PNP, "hId+", DeviceExtensionHub, irp, 0);
  2442. DeviceExtensionHub->PendingIdleIrp = irp;
  2443. }
  2444. }
  2445. IoReleaseCancelSpinLock(irql);
  2446. KeSetEvent(&DeviceExtensionHub->SubmitIdleEvent, 1, FALSE);
  2447. return ntStatus;
  2448. }
  2449. NTSTATUS
  2450. USBH_FdoPower(
  2451. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  2452. IN PIRP Irp,
  2453. IN UCHAR MinorFunction
  2454. )
  2455. /* ++
  2456. *
  2457. * Description:
  2458. *
  2459. * This function responds to IoControl PnPPower for the FDO. This function is
  2460. * synchronous.
  2461. *
  2462. * Arguments:
  2463. *
  2464. * DeviceExtensionHub - the FDO extension pIrp - the request packet
  2465. * MinorFunction - the minor function of the PnP Power request.
  2466. *
  2467. * Return:
  2468. *
  2469. * NTSTATUS
  2470. *
  2471. * -- */
  2472. {
  2473. PDEVICE_EXTENSION_HUB rootHubDevExt;
  2474. NTSTATUS ntStatus;
  2475. PDEVICE_OBJECT deviceObject;
  2476. PIO_STACK_LOCATION irpStack;
  2477. BOOLEAN allPDOsAreOff, bHubNeedsWW;
  2478. PPORT_DATA portData;
  2479. ULONG i, numberOfPorts;
  2480. KIRQL irql;
  2481. PIRP hubWaitWake = NULL;
  2482. POWER_STATE powerState;
  2483. irpStack = IoGetCurrentIrpStackLocation(Irp);
  2484. deviceObject = DeviceExtensionHub->FunctionalDeviceObject;
  2485. USBH_KdPrint((2,"'Power Request, FDO %x minor %x\n", deviceObject, MinorFunction));
  2486. switch (MinorFunction) {
  2487. //
  2488. // Pass it down to Pdo to handle these
  2489. //
  2490. case IRP_MN_SET_POWER:
  2491. //
  2492. // Hub is being asked to change power state
  2493. //
  2494. switch (irpStack->Parameters.Power.Type) {
  2495. case SystemPowerState:
  2496. {
  2497. POWER_STATE powerState;
  2498. LOGENTRY(LOG_PNP, "sysP", DeviceExtensionHub,
  2499. DeviceExtensionHub->FunctionalDeviceObject,
  2500. 0);
  2501. // Track the current system power state in the hub's device ext.
  2502. // Note that we only set this back to S0 (i.e. allow selective
  2503. // suspend once again) once the root hub is fully powered up.
  2504. if (irpStack->Parameters.Power.State.SystemState != PowerSystemWorking) {
  2505. LOGENTRY(LOG_PNP, "DSus", DeviceExtensionHub, 0, 0);
  2506. USBH_KdPrint((1,"'Selective Suspend disabled because system is suspending\n"));
  2507. rootHubDevExt = USBH_GetRootHubDevExt(DeviceExtensionHub);
  2508. rootHubDevExt->CurrentSystemPowerState =
  2509. irpStack->Parameters.Power.State.SystemState;
  2510. }
  2511. if (irpStack->Parameters.Power.State.SystemState ==
  2512. PowerSystemHibernate) {
  2513. DeviceExtensionHub->HubFlags |= HUBFLAG_HIBER;
  2514. USBH_KdPrint((1, "'Hibernate Detected\n"));
  2515. //TEST_TRAP();
  2516. }
  2517. // map the system state to the appropriate D state.
  2518. // our policy is:
  2519. // if we are enabled for wakeup -- go to D2
  2520. // else go to D3
  2521. USBH_KdPrint(
  2522. (1, "'IRP_MJ_POWER HU fdo(%x) MN_SET_POWER(SystemPowerState S%x)\n",
  2523. DeviceExtensionHub->FunctionalDeviceObject,
  2524. irpStack->Parameters.Power.State.SystemState - 1));
  2525. //
  2526. // walk are list of PDOs, if all are in D3 yje set the
  2527. // allPDOsAreOff flag
  2528. allPDOsAreOff = TRUE;
  2529. portData = DeviceExtensionHub->PortData;
  2530. //
  2531. // NOTE: if we are stopped the HubDescriptor will be NULL
  2532. //
  2533. if (portData &&
  2534. DeviceExtensionHub->HubDescriptor) {
  2535. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  2536. for (i=0; i < numberOfPorts; i++) {
  2537. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  2538. LOGENTRY(LOG_PNP, "cPRT", portData->DeviceObject,
  2539. 0,
  2540. 0);
  2541. if (portData->DeviceObject) {
  2542. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  2543. if (deviceExtensionPort->DeviceState != PowerDeviceD3) {
  2544. allPDOsAreOff = FALSE;
  2545. break;
  2546. }
  2547. }
  2548. portData++;
  2549. }
  2550. #if DBG
  2551. // if all PDOs are in D3 then this means the hub itself is a
  2552. // wakeup source
  2553. if (DeviceExtensionHub->HubFlags & HUBFLAG_PENDING_WAKE_IRP) {
  2554. if (allPDOsAreOff) {
  2555. USBH_KdPrint(
  2556. (1, "'**Hub enabled for wakeup -- hub is only potential wakeup source\n"));
  2557. } else {
  2558. USBH_KdPrint(
  2559. (1, "'**Hub enabled for wakeup -- device is potential wakeup source\n"));
  2560. }
  2561. }
  2562. #endif
  2563. }
  2564. if (irpStack->Parameters.Power.State.SystemState == PowerSystemWorking) {
  2565. //
  2566. // go to ON
  2567. //
  2568. powerState.DeviceState = PowerDeviceD0;
  2569. LOGENTRY(LOG_PNP, "syON", 0,
  2570. 0,
  2571. 0);
  2572. } else if ((DeviceExtensionHub->HubFlags &
  2573. HUBFLAG_PENDING_WAKE_IRP) ||
  2574. !allPDOsAreOff) {
  2575. //
  2576. // based on the system power state
  2577. // request a setting to the appropriate
  2578. // Dx state.
  2579. //
  2580. // all low power states have already been mapped
  2581. // to suspend
  2582. powerState.DeviceState =
  2583. DeviceExtensionHub->DeviceState[irpStack->Parameters.Power.State.SystemState];
  2584. //
  2585. // These tables should have already been fixed up by the root hub
  2586. // (usbd.sys) to not contain an entry of unspecified.
  2587. //
  2588. ASSERT (PowerDeviceUnspecified != powerState.DeviceState);
  2589. LOGENTRY(LOG_PNP, "syDX", powerState.DeviceState,
  2590. 0,
  2591. 0);
  2592. USBH_KdPrint((1,"'System state maps to device state 0x%x (D%x)\n",
  2593. powerState.DeviceState,
  2594. powerState.DeviceState - 1));
  2595. } else {
  2596. powerState.DeviceState = PowerDeviceD3;
  2597. LOGENTRY(LOG_PNP, "syD3", powerState.DeviceState,
  2598. 0,
  2599. 0);
  2600. }
  2601. //
  2602. // only make the request if it is for a different power
  2603. // state then the one we are in, and it is a valid state for the
  2604. // request. Also, make sure the hub has been started.
  2605. //
  2606. LOGENTRY(LOG_PNP, "H>Sx", DeviceExtensionHub,
  2607. DeviceExtensionHub->FunctionalDeviceObject,
  2608. powerState.DeviceState);
  2609. if (powerState.DeviceState != PowerDeviceUnspecified &&
  2610. powerState.DeviceState != DeviceExtensionHub->CurrentPowerState &&
  2611. (DeviceExtensionHub->HubFlags & HUBFLAG_NEED_CLEANUP)) {
  2612. DeviceExtensionHub->PowerIrp = Irp;
  2613. IoMarkIrpPending(Irp);
  2614. ntStatus = PoRequestPowerIrp(DeviceExtensionHub->PhysicalDeviceObject,
  2615. IRP_MN_SET_POWER,
  2616. powerState,
  2617. USBH_FdoDeferPoRequestCompletion,
  2618. DeviceExtensionHub,
  2619. NULL);
  2620. USBH_KdPrint((2,"'PoRequestPowerIrp returned 0x%x\n", ntStatus));
  2621. // We need to return STATUS_PENDING here because we marked the
  2622. // IRP pending above with IoMarkIrpPending.
  2623. USBH_ASSERT(ntStatus == STATUS_PENDING);
  2624. // In the case where an allocation failed, PoRequestPowerIrp
  2625. // can return a status code other than STATUS_PENDING. In this
  2626. // case, we still need to pass the IRP down to the lower driver,
  2627. // but we still need to return STATUS_PENDING from this routine.
  2628. if (ntStatus != STATUS_PENDING) {
  2629. IoCopyCurrentIrpStackLocationToNext(Irp);
  2630. PoStartNextPowerIrp(Irp);
  2631. ntStatus = PoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  2632. Irp);
  2633. }
  2634. ntStatus = STATUS_PENDING;
  2635. } else {
  2636. IoCopyCurrentIrpStackLocationToNext(Irp);
  2637. PoStartNextPowerIrp(Irp);
  2638. ntStatus = PoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  2639. Irp);
  2640. }
  2641. }
  2642. break; //SystemPowerState
  2643. case DevicePowerState:
  2644. USBH_KdPrint(
  2645. (1, "'IRP_MJ_POWER HU fdo(%x) MN_SET_POWER(DevicePowerState D%x)\n",
  2646. DeviceExtensionHub->FunctionalDeviceObject,
  2647. irpStack->Parameters.Power.State.DeviceState - 1));
  2648. LOGENTRY(LOG_PNP, "H>Dx", DeviceExtensionHub,
  2649. DeviceExtensionHub->FunctionalDeviceObject,
  2650. irpStack->Parameters.Power.State.DeviceState);
  2651. // If we are already in the requested power state, or if this is
  2652. // a Set D0 request and we already have one pending,
  2653. // just pass the request on.
  2654. if ((DeviceExtensionHub->CurrentPowerState ==
  2655. irpStack->Parameters.Power.State.DeviceState) ||
  2656. (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0 &&
  2657. (DeviceExtensionHub->HubFlags & HUBFLAG_SET_D0_PENDING))) {
  2658. LOGENTRY(LOG_PNP, "HDxP", DeviceExtensionHub, 0, 0);
  2659. IoCopyCurrentIrpStackLocationToNext(Irp);
  2660. PoStartNextPowerIrp(Irp);
  2661. IoMarkIrpPending(Irp);
  2662. PoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  2663. Irp);
  2664. ntStatus = STATUS_PENDING;
  2665. break;
  2666. }
  2667. switch (irpStack->Parameters.Power.State.DeviceState) {
  2668. case PowerDeviceD0:
  2669. USBH_ASSERT(DeviceExtensionHub->CurrentPowerState != PowerDeviceD0);
  2670. DeviceExtensionHub->HubFlags &=
  2671. ~(HUBFLAG_DEVICE_STOPPING | HUBFLAG_DEVICE_LOW_POWER);
  2672. DeviceExtensionHub->HubFlags |= HUBFLAG_SET_D0_PENDING;
  2673. //
  2674. // must pass this on to our PDO
  2675. //
  2676. IoCopyCurrentIrpStackLocationToNext(Irp);
  2677. IoSetCompletionRoutine(Irp,
  2678. USBH_PowerIrpCompletion,
  2679. DeviceExtensionHub,
  2680. TRUE,
  2681. TRUE,
  2682. TRUE);
  2683. IoMarkIrpPending(Irp);
  2684. PoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  2685. Irp);
  2686. // For some strange PnP reason, we have to return
  2687. // STATUS_PENDING here if our completion routine will also
  2688. // pend (e.g. return STATUS_MORE_PROCESSING_REQUIRED).
  2689. // (Ignore the PoCallDriver return value.)
  2690. ntStatus = STATUS_PENDING;
  2691. break;
  2692. case PowerDeviceD1:
  2693. case PowerDeviceD2:
  2694. case PowerDeviceD3:
  2695. // If there is a ChangeIndicationWorkitem pending, then we
  2696. // must wait for that to complete.
  2697. if (DeviceExtensionHub->ChangeIndicationWorkitemPending) {
  2698. USBH_KdPrint((2,"'Wait for single object\n"));
  2699. ntStatus = KeWaitForSingleObject(&DeviceExtensionHub->CWKEvent,
  2700. Suspended,
  2701. KernelMode,
  2702. FALSE,
  2703. NULL);
  2704. USBH_KdPrint((2,"'Wait for single object, returned %x\n", ntStatus));
  2705. }
  2706. //
  2707. // set our stop flag so that ChangeIndication does not submit
  2708. // any more transfers
  2709. //
  2710. // note that we skip this if the hub is 'stopped'
  2711. if (!(DeviceExtensionHub->HubFlags &
  2712. HUBFLAG_HUB_STOPPED)) {
  2713. NTSTATUS status;
  2714. BOOLEAN bRet;
  2715. DeviceExtensionHub->HubFlags |=
  2716. (HUBFLAG_DEVICE_STOPPING | HUBFLAG_DEVICE_LOW_POWER);
  2717. bRet = IoCancelIrp(DeviceExtensionHub->Irp);
  2718. // always wait -- this fixes a bosd on an IBM laptop
  2719. // the if was a hack someone put in but we have no idea why
  2720. // if (bRet) {
  2721. LOGENTRY(LOG_PNP, "aWAT", DeviceExtensionHub,
  2722. &DeviceExtensionHub->AbortEvent, bRet);
  2723. status = KeWaitForSingleObject(
  2724. &DeviceExtensionHub->AbortEvent,
  2725. Suspended,
  2726. KernelMode,
  2727. FALSE,
  2728. NULL);
  2729. LOGENTRY(LOG_PNP, "awat", DeviceExtensionHub,
  2730. 0, status);
  2731. }
  2732. //
  2733. // must pass this on to our PDO
  2734. //
  2735. IoCopyCurrentIrpStackLocationToNext(Irp);
  2736. IoSetCompletionRoutine(Irp,
  2737. USBH_PowerIrpCompletion,
  2738. DeviceExtensionHub,
  2739. TRUE,
  2740. TRUE,
  2741. TRUE);
  2742. PoStartNextPowerIrp(Irp);
  2743. IoMarkIrpPending(Irp);
  2744. PoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  2745. Irp);
  2746. // toss status and return status pending
  2747. // we do this because our completion routine
  2748. // stalls completion but we do not block here
  2749. // in dispatch.
  2750. // OS code only waits if status_pending is returned
  2751. ntStatus = STATUS_PENDING;
  2752. break;
  2753. }
  2754. break; //DevicePowerState
  2755. }
  2756. break; // MN_SET_POWER
  2757. case IRP_MN_QUERY_POWER:
  2758. USBH_KdPrint((1, "'IRP_MJ_POWER HU fdo(%x) MN_QUERY_POWER\n",
  2759. DeviceExtensionHub->FunctionalDeviceObject));
  2760. // Cancel our WW IRP if we are going to D3, the hub is idled
  2761. // (selectively suspended), and the hub is empty. We don't want
  2762. // to prevent going to D3 if the hub is selectively suspended and
  2763. // there are no children that would require the hub be wake-enabled.
  2764. powerState.DeviceState =
  2765. DeviceExtensionHub->DeviceState[irpStack->Parameters.Power.State.SystemState];
  2766. bHubNeedsWW = USBH_DoesHubNeedWaitWake(DeviceExtensionHub);
  2767. IoAcquireCancelSpinLock(&irql);
  2768. if (powerState.DeviceState == PowerDeviceD3 &&
  2769. DeviceExtensionHub->PendingWakeIrp &&
  2770. !bHubNeedsWW) {
  2771. hubWaitWake = DeviceExtensionHub->PendingWakeIrp;
  2772. DeviceExtensionHub->PendingWakeIrp = NULL;
  2773. }
  2774. IoReleaseCancelSpinLock(irql);
  2775. if (hubWaitWake) {
  2776. USBH_KdPrint((1, "'Cancelling hub's WW because we are going to D3 and there are no children\n"));
  2777. USBH_HubCancelWakeIrp(DeviceExtensionHub, hubWaitWake);
  2778. }
  2779. //
  2780. // Now pass this on to our PDO.
  2781. //
  2782. IoCopyCurrentIrpStackLocationToNext(Irp);
  2783. PoStartNextPowerIrp(Irp);
  2784. ntStatus = PoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  2785. Irp);
  2786. break;
  2787. case IRP_MN_WAIT_WAKE:
  2788. USBH_KdPrint((1, "'IRP_MJ_POWER HU fdo(%x) MN_WAIT_WAKE\n",
  2789. DeviceExtensionHub->FunctionalDeviceObject));
  2790. IoCopyCurrentIrpStackLocationToNext(Irp);
  2791. IoSetCompletionRoutine(Irp,
  2792. USBH_FdoWWIrpIoCompletion,
  2793. DeviceExtensionHub,
  2794. TRUE,
  2795. TRUE,
  2796. TRUE);
  2797. PoStartNextPowerIrp(Irp);
  2798. IoMarkIrpPending(Irp);
  2799. PoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  2800. Irp);
  2801. // For some strange PnP reason, we have to return
  2802. // STATUS_PENDING here if our completion routine will also
  2803. // pend (e.g. return STATUS_MORE_PROCESSING_REQUIRED).
  2804. // (Ignore the PoCallDriver return value.)
  2805. ntStatus = STATUS_PENDING;
  2806. break;
  2807. //
  2808. // otherwise pass the Irp down
  2809. //
  2810. default:
  2811. USBH_KdPrint((2,"'Unhandled Power request to fdo %x %x, passed to PDO\n",
  2812. deviceObject, MinorFunction));
  2813. IoCopyCurrentIrpStackLocationToNext(Irp);
  2814. PoStartNextPowerIrp(Irp);
  2815. ntStatus = PoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  2816. Irp);
  2817. break;
  2818. }
  2819. USBH_KdPrint((2,"'FdoPower exit %x\n", ntStatus));
  2820. return ntStatus;
  2821. }