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

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