Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7295 lines
215 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. USBHUB.C
  5. Abstract:
  6. This module contains code for the hub to function as a
  7. device on the USB. All USBH_Fdo functions live here.
  8. Author:
  9. John Lee
  10. Environment:
  11. kernel mode only
  12. Notes:
  13. Revision History:
  14. 02-02-96 : created
  15. 10-31-06 : jd, use worker thread to process change indications
  16. --*/
  17. #include <wdm.h>
  18. #include <windef.h>
  19. #include <ks.h>
  20. #ifdef WMI_SUPPORT
  21. #include <wmilib.h>
  22. #include <wdmguid.h>
  23. #endif /* WMI_SUPPORT */
  24. #include "usbhub.h"
  25. #include <stdio.h>
  26. #define ESD_RECOVERY_TIMEOUT 5000 // Timeout in ms (5 sec)
  27. #define ESD_RESET_TIMEOUT 5000
  28. #ifdef PAGE_CODE
  29. #ifdef ALLOC_PRAGMA
  30. #pragma alloc_text(PAGE, USBH_ChangeIndicationWorker)
  31. #pragma alloc_text(PAGE, USBH_ProcessHubStateChange)
  32. // #pragma alloc_text(PAGE, USBH_ProcessPortStateChange)
  33. #pragma alloc_text(PAGE, USBH_GetNameFromPdo)
  34. //#pragma alloc_text(PAGE, USBH_MakeName)
  35. //#pragma alloc_text(PAGE, USBH_GenerateDeviceName)
  36. #pragma alloc_text(PAGE, USBH_FdoStartDevice)
  37. #pragma alloc_text(PAGE, USBH_QueryCapabilities)
  38. #pragma alloc_text(PAGE, USBH_FdoHubStartDevice)
  39. // #pragma alloc_text(PAGE, UsbhFdoCleanup)
  40. #pragma alloc_text(PAGE, USBH_FdoStopDevice)
  41. #pragma alloc_text(PAGE, USBH_FdoRemoveDevice)
  42. #pragma alloc_text(PAGE, USBH_FdoQueryBusRelations)
  43. #pragma alloc_text(PAGE, USBH_HubIsBusPowered)
  44. #pragma alloc_text(PAGE, USBH_HubESDRecoveryWorker)
  45. #pragma alloc_text(PAGE, USBH_RegQueryDeviceIgnoreHWSerNumFlag)
  46. #pragma alloc_text(PAGE, USBH_RegQueryGenericUSBDeviceString)
  47. #pragma alloc_text(PAGE, USBH_DeviceIs2xDualMode)
  48. // Win98 breaks if we have an INIT segment
  49. //#pragma alloc_text(INIT, DriverEntry )
  50. #endif
  51. #endif
  52. #ifdef WMI_SUPPORT
  53. #define NUM_WMI_SUPPORTED_GUIDS 3
  54. WMIGUIDREGINFO USB_WmiGuidList[NUM_WMI_SUPPORTED_GUIDS];
  55. extern WMIGUIDREGINFO USB_PortWmiGuidList[];
  56. #endif /* WMI_SUPPORT */
  57. PWCHAR GenericUSBDeviceString = NULL;
  58. NTSTATUS
  59. USBH_GetConfigValue(
  60. IN PWSTR ValueName,
  61. IN ULONG ValueType,
  62. IN PVOID ValueData,
  63. IN ULONG ValueLength,
  64. IN PVOID Context,
  65. IN PVOID EntryContext
  66. )
  67. /*++
  68. Routine Description:
  69. This routine is a callback routine for RtlQueryRegistryValues
  70. It is called for each entry in the Parameters
  71. node to set the config values. The table is set up
  72. so that this function will be called with correct default
  73. values for keys that are not present.
  74. Arguments:
  75. ValueName - The name of the value (ignored).
  76. ValueType - The type of the value
  77. ValueData - The data for the value.
  78. ValueLength - The length of ValueData.
  79. Context - A pointer to the CONFIG structure.
  80. EntryContext - The index in Config->Parameters to save the value.
  81. Return Value:
  82. --*/
  83. {
  84. NTSTATUS ntStatus = STATUS_SUCCESS;
  85. PWCHAR tmpStr;
  86. USBH_KdPrint((2,"'Type 0x%x, Length 0x%x\n", ValueType, ValueLength));
  87. switch (ValueType) {
  88. case REG_DWORD:
  89. *(PVOID*)EntryContext = *(PVOID*)ValueData;
  90. break;
  91. case REG_BINARY:
  92. RtlCopyMemory(EntryContext, ValueData, ValueLength);
  93. break;
  94. case REG_SZ:
  95. if (ValueLength) {
  96. tmpStr = UsbhExAllocatePool(PagedPool, ValueLength);
  97. if (tmpStr) {
  98. RtlZeroMemory(tmpStr, ValueLength);
  99. RtlCopyMemory(tmpStr, ValueData, ValueLength);
  100. *(PWCHAR *)EntryContext = tmpStr;
  101. } else {
  102. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  103. }
  104. } else {
  105. ntStatus = STATUS_INVALID_PARAMETER;
  106. }
  107. break;
  108. default:
  109. ntStatus = STATUS_INVALID_PARAMETER;
  110. }
  111. return ntStatus;
  112. }
  113. NTSTATUS
  114. USBH_RegQueryUSBGlobalSelectiveSuspend(
  115. IN OUT PBOOLEAN DisableSelectiveSuspend
  116. )
  117. /*++
  118. Routine Description:
  119. See if selective suspend is glabllay disabled
  120. Arguments:
  121. Return Value:
  122. --*/
  123. {
  124. NTSTATUS ntStatus;
  125. RTL_QUERY_REGISTRY_TABLE QueryTable[2];
  126. PWCHAR usb = L"usb";
  127. ULONG disableSS;
  128. #define G_DISABLE_SS_KEY L"DisableSelectiveSuspend"
  129. PAGED_CODE();
  130. disableSS = 0;
  131. *DisableSelectiveSuspend = FALSE; // Default is enabled.
  132. //
  133. // Set up QueryTable to do the following:
  134. //
  135. // Upgrade install flag
  136. QueryTable[0].QueryRoutine = USBH_GetConfigValue;
  137. QueryTable[0].Flags = 0;
  138. QueryTable[0].Name = G_DISABLE_SS_KEY;
  139. QueryTable[0].EntryContext = &disableSS;
  140. QueryTable[0].DefaultType = REG_DWORD;
  141. QueryTable[0].DefaultData = &disableSS;
  142. QueryTable[0].DefaultLength = sizeof(disableSS);
  143. //
  144. // Stop
  145. //
  146. QueryTable[1].QueryRoutine = NULL;
  147. QueryTable[1].Flags = 0;
  148. QueryTable[1].Name = NULL;
  149. ntStatus = RtlQueryRegistryValues(
  150. RTL_REGISTRY_SERVICES,
  151. usb,
  152. QueryTable, // QueryTable
  153. NULL, // Context
  154. NULL); // Environment
  155. *DisableSelectiveSuspend = disableSS ? TRUE : FALSE;
  156. USBH_KdPrint((1,"'USB\\DisableSelectiveSuspend = 0x%x\n",
  157. *DisableSelectiveSuspend));
  158. return ntStatus;
  159. }
  160. NTSTATUS
  161. USBH_RegQueryDeviceIgnoreHWSerNumFlag(
  162. IN USHORT idVendor,
  163. IN USHORT idProduct,
  164. IN OUT PBOOLEAN IgnoreHWSerNumFlag
  165. )
  166. /*++
  167. Routine Description:
  168. Arguments:
  169. Return Value:
  170. --*/
  171. {
  172. NTSTATUS ntStatus;
  173. RTL_QUERY_REGISTRY_TABLE QueryTable[2];
  174. PWCHAR usbstr = L"usbflags";
  175. WCHAR buffer[sizeof(WCHAR) * 128];
  176. WCHAR tmplate[] = L"IgnoreHWSerNum%04x%04x";
  177. PAGED_CODE();
  178. *IgnoreHWSerNumFlag = FALSE; // Default is don't ignore.
  179. swprintf(buffer, tmplate, idVendor, idProduct);
  180. //
  181. // Set up QueryTable to do the following:
  182. //
  183. // Upgrade install flag
  184. QueryTable[0].QueryRoutine = USBH_GetConfigValue;
  185. QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
  186. QueryTable[0].Name = buffer;
  187. QueryTable[0].EntryContext = IgnoreHWSerNumFlag;
  188. QueryTable[0].DefaultType = 0;
  189. QueryTable[0].DefaultData = NULL;
  190. QueryTable[0].DefaultLength = 0;
  191. //
  192. // Stop
  193. //
  194. QueryTable[1].QueryRoutine = NULL;
  195. QueryTable[1].Flags = 0;
  196. QueryTable[1].Name = NULL;
  197. ntStatus = RtlQueryRegistryValues(
  198. RTL_REGISTRY_CONTROL,
  199. usbstr,
  200. QueryTable, // QueryTable
  201. NULL, // Context
  202. NULL); // Environment
  203. return ntStatus;
  204. }
  205. NTSTATUS
  206. USBH_RegQueryGenericUSBDeviceString(
  207. IN OUT PWCHAR *GenericUSBDeviceString
  208. )
  209. /*++
  210. Routine Description:
  211. Arguments:
  212. Return Value:
  213. --*/
  214. {
  215. NTSTATUS ntStatus;
  216. RTL_QUERY_REGISTRY_TABLE QueryTable[2];
  217. PWCHAR usbstr = L"usbflags";
  218. PWCHAR valuename = L"GenericUSBDeviceString";
  219. PAGED_CODE();
  220. //
  221. // Set up QueryTable to do the following:
  222. //
  223. // Upgrade install flag
  224. QueryTable[0].QueryRoutine = USBH_GetConfigValue;
  225. QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
  226. QueryTable[0].Name = valuename;
  227. QueryTable[0].EntryContext = GenericUSBDeviceString;
  228. QueryTable[0].DefaultType = 0;
  229. QueryTable[0].DefaultData = NULL;
  230. QueryTable[0].DefaultLength = 0;
  231. //
  232. // Stop
  233. //
  234. QueryTable[1].QueryRoutine = NULL;
  235. QueryTable[1].Flags = 0;
  236. QueryTable[1].Name = NULL;
  237. ntStatus = RtlQueryRegistryValues(
  238. RTL_REGISTRY_CONTROL,
  239. usbstr,
  240. QueryTable, // QueryTable
  241. NULL, // Context
  242. NULL); // Environment
  243. return ntStatus;
  244. }
  245. //
  246. // Make the DriverEntry discardable
  247. //
  248. NTSTATUS
  249. DriverEntry(
  250. IN PDRIVER_OBJECT DriverObject,
  251. IN PUNICODE_STRING UniRegistryPath)
  252. /* ++ Routine Description:
  253. *
  254. * Installable driver initialization entry point. We will remember the pointer
  255. * to our DeviceObject.
  256. *
  257. * Arguments:
  258. *
  259. * pDriverObject - pointer to driver object pustRegisterPath - pointer to a
  260. * unicode string representing the path to driver specific key in the
  261. * registry.
  262. *
  263. * Return Values:
  264. *
  265. * STATUS_SUCCESS - if successful
  266. * STATUS_UNSUCCESSFUL - otherwise
  267. *
  268. * -- */
  269. {
  270. NTSTATUS status, ntStatus = STATUS_SUCCESS;
  271. PUNICODE_STRING registryPath = &UsbhRegistryPath;
  272. USBH_KdPrint((2,"'enter DriverEntry\n"));
  273. USBH_LogInit();
  274. UsbhDriverObject = DriverObject; // remember ourselves
  275. //
  276. // Initialize the driver object with this driver's entry points.
  277. //
  278. DriverObject->MajorFunction[IRP_MJ_CREATE] =
  279. DriverObject->MajorFunction[IRP_MJ_CLOSE] =
  280. DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
  281. DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
  282. DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = USBH_HubDispatch;
  283. DriverObject->DriverUnload = USBH_DriverUnload;
  284. DriverObject->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE) USBH_AddDevice;
  285. DriverObject->MajorFunction[IRP_MJ_PNP] = USBH_HubDispatch;
  286. DriverObject->MajorFunction[IRP_MJ_POWER] = USBH_HubDispatch;
  287. //
  288. // Need to ensure that the registry path is null-terminated.
  289. // Allocate pool to hold a null-terminated copy of the path.
  290. // Safe in paged pool since all registry routines execute at
  291. // PASSIVE_LEVEL.
  292. //
  293. registryPath->MaximumLength = UniRegistryPath->Length + sizeof(UNICODE_NULL);
  294. registryPath->Length = UniRegistryPath->Length;
  295. registryPath->Buffer = ExAllocatePoolWithTag(
  296. PagedPool,
  297. registryPath->MaximumLength,
  298. USBHUB_HEAP_TAG);
  299. if (!registryPath->Buffer) {
  300. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  301. USBH_LogFree();
  302. goto DriverEntry_Exit;
  303. } else {
  304. RtlZeroMemory (registryPath->Buffer, registryPath->MaximumLength);
  305. RtlMoveMemory (registryPath->Buffer,
  306. UniRegistryPath->Buffer,
  307. UniRegistryPath->Length);
  308. #ifdef WMI_SUPPORT
  309. // These are the GUIDs that we support for the HUB.
  310. USB_WmiGuidList[0].Guid = (LPCGUID)&GUID_USB_WMI_STD_DATA;
  311. USB_WmiGuidList[0].InstanceCount = 1;
  312. USB_WmiGuidList[0].Flags = 0;
  313. USB_WmiGuidList[1].Guid = (LPCGUID)&GUID_USB_WMI_STD_NOTIFICATION;
  314. USB_WmiGuidList[1].InstanceCount = 1;
  315. USB_WmiGuidList[1].Flags = 0;
  316. // NB: GUID_POWER_DEVICE_ENABLE MUST be last because we only support
  317. // it for the Root Hub, and we omit the last one in the list if not
  318. // the Root Hub.
  319. USB_WmiGuidList[2].Guid = (LPCGUID)&GUID_POWER_DEVICE_ENABLE;
  320. USB_WmiGuidList[2].InstanceCount = 1;
  321. USB_WmiGuidList[2].Flags = 0;
  322. // These are the GUIDs that we support for the PORT PDOs.
  323. USB_PortWmiGuidList[0].Guid = (LPCGUID)&MSDeviceUI_FirmwareRevision_GUID;
  324. USB_PortWmiGuidList[0].InstanceCount = 1;
  325. USB_PortWmiGuidList[0].Flags = 0;
  326. #endif /* WMI_SUPPORT */
  327. }
  328. USBH_RegQueryGenericUSBDeviceString(&GenericUSBDeviceString);
  329. DriverEntry_Exit:
  330. USBH_KdPrint((2,"' exit DriverEntry %x\n", ntStatus));
  331. return ntStatus;
  332. }
  333. #if DBG
  334. VOID
  335. USBH_ShowPortState(
  336. IN USHORT PortNumber,
  337. IN PPORT_STATE PortState)
  338. /* ++
  339. *
  340. * Description:
  341. *
  342. * Arguments:
  343. *
  344. * Return:
  345. *
  346. * None
  347. *
  348. * -- */
  349. {
  350. USBH_KdPrint((2,"' Port state for port %x status = %x change = %x\n", PortNumber,
  351. PortState->PortStatus, PortState->PortChange));
  352. if (PortState->PortStatus & PORT_STATUS_CONNECT) {
  353. USBH_KdPrint((2,"'PORT_STATUS_CONNECT\n"));
  354. }
  355. if (PortState->PortStatus & PORT_STATUS_ENABLE) {
  356. USBH_KdPrint((2,"'PORT_STATUS_ENABLE\n"));
  357. }
  358. if (PortState->PortStatus & PORT_STATUS_SUSPEND) {
  359. USBH_KdPrint((2,"'PORT_STATUS_SUSPEND\n"));
  360. }
  361. if (PortState->PortStatus & PORT_STATUS_OVER_CURRENT) {
  362. USBH_KdPrint((2,"'PORT_STATUS_OVER_CURRENT\n"));
  363. }
  364. if (PortState->PortStatus & PORT_STATUS_RESET) {
  365. USBH_KdPrint((2,"'PORT_STATUS_RESET\n"));
  366. }
  367. if (PortState->PortStatus & PORT_STATUS_POWER) {
  368. USBH_KdPrint((2,"'PORT_STATUS_POWER\n"));
  369. }
  370. if (PortState->PortStatus & PORT_STATUS_LOW_SPEED) {
  371. USBH_KdPrint((2,"'PORT_STATUS_LOW_SPEED\n"));
  372. }
  373. if (PortState->PortChange & PORT_STATUS_CONNECT) {
  374. USBH_KdPrint((2,"'PORT_CHANGE_CONNECT\n"));
  375. }
  376. if (PortState->PortChange & PORT_STATUS_ENABLE) {
  377. USBH_KdPrint((2,"'PORT_CHANGE_ENABLE\n"));
  378. }
  379. if (PortState->PortChange & PORT_STATUS_SUSPEND) {
  380. USBH_KdPrint((2,"'PORT_CHANGE_SUSPEND\n"));
  381. }
  382. if (PortState->PortChange & PORT_STATUS_OVER_CURRENT) {
  383. USBH_KdPrint((2,"'PORT_CHANGE_OVER_CURRENT\n"));
  384. }
  385. if (PortState->PortChange & PORT_STATUS_RESET) {
  386. USBH_KdPrint((2,"'PORT_CHANGE_RESET\n"));
  387. }
  388. if (PortState->PortChange & PORT_STATUS_POWER) {
  389. USBH_KdPrint((2,"'PORT_CHANGE_POWER\n"));
  390. }
  391. if (PortState->PortChange & PORT_STATUS_LOW_SPEED) {
  392. USBH_KdPrint((2,"'PORT_CHANGE_LOW_SPEED\n"));
  393. }
  394. return;
  395. }
  396. #endif
  397. VOID
  398. USBH_CompleteIrp(
  399. IN PIRP Irp,
  400. IN NTSTATUS NtStatus)
  401. /* ++
  402. *
  403. * Description:
  404. *
  405. * This function complete the specified Irp with no priority boost. It also
  406. * sets up the IoStatusBlock.
  407. *
  408. * Arguments:
  409. *
  410. * Irp - the Irp to be completed by us NtStatus - the status code we want to
  411. * return
  412. *
  413. * Return:
  414. *
  415. * None
  416. *
  417. * -- */
  418. {
  419. Irp->IoStatus.Status = NtStatus;
  420. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  421. return;
  422. }
  423. NTSTATUS
  424. USBH_PassIrp(
  425. IN PIRP Irp,
  426. IN PDEVICE_OBJECT NextDeviceObject)
  427. /* ++
  428. *
  429. * Description:
  430. *
  431. * This function pass the Irp to lower level driver.
  432. *
  433. * Arguments:
  434. *
  435. * Return:
  436. *
  437. * NTSTATUS
  438. *
  439. * -- */
  440. {
  441. NTSTATUS ntStatus;
  442. USBH_KdPrint((2,"'PassIrp\n"));
  443. IoSkipCurrentIrpStackLocation(Irp);
  444. ntStatus = IoCallDriver(NextDeviceObject, Irp);
  445. USBH_KdPrint((2,"'Exit PassIrp\n"));
  446. return ntStatus;
  447. }
  448. NTSTATUS
  449. USBH_FdoDispatch(
  450. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  451. IN PIRP Irp)
  452. /* ++
  453. *
  454. * Description:
  455. *
  456. * All external Irps on FDO come here.
  457. *
  458. * Arguments:
  459. *
  460. * DeviceExtensionHub - the extension of the Fdo pIrp - the request
  461. *
  462. * Return:
  463. *
  464. * NTSTATUS
  465. *
  466. * -- */
  467. {
  468. NTSTATUS ntStatus = STATUS_SUCCESS;
  469. PIO_STACK_LOCATION ioStackLocation; // our stack location
  470. PDEVICE_OBJECT deviceObject;
  471. BOOLEAN bDoCheckHubIdle = FALSE;
  472. USBH_KdPrint((2,"'FdoDispatch DeviceExtension %x Irp %x\n", DeviceExtensionHub, Irp));
  473. deviceObject = DeviceExtensionHub->FunctionalDeviceObject;
  474. //
  475. // Get a pointer to IoStackLocation so we can retrieve parameters.
  476. //
  477. ioStackLocation = IoGetCurrentIrpStackLocation(Irp);
  478. LOGENTRY(LOG_PNP, "hIRP", DeviceExtensionHub,
  479. ioStackLocation->MajorFunction, ioStackLocation->MinorFunction);
  480. //
  481. // the called functions will complete the irp if necessary
  482. //
  483. switch (ioStackLocation->MajorFunction) {
  484. case IRP_MJ_CREATE:
  485. USBH_KdPrint((2,"'IRP_MJ_CREATE\n"));
  486. USBH_CompleteIrp(Irp, STATUS_SUCCESS);
  487. break;
  488. case IRP_MJ_CLOSE:
  489. USBH_KdPrint((2,"'IRP_MJ_CLOSE\n"));
  490. USBH_CompleteIrp(Irp, STATUS_SUCCESS);
  491. break;
  492. case IRP_MJ_DEVICE_CONTROL:
  493. {
  494. ULONG ioControlCode;
  495. USBH_KdPrint((2,"'Hub FDO IRP_MJ_DEVICE_CONTROL\n"));
  496. // If this hub is currently Selective Suspended, then we need to
  497. // power up the hub first before sending any IOCTL requests along to it.
  498. // Make sure hub has been started, though.
  499. if (DeviceExtensionHub->CurrentPowerState != PowerDeviceD0 &&
  500. (DeviceExtensionHub->HubFlags & HUBFLAG_NEED_CLEANUP)) {
  501. bDoCheckHubIdle = TRUE;
  502. USBH_HubSetD0(DeviceExtensionHub);
  503. }
  504. ioControlCode = ioStackLocation->Parameters.DeviceIoControl.IoControlCode;
  505. ntStatus = STATUS_DEVICE_BUSY;
  506. switch (ioControlCode) {
  507. case IOCTL_USB_GET_NODE_INFORMATION:
  508. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_STOPPED)) {
  509. ntStatus = USBH_IoctlGetNodeInformation(DeviceExtensionHub,
  510. Irp);
  511. } else {
  512. USBH_CompleteIrp(Irp, ntStatus);
  513. }
  514. break;
  515. case IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME:
  516. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_STOPPED)) {
  517. ntStatus = USBH_IoctlGetNodeConnectionDriverKeyName(DeviceExtensionHub,
  518. Irp);
  519. } else {
  520. USBH_CompleteIrp(Irp, ntStatus);
  521. }
  522. break;
  523. case IOCTL_USB_GET_NODE_CONNECTION_INFORMATION:
  524. // note, when rev all internal apps we can remove this
  525. // code
  526. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_STOPPED)) {
  527. ntStatus = USBH_IoctlGetNodeConnectionInformation(DeviceExtensionHub,
  528. Irp,
  529. FALSE);
  530. } else {
  531. USBH_CompleteIrp(Irp, ntStatus);
  532. }
  533. break;
  534. // EX api returns speed
  535. case IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX:
  536. // note, when rev all internal apps we can remove this
  537. // code
  538. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_STOPPED)) {
  539. ntStatus = USBH_IoctlGetNodeConnectionInformation(DeviceExtensionHub,
  540. Irp,
  541. TRUE);
  542. } else {
  543. USBH_CompleteIrp(Irp, ntStatus);
  544. }
  545. break;
  546. case IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES:
  547. // note, when rev all internal apps we can remove this
  548. // code
  549. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_STOPPED)) {
  550. ntStatus = USBH_IoctlGetNodeConnectionAttributes(DeviceExtensionHub,
  551. Irp);
  552. } else {
  553. USBH_CompleteIrp(Irp, ntStatus);
  554. }
  555. break;
  556. case IOCTL_USB_GET_NODE_CONNECTION_NAME:
  557. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_STOPPED)) {
  558. ntStatus = USBH_IoctlGetNodeName(DeviceExtensionHub,
  559. Irp);
  560. } else {
  561. USBH_CompleteIrp(Irp, ntStatus);
  562. }
  563. break;
  564. case IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION:
  565. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_STOPPED)) {
  566. ntStatus = USBH_IoctlGetDescriptorForPDO(DeviceExtensionHub,
  567. Irp);
  568. } else {
  569. USBH_CompleteIrp(Irp, ntStatus);
  570. }
  571. break;
  572. case IOCTL_USB_GET_HUB_CAPABILITIES:
  573. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_STOPPED)) {
  574. ntStatus = USBH_IoctlGetHubCapabilities(DeviceExtensionHub,
  575. Irp);
  576. } else {
  577. USBH_CompleteIrp(Irp, ntStatus);
  578. }
  579. break;
  580. case IOCTL_KS_PROPERTY:
  581. ntStatus = STATUS_INVALID_DEVICE_REQUEST;
  582. USBH_CompleteIrp(Irp, ntStatus);
  583. break;
  584. case IOCTL_USB_HUB_CYCLE_PORT:
  585. ntStatus = USBH_IoctlCycleHubPort(DeviceExtensionHub,
  586. Irp);
  587. break;
  588. default:
  589. ntStatus = USBH_PassIrp(Irp, DeviceExtensionHub->RootHubPdo);
  590. }
  591. if (bDoCheckHubIdle) {
  592. USBH_CheckHubIdle(DeviceExtensionHub);
  593. }
  594. }
  595. break;
  596. case IRP_MJ_INTERNAL_DEVICE_CONTROL:
  597. USBH_KdPrint((2,"'InternlDeviceControl IOCTL unknown pass on\n"));
  598. ntStatus = USBH_PassIrp(Irp, DeviceExtensionHub->TopOfStackDeviceObject);
  599. break;
  600. case IRP_MJ_PNP:
  601. USBH_KdPrint((2,"'IRP_MJ_PNP\n"));
  602. ntStatus = USBH_FdoPnP(DeviceExtensionHub, Irp, ioStackLocation->MinorFunction);
  603. break;
  604. case IRP_MJ_POWER:
  605. USBH_KdPrint((2,"'IRP_MJ_POWER\n"));
  606. ntStatus = USBH_FdoPower(DeviceExtensionHub, Irp, ioStackLocation->MinorFunction);
  607. break;
  608. #ifdef WMI_SUPPORT
  609. case IRP_MJ_SYSTEM_CONTROL:
  610. USBH_KdPrint((2,"'IRP_MJ_SYSTEM_CONTROL\n"));
  611. ntStatus =
  612. USBH_SystemControl ((PDEVICE_EXTENSION_FDO) DeviceExtensionHub, Irp);
  613. break;
  614. #endif
  615. default:
  616. //
  617. // Unknown Irp -- pass on
  618. //
  619. USBH_KdBreak(("Unknown Irp for fdo %x Irp_Mj %x\n",
  620. deviceObject, ioStackLocation->MajorFunction));
  621. ntStatus = USBH_PassIrp(Irp, DeviceExtensionHub->TopOfStackDeviceObject);
  622. break;
  623. }
  624. //USBH_FdoDispatch_Done:
  625. USBH_KdPrint((2,"' exit USBH_FdoDispatch Object %x Status %x\n",
  626. deviceObject, ntStatus));
  627. //
  628. // always return a status code
  629. //
  630. return ntStatus;
  631. }
  632. NTSTATUS
  633. USBH_HubDispatch(
  634. IN PDEVICE_OBJECT DeviceObject,
  635. IN PIRP Irp
  636. )
  637. /* ++
  638. *
  639. * Routine Description:
  640. *
  641. * This is the dispatch routine for all Irps passed to the hub driver.
  642. * It is here that we determine if the call was passed throug the FDO
  643. * for the hub itself or a PDO owned by the hub.
  644. *
  645. * Arguments:
  646. *
  647. * Return Value:
  648. *
  649. * -- */
  650. {
  651. NTSTATUS ntStatus;
  652. PDEVICE_EXTENSION_HEADER deviceExtensionHeader;
  653. //
  654. // Get the pointer to the device extension.
  655. //
  656. //
  657. // examine the extension
  658. //
  659. deviceExtensionHeader = (PDEVICE_EXTENSION_HEADER) DeviceObject->DeviceExtension;
  660. switch(deviceExtensionHeader->ExtensionType) {
  661. case EXTENSION_TYPE_HUB:
  662. ntStatus = USBH_FdoDispatch((PDEVICE_EXTENSION_HUB) deviceExtensionHeader, Irp);
  663. break;
  664. case EXTENSION_TYPE_PORT:
  665. ntStatus = USBH_PdoDispatch((PDEVICE_EXTENSION_PORT) deviceExtensionHeader, Irp);
  666. break;
  667. case EXTENSION_TYPE_PARENT:
  668. ntStatus = USBH_ParentDispatch((PDEVICE_EXTENSION_PARENT) deviceExtensionHeader, Irp);
  669. break;
  670. case EXTENSION_TYPE_FUNCTION:
  671. ntStatus = USBH_FunctionPdoDispatch((PDEVICE_EXTENSION_FUNCTION) deviceExtensionHeader, Irp);
  672. break;
  673. default:
  674. USBH_KdBreak(("bad extension type\n"));
  675. ntStatus = STATUS_INVALID_PARAMETER;
  676. }
  677. return ntStatus;
  678. }
  679. VOID
  680. USBH_DriverUnload(
  681. IN PDRIVER_OBJECT DriverObject)
  682. /* ++
  683. *
  684. * Description:
  685. *
  686. * This function will clean up all resources we allocated.
  687. *
  688. * Arguments:
  689. *
  690. * pDriverObject - Ourselves
  691. *
  692. * Return:
  693. *
  694. * None
  695. *
  696. * -- */
  697. {
  698. PUNICODE_STRING registryPath = &UsbhRegistryPath;
  699. USBH_KdPrint((1, "'USBHUB.SYS unload\n"));
  700. USBH_LogFree();
  701. if (registryPath->Buffer) {
  702. ExFreePool(registryPath->Buffer);
  703. registryPath->Buffer = NULL;
  704. }
  705. if (GenericUSBDeviceString) {
  706. UsbhExFreePool(GenericUSBDeviceString);
  707. GenericUSBDeviceString = NULL;
  708. }
  709. // assert here that all PDOs for this hub have been removed
  710. return;
  711. }
  712. NTSTATUS
  713. USBH_AbortInterruptPipe(
  714. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub)
  715. /* ++
  716. *
  717. * Description:
  718. *
  719. * Abort our pending transfer on the interrupt
  720. * pipe.
  721. *
  722. * Arguments:
  723. *
  724. * Return:
  725. *
  726. * NTSTATUS
  727. *
  728. * -- */
  729. {
  730. NTSTATUS ntStatus, status;
  731. PURB urb;
  732. USBH_KdPrint((2,"'Enter AbortInterruptPipe pExt=%x\n", DeviceExtensionHub));
  733. LOGENTRY(LOG_PNP, "ABRT", DeviceExtensionHub, 0, 0);
  734. urb = UsbhExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
  735. if (urb) {
  736. urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_PIPE_REQUEST);
  737. urb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
  738. urb->UrbPipeRequest.PipeHandle = DeviceExtensionHub->PipeInformation.PipeHandle;
  739. ntStatus = USBH_FdoSyncSubmitUrb(DeviceExtensionHub->FunctionalDeviceObject, urb);
  740. //
  741. // wait on the abort event
  742. //
  743. //
  744. // timeout here?
  745. LOGENTRY(LOG_PNP, "hWAT", DeviceExtensionHub,
  746. &DeviceExtensionHub->AbortEvent, ntStatus);
  747. if (NT_SUCCESS(ntStatus)) {
  748. status = KeWaitForSingleObject(
  749. &DeviceExtensionHub->AbortEvent,
  750. Suspended,
  751. KernelMode,
  752. FALSE,
  753. NULL);
  754. }
  755. UsbhExFreePool(urb);
  756. } else {
  757. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  758. }
  759. USBH_KdPrint((2,"'Exit AbortInterruptPipe %x\n", ntStatus));
  760. return ntStatus;
  761. }
  762. #if 0
  763. NTSTATUS
  764. USBH_GetHubConfigurationDescriptor(
  765. IN OUT PDEVICE_EXTENSION_HUB DeviceExtensionHub)
  766. /* ++
  767. *
  768. * Description:
  769. *
  770. * Get our configuration info.
  771. *
  772. * Return:
  773. *
  774. * NTSTATUS
  775. *
  776. * -- */
  777. {
  778. NTSTATUS ntStatus = STATUS_SUCCESS;
  779. PURB urb;
  780. ULONG numBytes; // transfer length
  781. PUCHAR buffer; // a pointer to the transfer buffer
  782. PDEVICE_OBJECT deviceObject;
  783. USBH_KdPrint((2,"'enter GetConfigurationDescriptor\n"));
  784. USBH_ASSERT(EXTENSION_TYPE_HUB == DeviceExtensionHub->ExtensionType);
  785. deviceObject = DeviceExtensionHub->FunctionalDeviceObject;
  786. USBH_ASSERT(DeviceExtensionHub->ConfigurationDescriptor == NULL);
  787. //
  788. // Most likely a Hub has 1 configuration, 1 interface and 1 endpoint
  789. // possibly follwed by the hub descriptor, try to get it on the first
  790. // pass
  791. //
  792. numBytes = sizeof(USB_CONFIGURATION_DESCRIPTOR) +
  793. sizeof(USB_INTERFACE_DESCRIPTOR) +
  794. sizeof(USB_ENDPOINT_DESCRIPTOR) +
  795. sizeof(USB_HUB_DESCRIPTOR);
  796. //
  797. // Allocate an Urb and descriptor buffer.
  798. //
  799. urb = UsbhExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
  800. if (NULL == urb) {
  801. USBH_KdBreak(("GetConfigurationDescriptor fail alloc Urb\n"));
  802. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  803. }
  804. if (NT_SUCCESS(ntStatus)) {
  805. //
  806. // got the urb no try to get descriptor data
  807. //
  808. USBH_GetHubConfigurationDescriptor_Retry:
  809. buffer = (PUCHAR) UsbhExAllocatePool(NonPagedPool, numBytes);
  810. if (buffer != NULL) {
  811. UsbBuildGetDescriptorRequest(urb,
  812. (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
  813. USB_CONFIGURATION_DESCRIPTOR_TYPE,
  814. 0,
  815. 0,
  816. buffer,
  817. NULL,
  818. numBytes,
  819. NULL);
  820. ntStatus = USBH_FdoSyncSubmitUrb(deviceObject, urb);
  821. } else {
  822. USBH_KdBreak(("GetConfigurationDescriptor fail alloc memory\n"));
  823. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  824. }
  825. if (!NT_SUCCESS(ntStatus)) {
  826. UsbhExFreePool(buffer);
  827. USBH_KdPrint((2,"'GetConfigurationDescriptor fail calling Usbd code %x\n",
  828. ntStatus));
  829. } else {
  830. if (((PUSB_CONFIGURATION_DESCRIPTOR) buffer)->wTotalLength > numBytes) {
  831. //
  832. // should only hit this if the hub has > 7 ports
  833. //
  834. UsbhExFreePool(buffer);
  835. USBH_KdBreak(("GetConfigurationDescriptor 2nd try\n"));
  836. goto USBH_GetHubConfigurationDescriptor_Retry;
  837. } else {
  838. //
  839. // success
  840. //
  841. DeviceExtensionHub->ConfigurationDescriptor =
  842. (PUSB_CONFIGURATION_DESCRIPTOR) buffer;
  843. }
  844. }
  845. }
  846. //
  847. // Free the Urb and first buffer for descriptors
  848. //
  849. if (urb != NULL) {
  850. UsbhExFreePool(urb);
  851. }
  852. return ntStatus;
  853. }
  854. #endif
  855. BOOLEAN
  856. IsBitSet(
  857. PVOID Bitmap,
  858. ULONG PortNumber)
  859. /* ++
  860. *
  861. * Description:
  862. *
  863. * Check if a bit is set given a string of bytes.
  864. *
  865. * Arguments:
  866. *
  867. * pul - the string of bitmap ulPortNumber - the bit location to check for the
  868. * port
  869. *
  870. * Return:
  871. *
  872. * TRUE - if the corresponding bit is set. FALSE - otherwise
  873. *
  874. * -- */
  875. {
  876. ULONG dwordOffset;
  877. ULONG bitOffset;
  878. PUCHAR l = (PUCHAR) Bitmap;
  879. dwordOffset = PortNumber / 8;
  880. bitOffset = PortNumber % 8;
  881. return ((l[dwordOffset] & (1 << bitOffset)) ? TRUE : FALSE);
  882. }
  883. NTSTATUS
  884. USBH_OpenConfiguration(
  885. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub)
  886. /* ++
  887. *
  888. * Description:
  889. *
  890. * Configure the USB hub device.
  891. *
  892. * Argument:
  893. *
  894. * Return:
  895. *
  896. * NtStatus
  897. *
  898. * -- */
  899. {
  900. NTSTATUS ntStatus;
  901. PURB urb;
  902. PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor;
  903. USBD_INTERFACE_LIST_ENTRY interfaceList[2];
  904. USBH_KdPrint((2,"'Enter OpenConfiguration\n"));
  905. //
  906. // I do not believe it is legal to have a hub with any other
  907. // interfaces.
  908. //
  909. // This code will locate the 'HUB' interface and configure
  910. // the device as if this were the only interface.
  911. //
  912. //
  913. // find the hub interface
  914. //
  915. if ((DeviceExtensionHub->HubFlags & HUBFLAG_USB20_HUB) &&
  916. !IS_ROOT_HUB(DeviceExtensionHub)) {
  917. // 2.0 hubs may have multiple interfaces
  918. // one for per port TTs
  919. // one for a global TT
  920. // these are diferentiated by the bportocol field we attempt
  921. // to select the multi-TT version by default
  922. //
  923. USBH_KdPrint((1,"'Configure 2.0 hub %x\n",
  924. DeviceExtensionHub->ConfigurationDescriptor));
  925. // find a Multi TT interface
  926. interfaceDescriptor =
  927. USBD_ParseConfigurationDescriptorEx(
  928. (PUSB_CONFIGURATION_DESCRIPTOR) DeviceExtensionHub->ConfigurationDescriptor,
  929. (PVOID) DeviceExtensionHub->ConfigurationDescriptor,
  930. -1, //interface, don't care
  931. -1, //alt setting, don't care
  932. USB_DEVICE_CLASS_HUB, // hub class
  933. -1, // subclass, don't care
  934. 2); // multi TT protocol
  935. if (interfaceDescriptor != NULL) {
  936. USBH_KdPrint((1,"'USB 2.0 hub - Multi TT\n"));
  937. DeviceExtensionHub->HubFlags |= HUBFLAG_USB20_MULTI_TT;
  938. } else {
  939. // locate the single TT protocol, may be zero or 1
  940. interfaceDescriptor =
  941. USBD_ParseConfigurationDescriptorEx(
  942. (PUSB_CONFIGURATION_DESCRIPTOR) DeviceExtensionHub->ConfigurationDescriptor,
  943. (PVOID) DeviceExtensionHub->ConfigurationDescriptor,
  944. -1, //interface, don't care
  945. -1, //alt setting, don't care
  946. USB_DEVICE_CLASS_HUB, // hub class
  947. -1, // subclass, don't care
  948. 1); // single TT protocol
  949. if (interfaceDescriptor == NULL) {
  950. // locate the single TT protocol
  951. interfaceDescriptor =
  952. USBD_ParseConfigurationDescriptorEx(
  953. (PUSB_CONFIGURATION_DESCRIPTOR) DeviceExtensionHub->ConfigurationDescriptor,
  954. (PVOID) DeviceExtensionHub->ConfigurationDescriptor,
  955. -1, //interface, don't care
  956. -1, //alt setting, don't care
  957. USB_DEVICE_CLASS_HUB, // hub class
  958. -1, // subclass, don't care
  959. 0); // single TT protocol
  960. }
  961. if (interfaceDescriptor != NULL) {
  962. USBH_KdPrint((1,"'USB 2.0 hub - Single TT\n"));
  963. }
  964. }
  965. } else {
  966. // just do what we always did to be safe
  967. interfaceDescriptor =
  968. USBD_ParseConfigurationDescriptorEx(
  969. (PUSB_CONFIGURATION_DESCRIPTOR) DeviceExtensionHub->ConfigurationDescriptor,
  970. (PVOID) DeviceExtensionHub->ConfigurationDescriptor,
  971. -1, //interface, don't care
  972. -1, //alt setting, don't care
  973. USB_DEVICE_CLASS_HUB, // hub class
  974. -1, // subclass, don't care
  975. -1); // protocol, don't care
  976. }
  977. if (interfaceDescriptor == NULL ||
  978. interfaceDescriptor->bInterfaceClass != USB_DEVICE_CLASS_HUB) {
  979. USBH_KdBreak(("OpenConfiguration interface not found\n"));
  980. return STATUS_UNSUCCESSFUL;
  981. }
  982. interfaceList[0].InterfaceDescriptor =
  983. interfaceDescriptor;
  984. // terminate the list
  985. interfaceList[1].InterfaceDescriptor =
  986. NULL;
  987. urb = USBD_CreateConfigurationRequestEx(DeviceExtensionHub->ConfigurationDescriptor,
  988. &interfaceList[0]);
  989. if (NULL == urb) {
  990. USBH_KdBreak(("OpenConfiguration aloc Urb failed\n"));
  991. return STATUS_INSUFFICIENT_RESOURCES;
  992. }
  993. ntStatus = USBH_FdoSyncSubmitUrb(DeviceExtensionHub->FunctionalDeviceObject, urb);
  994. if (NT_SUCCESS(ntStatus)) {
  995. PUSBD_INTERFACE_INFORMATION interface;
  996. //
  997. // interface we selected
  998. //
  999. interface = interfaceList[0].Interface;
  1000. //
  1001. // Save the pipe handle for the Interrupt pipe
  1002. //
  1003. DeviceExtensionHub->PipeInformation =
  1004. interface->Pipes[0];
  1005. DeviceExtensionHub->Configuration =
  1006. urb->UrbSelectConfiguration.ConfigurationHandle;
  1007. }
  1008. ExFreePool(urb);
  1009. USBH_KdPrint((2,"'Exit OpenConfiguration PipeInfo %x\n", DeviceExtensionHub->PipeInformation));
  1010. return ntStatus;
  1011. }
  1012. NTSTATUS
  1013. USBH_CloseConfiguration(
  1014. IN PDEVICE_EXTENSION_FDO DeviceExtensionFdo
  1015. )
  1016. /* ++
  1017. *
  1018. * Description:
  1019. *
  1020. * Close our confiuration on USB to prepare for removal of ourselves. Before
  1021. * this is called, the InterruptTransfer should have been removed by
  1022. * USBH_AbortInterruptPipe.
  1023. *
  1024. * Argument:
  1025. *
  1026. * DeviceExtensionHub - pointer to the FDO extension
  1027. *
  1028. * Return:
  1029. *
  1030. * NtStatus
  1031. *
  1032. * -- */
  1033. {
  1034. NTSTATUS ntStatus;
  1035. PURB urb;
  1036. USBH_KdPrint((2,"'Enter CloseConfiguration\n"));
  1037. urb = UsbhExAllocatePool(NonPagedPool, sizeof(struct _URB_SELECT_CONFIGURATION));
  1038. if (NULL == urb) {
  1039. USBH_KdBreak(("OpenConfiguration aloc Urb failed\n"));
  1040. return STATUS_INSUFFICIENT_RESOURCES;
  1041. }
  1042. urb->UrbHeader.Length = sizeof(struct _URB_SELECT_CONFIGURATION);
  1043. urb->UrbHeader.Function = URB_FUNCTION_SELECT_CONFIGURATION;
  1044. urb->UrbSelectConfiguration.ConfigurationDescriptor = NULL;
  1045. ntStatus = USBH_FdoSyncSubmitUrb(DeviceExtensionFdo->FunctionalDeviceObject, urb);
  1046. UsbhExFreePool(urb);
  1047. USBH_KdPrint((2,"'Exit CloseConfiguration %x\n", ntStatus));
  1048. return ntStatus;
  1049. }
  1050. NTSTATUS
  1051. USBH_SubmitInterruptTransfer(
  1052. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  1053. )
  1054. /* ++
  1055. *
  1056. * Description:
  1057. *
  1058. * To submit a listen down for Status Change interrupt transfer. When the
  1059. * transfer is completed, the USBH_ChangeIndication will be called.
  1060. *
  1061. * Arguments:
  1062. *
  1063. * DeviceExtensionHub - the hub we are listening
  1064. *
  1065. * Return:
  1066. *
  1067. * NTSTATUS
  1068. *
  1069. * -- */
  1070. {
  1071. NTSTATUS ntStatus;
  1072. PIO_STACK_LOCATION nextStack; // next stack of the Irp
  1073. PIRP irp;
  1074. PURB urb;
  1075. CHAR stackSize;
  1076. USBH_KdPrint((2,"'Enter Submit IntTrans\n"));
  1077. irp = DeviceExtensionHub->Irp;
  1078. USBH_ASSERT(NULL != irp);
  1079. // Synchronize with FdoPower. Don't let the IRP slip through if FdoPower
  1080. // has already set the HUBFLAG_DEVICE_LOW_POWER flag.
  1081. //
  1082. // It is ok to allow this through in the REMOVE case
  1083. // (i.e. HUBFLAG_DEVICE_STOPPING is set) because the IRP will need to
  1084. // be submitted so that it can be aborted by USBH_FdoCleanup.
  1085. if (DeviceExtensionHub->HubFlags & HUBFLAG_DEVICE_LOW_POWER) {
  1086. irp = NULL;
  1087. }
  1088. if (!irp) {
  1089. ntStatus = STATUS_INVALID_DEVICE_STATE;
  1090. LOGENTRY(LOG_PNP, "Int!", DeviceExtensionHub,
  1091. DeviceExtensionHub->HubFlags, 0);
  1092. goto SubmitIntTrans_Exit;
  1093. }
  1094. urb = &DeviceExtensionHub->Urb;
  1095. USBH_ASSERT(NULL != urb);
  1096. USBH_ASSERT(sizeof(*urb) >= sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
  1097. //
  1098. // Fill in Urb header
  1099. //
  1100. LOGENTRY(LOG_PNP, "Int>", DeviceExtensionHub, urb, irp);
  1101. urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
  1102. urb->UrbHeader.Function =
  1103. URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
  1104. urb->UrbHeader.UsbdDeviceHandle = NULL;
  1105. //
  1106. // Fill in Urb body
  1107. //
  1108. urb->UrbBulkOrInterruptTransfer.PipeHandle = DeviceExtensionHub->PipeInformation.PipeHandle;
  1109. urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK;
  1110. urb->UrbBulkOrInterruptTransfer.TransferBufferLength =
  1111. DeviceExtensionHub->TransferBufferLength;
  1112. urb->UrbBulkOrInterruptTransfer.TransferBuffer = DeviceExtensionHub->TransferBuffer;
  1113. urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
  1114. urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
  1115. stackSize = DeviceExtensionHub->TopOfStackDeviceObject->StackSize;
  1116. IoInitializeIrp(irp,
  1117. (USHORT) (sizeof(IRP) + stackSize * sizeof(IO_STACK_LOCATION)),
  1118. (CCHAR) stackSize);
  1119. nextStack = IoGetNextIrpStackLocation(irp);
  1120. nextStack->Parameters.Others.Argument1 = urb;
  1121. nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  1122. nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
  1123. IoSetCompletionRoutine(irp, // Irp
  1124. USBH_ChangeIndication,
  1125. DeviceExtensionHub, // context
  1126. TRUE, // invoke on success
  1127. TRUE, // invoke on error
  1128. TRUE); // invoke on cancel
  1129. //
  1130. // Call the USB stack
  1131. //
  1132. //
  1133. // reset the abort event to not-signaled
  1134. //
  1135. KeResetEvent(&DeviceExtensionHub->AbortEvent);
  1136. ntStatus = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject, irp);
  1137. //
  1138. // completion routine will handle errors.
  1139. //
  1140. SubmitIntTrans_Exit:
  1141. USBH_KdPrint((2,"'Exit SubmitIntTrans %x\n", ntStatus));
  1142. return ntStatus;
  1143. }
  1144. NTSTATUS
  1145. USBH_QueryCapsComplete(
  1146. IN PDEVICE_OBJECT PNull,
  1147. IN PIRP Irp,
  1148. IN PVOID Context)
  1149. /* ++
  1150. *
  1151. * Description:
  1152. *
  1153. * This is a call back when the listen of Interrupt control completes.
  1154. *
  1155. * Arguments:
  1156. *
  1157. * pDeviceObject - should be NULL in our case pIrp - the Irp that is completed
  1158. * for the interrupt transfer. pContext - context value for this Irp.
  1159. *
  1160. * Return:
  1161. *
  1162. * NTSTATUS
  1163. *
  1164. * -- */
  1165. {
  1166. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  1167. NTSTATUS ntStatus;
  1168. PDEVICE_CAPABILITIES deviceCapabilities;
  1169. PIO_STACK_LOCATION ioStack;
  1170. deviceExtensionHub = Context;
  1171. ntStatus = Irp->IoStatus.Status;
  1172. if (Irp->PendingReturned) {
  1173. IoMarkIrpPending(Irp);
  1174. }
  1175. //cause we said 'invoke on success'
  1176. USBH_ASSERT(NT_SUCCESS(ntStatus));
  1177. ioStack = IoGetCurrentIrpStackLocation(Irp);
  1178. deviceCapabilities = ioStack->Parameters.DeviceCapabilities.Capabilities;
  1179. USBH_ASSERT(ioStack != NULL);
  1180. USBH_ASSERT(ioStack->MajorFunction == IRP_MJ_PNP);
  1181. USBH_ASSERT(ioStack->MinorFunction == IRP_MN_QUERY_CAPABILITIES);
  1182. deviceCapabilities->SurpriseRemovalOK = TRUE;
  1183. USBH_KdPrint((1,"'Setting SurpriseRemovalOK to TRUE\n"));
  1184. return ntStatus;
  1185. }
  1186. NTSTATUS
  1187. USBH_HRPPCancelComplete(
  1188. IN PDEVICE_OBJECT DeviceObject,
  1189. IN PIRP Irp,
  1190. IN PVOID Context
  1191. )
  1192. {
  1193. PLONG lock = (PLONG) Context;
  1194. if (InterlockedExchange(lock, 3) == 1) {
  1195. return STATUS_MORE_PROCESSING_REQUIRED;
  1196. }
  1197. return STATUS_SUCCESS;
  1198. }
  1199. NTSTATUS
  1200. USBH_HubResetParentPort(
  1201. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  1202. )
  1203. /*++
  1204. Routine Description:
  1205. Resets the hub parent port.
  1206. Arguments:
  1207. Return Value:
  1208. NTSTATUS
  1209. --*/
  1210. {
  1211. NTSTATUS ntStatus, status = STATUS_SUCCESS;
  1212. PIRP irp;
  1213. KEVENT event;
  1214. IO_STATUS_BLOCK ioStatus;
  1215. LARGE_INTEGER dueTime;
  1216. LONG lock;
  1217. USBH_KdPrint((1,"'Reset Hub Parent Port, Hub DevExt: %x, PDO: %x\n",
  1218. DeviceExtensionHub, DeviceExtensionHub->PhysicalDeviceObject));
  1219. LOGENTRY(LOG_PNP, "HRPP", DeviceExtensionHub,
  1220. DeviceExtensionHub->TopOfStackDeviceObject,
  1221. DeviceExtensionHub->RootHubPdo);
  1222. //
  1223. // issue a synchronous request
  1224. //
  1225. KeInitializeEvent(&event, NotificationEvent, FALSE);
  1226. irp = IoBuildDeviceIoControlRequest(
  1227. IOCTL_INTERNAL_USB_RESET_PORT,
  1228. DeviceExtensionHub->TopOfStackDeviceObject,
  1229. NULL,
  1230. 0,
  1231. NULL,
  1232. 0,
  1233. TRUE, /* INTERNAL */
  1234. &event,
  1235. &ioStatus);
  1236. if (irp == NULL) {
  1237. return STATUS_INSUFFICIENT_RESOURCES;
  1238. }
  1239. lock = 0;
  1240. IoSetCompletionRoutine(
  1241. irp,
  1242. USBH_HRPPCancelComplete,
  1243. &lock,
  1244. TRUE,
  1245. TRUE,
  1246. TRUE
  1247. );
  1248. //
  1249. // Call the class driver to perform the operation. If the returned status
  1250. // is PENDING, wait for the request to complete.
  1251. //
  1252. ntStatus = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  1253. irp);
  1254. if (ntStatus == STATUS_PENDING) {
  1255. dueTime.QuadPart = -10000 * ESD_RESET_TIMEOUT;
  1256. status = KeWaitForSingleObject(
  1257. &event,
  1258. Suspended,
  1259. KernelMode,
  1260. FALSE,
  1261. &dueTime);
  1262. if (status == STATUS_TIMEOUT) {
  1263. LOGENTRY(LOG_PNP, "HRPX", DeviceExtensionHub,
  1264. DeviceExtensionHub->TopOfStackDeviceObject,
  1265. DeviceExtensionHub->RootHubPdo);
  1266. USBH_KdPrint((1,"'Reset Hub Parent Port timed out!\n"));
  1267. if (InterlockedExchange(&lock, 1) == 0) {
  1268. //
  1269. // We got it to the IRP before it was completed. We can cancel
  1270. // the IRP without fear of losing it, as the completion routine
  1271. // won't let go of the IRP until we say so.
  1272. //
  1273. IoCancelIrp(irp);
  1274. //
  1275. // Release the completion routine. If it already got there,
  1276. // then we need to complete it ourselves. Otherwise we got
  1277. // through IoCancelIrp before the IRP completed entirely.
  1278. //
  1279. if (InterlockedExchange(&lock, 2) == 3) {
  1280. //
  1281. // Mark it pending because we switched threads.
  1282. //
  1283. IoMarkIrpPending(irp);
  1284. IoCompleteRequest(irp, IO_NO_INCREMENT);
  1285. }
  1286. }
  1287. KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
  1288. // Return STATUS_TIMEOUT
  1289. ioStatus.Status = status;
  1290. }
  1291. } else {
  1292. ioStatus.Status = ntStatus;
  1293. }
  1294. ntStatus = ioStatus.Status;
  1295. return ntStatus;
  1296. }
  1297. VOID
  1298. USBH_HubESDRecoveryDPC(
  1299. IN PKDPC Dpc,
  1300. IN PVOID DeferredContext,
  1301. IN PVOID SystemArgument1,
  1302. IN PVOID SystemArgument2
  1303. )
  1304. /*++
  1305. Routine Description:
  1306. This routine runs at DISPATCH_LEVEL IRQL.
  1307. Arguments:
  1308. Dpc - Pointer to the DPC object.
  1309. DeferredContext -
  1310. SystemArgument1 - not used.
  1311. SystemArgument2 - not used.
  1312. Return Value:
  1313. None.
  1314. --*/
  1315. {
  1316. PHUB_ESD_RECOVERY_CONTEXT hubESDRecoveryContext = DeferredContext;
  1317. PDEVICE_EXTENSION_HUB deviceExtensionHub =
  1318. hubESDRecoveryContext->DeviceExtensionHub;
  1319. PUSBH_HUB_ESD_RECOVERY_WORK_ITEM workItemHubESDRecovery;
  1320. USBH_KdPrint((1,"'Hub ESD Recovery DPC\n"));
  1321. UsbhExFreePool(hubESDRecoveryContext);
  1322. InterlockedExchange(&deviceExtensionHub->InESDRecovery, 0);
  1323. if (!(deviceExtensionHub->HubFlags & HUBFLAG_DEVICE_STOPPING)) {
  1324. //
  1325. // Schedule a work item to process this.
  1326. //
  1327. workItemHubESDRecovery = UsbhExAllocatePool(NonPagedPool,
  1328. sizeof(USBH_HUB_ESD_RECOVERY_WORK_ITEM));
  1329. if (workItemHubESDRecovery) {
  1330. workItemHubESDRecovery->DeviceExtensionHub = deviceExtensionHub;
  1331. ExInitializeWorkItem(&workItemHubESDRecovery->WorkQueueItem,
  1332. USBH_HubESDRecoveryWorker,
  1333. workItemHubESDRecovery);
  1334. LOGENTRY(LOG_PNP, "hESD", deviceExtensionHub,
  1335. &workItemHubESDRecovery->WorkQueueItem, 0);
  1336. ExQueueWorkItem(&workItemHubESDRecovery->WorkQueueItem,
  1337. DelayedWorkQueue);
  1338. // The WorkItem is freed by USBH_HubESDRecoveryWorker()
  1339. // Don't try to access the WorkItem after it is queued.
  1340. } else {
  1341. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1342. }
  1343. } else {
  1344. USBH_KdPrint((1,"'Hub stopping, nothing to do\n"));
  1345. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1346. }
  1347. }
  1348. NTSTATUS
  1349. USBH_ScheduleESDRecovery(
  1350. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  1351. )
  1352. /* ++
  1353. *
  1354. * Description:
  1355. *
  1356. * Schedules the timer event to handle a hub ESD failure.
  1357. *
  1358. *
  1359. * Arguments:
  1360. *
  1361. * Return:
  1362. *
  1363. * -- */
  1364. {
  1365. PHUB_ESD_RECOVERY_CONTEXT hubESDRecoveryContext = NULL;
  1366. LARGE_INTEGER dueTime;
  1367. NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
  1368. // Only do this for external hubs.
  1369. if (IS_ROOT_HUB(DeviceExtensionHub)) {
  1370. USBH_KdPrint((1,"'RootHub failed\n"));
  1371. return STATUS_UNSUCCESSFUL;
  1372. }
  1373. if (InterlockedExchange(&DeviceExtensionHub->InESDRecovery, 1) == 1) {
  1374. // We already have a timer event scheduled for this. Don't reschedule.
  1375. } else {
  1376. USBH_KdPrint((1,"'Schedule ESD Recovery\n"));
  1377. LOGENTRY(LOG_PNP, "ESDs", DeviceExtensionHub,
  1378. DeviceExtensionHub->HubFlags, 0);
  1379. hubESDRecoveryContext = UsbhExAllocatePool(NonPagedPool,
  1380. sizeof(*hubESDRecoveryContext));
  1381. if (hubESDRecoveryContext) {
  1382. hubESDRecoveryContext->DeviceExtensionHub = DeviceExtensionHub;
  1383. KeInitializeTimer(&hubESDRecoveryContext->TimeoutTimer);
  1384. KeInitializeDpc(&hubESDRecoveryContext->TimeoutDpc,
  1385. USBH_HubESDRecoveryDPC,
  1386. hubESDRecoveryContext);
  1387. dueTime.QuadPart = -10000 * ESD_RECOVERY_TIMEOUT;
  1388. KeSetTimer(&hubESDRecoveryContext->TimeoutTimer,
  1389. dueTime,
  1390. &hubESDRecoveryContext->TimeoutDpc);
  1391. USBH_INC_PENDING_IO_COUNT(DeviceExtensionHub);
  1392. ntStatus = STATUS_SUCCESS;
  1393. }
  1394. }
  1395. return ntStatus;
  1396. }
  1397. NTSTATUS
  1398. USBH_HubESDRecoverySetD0Completion(
  1399. IN PDEVICE_OBJECT DeviceObject,
  1400. IN UCHAR MinorFunction,
  1401. IN POWER_STATE PowerState,
  1402. IN PVOID Context,
  1403. IN PIO_STATUS_BLOCK IoStatus
  1404. )
  1405. /*++
  1406. Routine Description:
  1407. Arguments:
  1408. DeviceObject - Pointer to the device object for the class device.
  1409. Irp - Irp completed.
  1410. Context - Driver defined context.
  1411. Return Value:
  1412. The function value is the final status from the operation.
  1413. --*/
  1414. {
  1415. NTSTATUS ntStatus, status;
  1416. PDEVICE_EXTENSION_HUB deviceExtensionHub = Context;
  1417. ntStatus = IoStatus->Status;
  1418. if (NT_SUCCESS(ntStatus)) {
  1419. // Hub is now powered back on and fully recovered. Now find the
  1420. // devices attached to the hub.
  1421. deviceExtensionHub->HubFlags &=
  1422. ~(HUBFLAG_HUB_HAS_LOST_BRAINS | HUBFLAG_HUB_FAILURE);
  1423. // Don't allow selective suspend while post-ESD enumeration is
  1424. // pending.
  1425. deviceExtensionHub->HubFlags |= HUBFLAG_POST_ESD_ENUM_PENDING;
  1426. USBH_IoInvalidateDeviceRelations(deviceExtensionHub->PhysicalDeviceObject,
  1427. BusRelations);
  1428. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1429. } else {
  1430. // Setting hub parent port to D0 failed, we are likely still
  1431. // experiencing ESD. Reschedule the ESD recovery.
  1432. status = USBH_ScheduleESDRecovery(deviceExtensionHub);
  1433. if (status == STATUS_SUCCESS) {
  1434. // Remove extra pending count bump
  1435. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1436. }
  1437. }
  1438. return ntStatus;
  1439. }
  1440. NTSTATUS
  1441. USBH_HubESDRecoverySetD3Completion(
  1442. IN PDEVICE_OBJECT DeviceObject,
  1443. IN UCHAR MinorFunction,
  1444. IN POWER_STATE PowerState,
  1445. IN PVOID Context,
  1446. IN PIO_STATUS_BLOCK IoStatus
  1447. )
  1448. /*++
  1449. Routine Description:
  1450. Arguments:
  1451. DeviceObject - Pointer to the device object for the class device.
  1452. Irp - Irp completed.
  1453. Context - Driver defined context.
  1454. Return Value:
  1455. The function value is the final status from the operation.
  1456. --*/
  1457. {
  1458. NTSTATUS ntStatus;
  1459. PKEVENT pEvent = Context;
  1460. KeSetEvent(pEvent, 1, FALSE);
  1461. ntStatus = IoStatus->Status;
  1462. return ntStatus;
  1463. }
  1464. VOID
  1465. USBH_HubESDRecoveryWorker(
  1466. IN PVOID Context)
  1467. /* ++
  1468. *
  1469. * Description:
  1470. *
  1471. * Work item scheduled to handle a hub ESD failure.
  1472. *
  1473. *
  1474. * Arguments:
  1475. *
  1476. * Return:
  1477. *
  1478. * -- */
  1479. {
  1480. PUSBH_HUB_ESD_RECOVERY_WORK_ITEM workItemHubESDRecovery;
  1481. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  1482. PDEVICE_EXTENSION_PORT hubParentDeviceExtensionPort;
  1483. POWER_STATE powerState;
  1484. PORT_STATE portState;
  1485. NTSTATUS ntStatus, status;
  1486. PAGED_CODE();
  1487. workItemHubESDRecovery = Context;
  1488. deviceExtensionHub = workItemHubESDRecovery->DeviceExtensionHub;
  1489. UsbhExFreePool(workItemHubESDRecovery);
  1490. USBH_KdPrint((1,"'Hub ESD Recovery Worker\n"));
  1491. // NB: Because I now check for HUBFLAG_DEVICE_STOPPING in
  1492. // USBH_HubESDRecoveryDPC, some of the following sanity checking might
  1493. // not be necessary, but I'll leave it in anyway to be safe.
  1494. // In the case where there are nested hubs, the hub device extension
  1495. // for one of the downstream hubs might be invalid by the time this
  1496. // workitem is called. Check for that here.
  1497. if (deviceExtensionHub->ExtensionType != EXTENSION_TYPE_HUB) {
  1498. USBH_KdPrint((1,"'Downstream hub already removed, nothing to do\n"));
  1499. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1500. return;
  1501. }
  1502. // If the hub has been physically removed from the bus, then we have
  1503. // nothing to do here.
  1504. if (!deviceExtensionHub->PhysicalDeviceObject) {
  1505. USBH_KdPrint((1,"'Hub has been removed (no PDO), nothing to do\n"));
  1506. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1507. return;
  1508. }
  1509. hubParentDeviceExtensionPort = deviceExtensionHub->PhysicalDeviceObject->DeviceExtension;
  1510. LOGENTRY(LOG_PNP, "ESDw", deviceExtensionHub,
  1511. hubParentDeviceExtensionPort->PortPdoFlags, deviceExtensionHub->HubFlags);
  1512. // USBH_KdPrint((1,"'Hub parent port PortPdoFlags: %x\n",
  1513. // hubParentDeviceExtensionPort->PortPdoFlags));
  1514. // We definitely need the following check, so don't remove this.
  1515. if (hubParentDeviceExtensionPort->PortPdoFlags &
  1516. (PORTPDO_DELETED_PDO | PORTPDO_DELETE_PENDING) ||
  1517. !(hubParentDeviceExtensionPort->PortPdoFlags & PORTPDO_DEVICE_IS_HUB)) {
  1518. USBH_KdPrint((1,"'Hub has been removed, nothing to do\n"));
  1519. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1520. return;
  1521. }
  1522. // Be sure that the hub hasn't stopped or set to a low power state before
  1523. // this workitem had a chance to run.
  1524. if (deviceExtensionHub->HubFlags & HUBFLAG_DEVICE_STOPPING) {
  1525. USBH_KdPrint((1,"'Hub has is stopping or in low power, nothing to do\n"));
  1526. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1527. return;
  1528. }
  1529. // One last sanity check. Check the port status for the parent port
  1530. // of this hub and be sure that a device is still connected.
  1531. ntStatus = USBH_SyncGetPortStatus(
  1532. hubParentDeviceExtensionPort->DeviceExtensionHub,
  1533. hubParentDeviceExtensionPort->PortNumber,
  1534. (PUCHAR) &portState,
  1535. sizeof(portState));
  1536. if (!NT_SUCCESS(ntStatus) ||
  1537. !(portState.PortStatus & PORT_STATUS_CONNECT)) {
  1538. USBH_KdPrint((1,"'Hub device has been physically disconnected, nothing to do\n"));
  1539. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1540. return;
  1541. }
  1542. // Reset the parent port for this hub.
  1543. ntStatus = USBH_HubResetParentPort(deviceExtensionHub);
  1544. USBH_KdPrint((1,"'USBH_HubResetParentPort returned %x\n", ntStatus));
  1545. if (ntStatus == STATUS_INVALID_PARAMETER) {
  1546. // Looks like we lost the port PDO somewhere along the way.
  1547. // (Call to USBH_ResetDevice from USBH_RestoreDevice failed.)
  1548. // Bail out of this ESD recovery, and the user will have to
  1549. // unplug/replug the hub to get it back. Maybe we can revisit
  1550. // this later.
  1551. USBH_KdPrint((1,"'Lost hub PDO during reset, bail\n"));
  1552. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1553. return;
  1554. }
  1555. if (ntStatus == STATUS_TIMEOUT) {
  1556. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1557. return;
  1558. }
  1559. if (NT_SUCCESS(ntStatus)) {
  1560. KEVENT event;
  1561. // On Memphis we must tell power management that the hub is in D3.
  1562. // (It thinks that the hub is in D0 because it does not know that
  1563. // resetting the hub's parent port causes the hub to lose power.
  1564. //
  1565. // We need to do this on Memphis because power management appears to
  1566. // track the power state of devices and will suppress sending a power
  1567. // request to a device if it thinks that the device is already in that
  1568. // power state. Under NT they don't seem to care and will send the
  1569. // request along anyway.
  1570. KeInitializeEvent(&event, NotificationEvent, FALSE);
  1571. powerState.DeviceState = PowerDeviceD3;
  1572. // "Power down" the hub.
  1573. ntStatus = PoRequestPowerIrp(deviceExtensionHub->PhysicalDeviceObject,
  1574. IRP_MN_SET_POWER,
  1575. powerState,
  1576. USBH_HubESDRecoverySetD3Completion,
  1577. &event,
  1578. NULL);
  1579. USBH_ASSERT(ntStatus == STATUS_PENDING);
  1580. if (ntStatus == STATUS_PENDING) {
  1581. USBH_KdPrint((2,"'Wait for single object\n"));
  1582. status = KeWaitForSingleObject(&event,
  1583. Suspended,
  1584. KernelMode,
  1585. FALSE,
  1586. NULL);
  1587. USBH_KdPrint((2,"'Wait for single object, returned %x\n", status));
  1588. }
  1589. deviceExtensionHub->CurrentPowerState = PowerDeviceD3;
  1590. powerState.DeviceState = PowerDeviceD0;
  1591. // Power up the hub.
  1592. ntStatus = PoRequestPowerIrp(deviceExtensionHub->PhysicalDeviceObject,
  1593. IRP_MN_SET_POWER,
  1594. powerState,
  1595. USBH_HubESDRecoverySetD0Completion,
  1596. deviceExtensionHub,
  1597. NULL);
  1598. if (ntStatus != STATUS_PENDING) {
  1599. // Power IRP request was not successful. Reschedule the recovery
  1600. // so that we can try again later.
  1601. status = USBH_ScheduleESDRecovery(deviceExtensionHub);
  1602. if (status == STATUS_SUCCESS) {
  1603. // Remove extra pending count bump
  1604. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1605. }
  1606. }
  1607. } else {
  1608. // Reset hub parent port failed, we are likely still experiencing ESD.
  1609. // Reschedule the ESD recovery.
  1610. status = USBH_ScheduleESDRecovery(deviceExtensionHub);
  1611. if (status == STATUS_SUCCESS) {
  1612. // Remove extra pending count bump
  1613. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1614. }
  1615. }
  1616. }
  1617. NTSTATUS
  1618. USBH_ChangeIndication(
  1619. IN PDEVICE_OBJECT PNull,
  1620. IN PIRP Irp,
  1621. IN PVOID Context)
  1622. /* ++
  1623. *
  1624. * Description:
  1625. *
  1626. * This is a call back when the listen of Interrupt control completes.
  1627. *
  1628. * Arguments:
  1629. *
  1630. * pDeviceObject - should be NULL in our case pIrp - the Irp that is completed
  1631. * for the interrupt transfer. pContext - context value for this Irp.
  1632. *
  1633. * Return:
  1634. *
  1635. * NTSTATUS
  1636. *
  1637. * -- */
  1638. {
  1639. PURB urb; // the Urb assocaited with this Irp
  1640. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  1641. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  1642. PUSBH_WORK_ITEM workItem;
  1643. BOOLEAN requestReset = FALSE;
  1644. USHORT portNumber, numberOfPorts;
  1645. deviceExtensionHub = (PDEVICE_EXTENSION_HUB) Context; // the context is
  1646. // DeviceExtensionHub
  1647. urb = &deviceExtensionHub->Urb;
  1648. USBH_KdPrint((2,"'ChangeIndication Irp status %x URB status = %x\n",
  1649. Irp->IoStatus.Status, urb->UrbHeader.Status));
  1650. LOGENTRY(LOG_PNP, "chID", deviceExtensionHub, urb, Irp);
  1651. if (NT_ERROR(Irp->IoStatus.Status) ||
  1652. USBD_ERROR(urb->UrbHeader.Status) ||
  1653. (deviceExtensionHub->HubFlags & (HUBFLAG_HUB_FAILURE |
  1654. HUBFLAG_DEVICE_STOPPING)) ||
  1655. urb->UrbHeader.Status == USBD_STATUS_CANCELED) {
  1656. requestReset = TRUE;
  1657. deviceExtensionHub->ErrorCount++;
  1658. //
  1659. // An error has occurred submitting the interrupt
  1660. // transfer, possible causes:
  1661. //
  1662. // 1. the interrupt pipe is stalled
  1663. // 2. the hub is experiencing a temporary problem
  1664. // 3. the hub is messed up and we need to reset it
  1665. // 4. we are stopping the device
  1666. // 5. the hub has been removed from the bus
  1667. //
  1668. // In any case we will need to take some action.
  1669. //
  1670. // if an abort event is waiting signal it
  1671. //
  1672. LOGENTRY(LOG_PNP, "cERR", deviceExtensionHub,
  1673. &deviceExtensionHub->AbortEvent, deviceExtensionHub->ErrorCount);
  1674. if ((deviceExtensionHub->HubFlags & HUBFLAG_DEVICE_STOPPING) ||
  1675. deviceExtensionHub->ErrorCount > USBH_MAX_ERRORS ||
  1676. (deviceExtensionHub->HubFlags & HUBFLAG_HUB_FAILURE) ||
  1677. Irp->IoStatus.Status == STATUS_DELETE_PENDING) {
  1678. //
  1679. // shutting down the hub, do not schedule any more
  1680. // work items while in this state.
  1681. //
  1682. USBH_KdPrint((2,"'ChangeIndication, device stopping or hub failure\n"));
  1683. #if DBG
  1684. if (deviceExtensionHub->ErrorCount > USBH_MAX_ERRORS) {
  1685. // we may ideed have a hub failure, more liekly
  1686. // the device was just unplugged, if the hub has
  1687. // failed we should pick this up when we try to
  1688. // do control tranfers to it.
  1689. LOGENTRY(LOG_PNP, "xERR", deviceExtensionHub,
  1690. 0, deviceExtensionHub->ErrorCount);
  1691. }
  1692. #endif
  1693. // Set the AbortEvent after checking the HubFlags, not before.
  1694. // As soon as the AbortEvent is set the thread waiting on it may
  1695. // run and cause the HubFlags to change.
  1696. //
  1697. KeSetEvent(&deviceExtensionHub->AbortEvent,
  1698. 1,
  1699. FALSE);
  1700. goto USBH_ChangeIndication_Done;
  1701. }
  1702. // Set the AbortEvent after checking the HubFlags, not before.
  1703. //
  1704. KeSetEvent(&deviceExtensionHub->AbortEvent,
  1705. 1,
  1706. FALSE);
  1707. } else {
  1708. // reset error count on successful
  1709. // transfer
  1710. LOGENTRY(LOG_PNP, "zERR", deviceExtensionHub,
  1711. 0, deviceExtensionHub->ErrorCount);
  1712. deviceExtensionHub->ErrorCount = 0;
  1713. }
  1714. USBH_KdPrint((2,"'Enter ChangeIndication Transfer %x \n",
  1715. deviceExtensionHub->TransferBuffer));
  1716. #if DBG
  1717. {
  1718. ULONG i;
  1719. for (i=0; i< deviceExtensionHub->TransferBufferLength; i++) {
  1720. USBH_KdPrint((2,"'TransferBuffer[%d] = %x\n", i,
  1721. deviceExtensionHub->TransferBuffer[i]));
  1722. }
  1723. }
  1724. #endif
  1725. //
  1726. // Schedule a work item to process this change
  1727. //
  1728. workItem = UsbhExAllocatePool(NonPagedPool, sizeof(USBH_WORK_ITEM)+
  1729. deviceExtensionHub->TransferBufferLength);
  1730. if (workItem) {
  1731. NTSTATUS status;
  1732. workItem->Flags = 0;
  1733. if (requestReset) {
  1734. workItem->Flags = USBH_WKFLAG_REQUEST_RESET;
  1735. }
  1736. // i-friend, indicate we have a workitem pendingf=
  1737. {
  1738. LONG cWKPendingCount;
  1739. cWKPendingCount = InterlockedIncrement(
  1740. &deviceExtensionHub->ChangeIndicationWorkitemPending);
  1741. // Prevent hub from powering down or being removed if there is a
  1742. // ChangeIndicationAckChange pending.
  1743. if (cWKPendingCount == 1) {
  1744. KeResetEvent(&deviceExtensionHub->CWKEvent);
  1745. }
  1746. }
  1747. workItem->DeviceExtensionHub = deviceExtensionHub;
  1748. USBH_ASSERT(deviceExtensionHub->WorkItemToQueue == NULL);
  1749. deviceExtensionHub->WorkItemToQueue = workItem;
  1750. RtlCopyMemory(&workItem->Data[0], deviceExtensionHub->TransferBuffer,
  1751. deviceExtensionHub->TransferBufferLength);
  1752. ExInitializeWorkItem(&workItem->WorkQueueItem,
  1753. USBH_ChangeIndicationWorker,
  1754. workItem);
  1755. // now process the change, this will signal any waiting
  1756. // reset or resume withoutr reqireing a work item
  1757. LOGENTRY(LOG_PNP, "cITM", deviceExtensionHub,
  1758. &workItem->WorkQueueItem, 0);
  1759. numberOfPorts = deviceExtensionHub->HubDescriptor->bNumberOfPorts;
  1760. for (portNumber = 0; portNumber <= numberOfPorts; portNumber++) {
  1761. if (IsBitSet(&workItem->Data[0],
  1762. portNumber)) {
  1763. break;
  1764. }
  1765. }
  1766. // If none of the bits for the ports were set in the loop above
  1767. // (i.e. we can't find a change on any of the ports), then just
  1768. // assume port zero and USBH_ChangeIndicationQueryChange will
  1769. // handle accordingly.
  1770. if (portNumber > numberOfPorts) {
  1771. portNumber = 0;
  1772. }
  1773. status = USBH_ChangeIndicationQueryChange(
  1774. deviceExtensionHub,
  1775. Irp,
  1776. urb,
  1777. portNumber);
  1778. if (NT_ERROR(status)) {
  1779. HUB_FAILURE(deviceExtensionHub);
  1780. }
  1781. }
  1782. #if DBG
  1783. else {
  1784. LOGENTRY(LOG_PNP, "XMEM", deviceExtensionHub, 0, 0);
  1785. UsbhWarning(NULL,
  1786. "Memory allocation error in USBH_ChangeIndication, cannot process hub changes.\n",
  1787. FALSE);
  1788. }
  1789. #endif
  1790. USBH_ChangeIndication_Done:
  1791. //
  1792. // keep the irp
  1793. //
  1794. return STATUS_MORE_PROCESSING_REQUIRED;
  1795. }
  1796. NTSTATUS
  1797. USBH_ChangeIndicationQueryChange(
  1798. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1799. IN PIRP Irp,
  1800. IN PURB Urb,
  1801. IN USHORT Port
  1802. )
  1803. /* ++
  1804. *
  1805. * Description:
  1806. *
  1807. * Queries what changed, ie checks the port to see what changed
  1808. *
  1809. * Arguments:
  1810. *
  1811. * Return:
  1812. *
  1813. *
  1814. * -- */
  1815. {
  1816. NTSTATUS ntStatus = STATUS_SUCCESS;
  1817. PIO_STACK_LOCATION nextStack; // next stack of the Irp
  1818. CHAR stackSize;
  1819. PUSBH_WORK_ITEM workItem;
  1820. LONG cWKPendingCount;
  1821. LOGENTRY(LOG_PNP, "QCH>", DeviceExtensionHub, Urb, Port);
  1822. // bump the io count now this represents pending workitem
  1823. // we will queue on completion of this irp
  1824. USBH_INC_PENDING_IO_COUNT(DeviceExtensionHub);
  1825. if (Port == 0) {
  1826. //
  1827. // if we have a hub status change just queue our
  1828. // workitem and get out
  1829. //
  1830. USBH_ASSERT(DeviceExtensionHub->WorkItemToQueue != NULL);
  1831. workItem = DeviceExtensionHub->WorkItemToQueue;
  1832. DeviceExtensionHub->WorkItemToQueue = NULL;
  1833. LOGENTRY(LOG_PNP, "qIT2", DeviceExtensionHub,
  1834. &workItem->WorkQueueItem, 0);
  1835. ExQueueWorkItem(&workItem->WorkQueueItem,
  1836. DelayedWorkQueue);
  1837. return ntStatus;
  1838. }
  1839. Urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
  1840. Urb->UrbHeader.Function = URB_FUNCTION_CLASS_OTHER;
  1841. //
  1842. // Fill in Urb body
  1843. //
  1844. UsbhBuildVendorClassUrb(Urb,
  1845. NULL,
  1846. URB_FUNCTION_CLASS_OTHER,
  1847. USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
  1848. REQUEST_TYPE_GET_PORT_STATUS,
  1849. REQUEST_GET_STATUS,
  1850. 0,
  1851. Port,
  1852. sizeof(DeviceExtensionHub->PortStateBuffer),
  1853. &DeviceExtensionHub->PortStateBuffer);
  1854. DeviceExtensionHub->ResetPortNumber = Port;
  1855. stackSize = DeviceExtensionHub->TopOfStackDeviceObject->StackSize;
  1856. IoInitializeIrp(Irp,
  1857. (USHORT) (sizeof(IRP) + stackSize * sizeof(IO_STACK_LOCATION)),
  1858. (CCHAR) stackSize);
  1859. nextStack = IoGetNextIrpStackLocation(Irp);
  1860. nextStack->Parameters.Others.Argument1 = Urb;
  1861. nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  1862. nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
  1863. IoSetCompletionRoutine(Irp, // Irp
  1864. USBH_ChangeIndicationProcessChange,
  1865. DeviceExtensionHub, // context
  1866. TRUE, // invoke on success
  1867. TRUE, // invoke on error
  1868. TRUE); // invoke on cancel
  1869. ntStatus = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject, Irp);
  1870. return ntStatus;
  1871. }
  1872. NTSTATUS
  1873. USBH_ChangeIndicationProcessChange(
  1874. IN PDEVICE_OBJECT PNull,
  1875. IN PIRP Irp,
  1876. IN PVOID Context
  1877. )
  1878. /* ++
  1879. *
  1880. * Description:
  1881. *
  1882. * Take some action based on change
  1883. *
  1884. * Arguments:
  1885. *
  1886. * Return:
  1887. *
  1888. *
  1889. * -- */
  1890. {
  1891. PPORT_STATE currentPortState;
  1892. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  1893. PUSBH_WORK_ITEM workItem;
  1894. PURB urb;
  1895. NTSTATUS status;
  1896. USHORT wFeatureSelector;
  1897. LONG cWKPendingCount;
  1898. deviceExtensionHub = Context;
  1899. currentPortState = &(deviceExtensionHub->PortStateBuffer);
  1900. urb = &deviceExtensionHub->Urb;
  1901. LOGENTRY(LOG_PNP, "PCHc", deviceExtensionHub,
  1902. *((PULONG)currentPortState), Irp);
  1903. if ((NT_SUCCESS(Irp->IoStatus.Status) ||
  1904. USBD_SUCCESS(urb->UrbHeader.Status)) &&
  1905. (currentPortState->PortChange & PORT_STATUS_RESET ||
  1906. currentPortState->PortChange & PORT_STATUS_ENABLE)) {
  1907. //
  1908. // bit 4 RESET completed
  1909. //
  1910. // no workitem
  1911. LOGENTRY(LOG_PNP, "nITM", deviceExtensionHub,
  1912. 0, 0);
  1913. USBH_DEC_PENDING_IO_COUNT(deviceExtensionHub);
  1914. UsbhExFreePool(deviceExtensionHub->WorkItemToQueue);
  1915. deviceExtensionHub->WorkItemToQueue = NULL;
  1916. //
  1917. // Signal the PNP thread that a the reset has completed
  1918. //
  1919. // once we do this we can get antother change indication
  1920. // so we free the workitem first.
  1921. //
  1922. LOGENTRY(LOG_PNP, "RESc", deviceExtensionHub,
  1923. deviceExtensionHub->ResetPortNumber, 0);
  1924. if (currentPortState->PortChange & PORT_STATUS_RESET) {
  1925. wFeatureSelector = FEATURE_C_PORT_RESET;
  1926. } else {
  1927. wFeatureSelector = FEATURE_C_PORT_ENABLE;
  1928. }
  1929. status = USBH_ChangeIndicationAckChange(
  1930. deviceExtensionHub,
  1931. Irp,
  1932. urb,
  1933. (USHORT)deviceExtensionHub->ResetPortNumber,
  1934. wFeatureSelector);
  1935. return STATUS_MORE_PROCESSING_REQUIRED;
  1936. }
  1937. // if (deviceExtensionHub->HubFlags & HUBFLAG_PENDING_PORT_RESET) {
  1938. // USBH_KdPrint((0,"'port change broke reset\n"));
  1939. // TEST_TRAP();
  1940. // }
  1941. //
  1942. // now queue the workitem to finish the processing
  1943. //
  1944. USBH_ASSERT(deviceExtensionHub->WorkItemToQueue != NULL);
  1945. workItem = deviceExtensionHub->WorkItemToQueue;
  1946. deviceExtensionHub->WorkItemToQueue = NULL;
  1947. LOGENTRY(LOG_PNP, "qITM", deviceExtensionHub,
  1948. &workItem->WorkQueueItem, 0);
  1949. ExQueueWorkItem(&workItem->WorkQueueItem,
  1950. DelayedWorkQueue);
  1951. return STATUS_MORE_PROCESSING_REQUIRED;
  1952. }
  1953. NTSTATUS
  1954. USBH_ChangeIndicationAckChange(
  1955. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  1956. IN PIRP Irp,
  1957. IN PURB Urb,
  1958. IN USHORT Port,
  1959. IN USHORT FeatureSelector
  1960. )
  1961. /* ++
  1962. *
  1963. * Description:
  1964. *
  1965. * Ack a reset change
  1966. *
  1967. * Arguments:
  1968. *
  1969. * Return:
  1970. *
  1971. *
  1972. * -- */
  1973. {
  1974. NTSTATUS ntStatus = STATUS_SUCCESS;
  1975. PIO_STACK_LOCATION nextStack; // next stack of the Irp
  1976. CHAR stackSize;
  1977. LOGENTRY(LOG_PNP, "ACH>", DeviceExtensionHub, FeatureSelector, Port);
  1978. Urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
  1979. Urb->UrbHeader.Function = URB_FUNCTION_CLASS_OTHER;
  1980. //
  1981. // Fill in Urb body
  1982. //
  1983. UsbhBuildVendorClassUrb(Urb,
  1984. NULL,
  1985. URB_FUNCTION_CLASS_OTHER,
  1986. USBD_TRANSFER_DIRECTION_OUT | USBD_SHORT_TRANSFER_OK,
  1987. REQUEST_TYPE_SET_PORT_FEATURE,
  1988. REQUEST_CLEAR_FEATURE,
  1989. FeatureSelector,
  1990. Port,
  1991. 0,
  1992. NULL);
  1993. stackSize = DeviceExtensionHub->TopOfStackDeviceObject->StackSize;
  1994. IoInitializeIrp(Irp,
  1995. (USHORT) (sizeof(IRP) + stackSize * sizeof(IO_STACK_LOCATION)),
  1996. (CCHAR) stackSize);
  1997. nextStack = IoGetNextIrpStackLocation(Irp);
  1998. nextStack->Parameters.Others.Argument1 = Urb;
  1999. nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  2000. nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
  2001. IoSetCompletionRoutine(Irp, // Irp
  2002. USBH_ChangeIndicationAckChangeComplete,
  2003. DeviceExtensionHub, // context
  2004. TRUE, // invoke on success
  2005. TRUE, // invoke on error
  2006. TRUE); // invoke on cancel
  2007. ntStatus = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject, Irp);
  2008. return ntStatus;
  2009. }
  2010. NTSTATUS
  2011. USBH_ChangeIndicationAckChangeComplete(
  2012. IN PDEVICE_OBJECT PNull,
  2013. IN PIRP Irp,
  2014. IN PVOID Context
  2015. )
  2016. /* ++
  2017. *
  2018. * Description:
  2019. *
  2020. * ack a reset change
  2021. *
  2022. * Arguments:
  2023. *
  2024. * Return:
  2025. *
  2026. *
  2027. * -- */
  2028. {
  2029. PPORT_STATE currentPortState;
  2030. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  2031. PPORT_STATE hubExtensionPortState;
  2032. PURB urb;
  2033. PKEVENT resetEvent;
  2034. LONG pendingWorkitem = 0;
  2035. deviceExtensionHub = Context;
  2036. currentPortState = &(deviceExtensionHub->PortStateBuffer);
  2037. urb = &deviceExtensionHub->Urb;
  2038. LOGENTRY(LOG_PNP, "aCHc", deviceExtensionHub,
  2039. *((PULONG)currentPortState), Irp);
  2040. hubExtensionPortState =
  2041. &(deviceExtensionHub->PortData + deviceExtensionHub->ResetPortNumber - 1)->PortState;
  2042. *hubExtensionPortState = *currentPortState;
  2043. resetEvent = InterlockedExchangePointer(&deviceExtensionHub->Event, NULL);
  2044. if (resetEvent) {
  2045. LOGENTRY(LOG_PNP, "WAKr", deviceExtensionHub, resetEvent, 0);
  2046. KeSetEvent(resetEvent,
  2047. 1,
  2048. FALSE);
  2049. }
  2050. USBH_SubmitInterruptTransfer(deviceExtensionHub);
  2051. pendingWorkitem = InterlockedDecrement(
  2052. &deviceExtensionHub->ChangeIndicationWorkitemPending);
  2053. // If USBH_FdoPower or USBH_FdoCleanup is waiting on this
  2054. // ChangeIndicationAckChangeComplete, then signal the thread
  2055. // that it may now continue.
  2056. if (!pendingWorkitem) {
  2057. KeSetEvent(&deviceExtensionHub->CWKEvent, 1, FALSE);
  2058. }
  2059. return STATUS_MORE_PROCESSING_REQUIRED;
  2060. }
  2061. VOID
  2062. USBH_ChangeIndicationWorker(
  2063. IN PVOID Context)
  2064. /* ++
  2065. *
  2066. * Description:
  2067. *
  2068. * Work item scheduled to process a change indication from the hub. we process
  2069. * the URB here and if necessary re-submit the interrupt transfer.
  2070. *
  2071. *
  2072. * Arguments:
  2073. *
  2074. * Return:
  2075. *
  2076. * NTSTATUS
  2077. *
  2078. * -- */
  2079. {
  2080. NTSTATUS ntStatus;
  2081. ULONG numberOfPorts; // total ports on this hub
  2082. ULONG portNumber; // port that has status change
  2083. PDEVICE_EXTENSION_HUB DeviceExtensionHub;
  2084. PUSBH_WORK_ITEM workItem;
  2085. ULONG state;
  2086. //LONG ioCount;
  2087. BOOLEAN newTransfer = FALSE;
  2088. PDEVICE_EXTENSION_PORT hubParentDeviceExtensionPort;
  2089. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  2090. PPORT_DATA p;
  2091. ULONG i;
  2092. PORT_STATE portState;
  2093. LONG pendingWorkitem = 0;
  2094. PAGED_CODE();
  2095. USBH_ASSERT(sizeof(state) == sizeof(HUB_STATE));
  2096. USBH_ASSERT(sizeof(state) == sizeof(PORT_STATE));
  2097. workItem = Context;
  2098. DeviceExtensionHub = workItem->DeviceExtensionHub;
  2099. LOGENTRY(LOG_PNP, "cWK+", DeviceExtensionHub, Context, 0);
  2100. USBH_KdPrint((2,"'Enter ChangeIndicationWorker %x\n", DeviceExtensionHub));
  2101. // lock access to the hub ports
  2102. // pending count inc'ed when work item was schedued
  2103. USBH_KdPrint((2,"'***WAIT hub mutex %x\n", DeviceExtensionHub));
  2104. KeWaitForSingleObject(&DeviceExtensionHub->HubMutex,
  2105. Executive,
  2106. KernelMode,
  2107. FALSE,
  2108. NULL);
  2109. USBH_KdPrint((2,"'***WAIT hub mutex done %x\n", DeviceExtensionHub));
  2110. //
  2111. // device is stopping, perform no processing
  2112. // of the change request.
  2113. //
  2114. if (DeviceExtensionHub->HubFlags & HUBFLAG_DEVICE_STOPPING) {
  2115. // set the abort event since we will not be
  2116. // submitting another transfer
  2117. KeSetEvent(&DeviceExtensionHub->AbortEvent,
  2118. 1,
  2119. FALSE);
  2120. goto USBH_ChangeIndicationWorker_Exit;
  2121. }
  2122. // Check for hub ESD failure.
  2123. // Make sure that this is an external hub.
  2124. if (DeviceExtensionHub->ErrorCount &&
  2125. DeviceExtensionHub->PhysicalDeviceObject != DeviceExtensionHub->RootHubPdo) {
  2126. hubParentDeviceExtensionPort =
  2127. DeviceExtensionHub->PhysicalDeviceObject->DeviceExtension;
  2128. if (hubParentDeviceExtensionPort->PortPdoFlags & PORTPDO_USB_SUSPEND) {
  2129. // Hub likely failed during power up. Don't do recovery here.
  2130. // The Peracom hub (TI chipset) generally fails the first power
  2131. // up if one of its downstream devices caused the wake, but the
  2132. // next power up (from set S0 request) is successful. Performing
  2133. // ESD recovery at this time interferes with this.
  2134. goto USBH_CIW_NoESD;
  2135. }
  2136. // if hub backpointer is null then this device is removed, attempt
  2137. // no ESD crap here.
  2138. if (hubParentDeviceExtensionPort->DeviceExtensionHub == NULL) {
  2139. goto USBH_CIW_NoESD;
  2140. }
  2141. // See if we can differentiate between hub removal and ESD.
  2142. // Check the upstream port status.
  2143. ntStatus = USBH_SyncGetPortStatus(
  2144. hubParentDeviceExtensionPort->DeviceExtensionHub,
  2145. hubParentDeviceExtensionPort->PortNumber,
  2146. (PUCHAR) &portState,
  2147. sizeof(portState));
  2148. // if (!NT_SUCCESS(ntStatus) ||
  2149. // portState.PortStatus & PORT_STATUS_CONNECT) {
  2150. if (NT_SUCCESS(ntStatus) &&
  2151. portState.PortStatus & PORT_STATUS_CONNECT) {
  2152. // ESD
  2153. USBH_KdPrint((1,"'Looks like ESD event (hub failure)\n"));
  2154. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_HAS_LOST_BRAINS)) {
  2155. DeviceExtensionHub->HubFlags |= HUBFLAG_HUB_HAS_LOST_BRAINS;
  2156. LOGENTRY(LOG_PNP, "ESD!", DeviceExtensionHub,
  2157. 0, DeviceExtensionHub->ErrorCount);
  2158. #if DBG
  2159. UsbhWarning(NULL,
  2160. "ESD or hub failure occurred, attempting recovery.\n",
  2161. FALSE);
  2162. #endif
  2163. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  2164. USBH_ASSERT(DeviceExtensionHub->PortData != NULL);
  2165. p = DeviceExtensionHub->PortData;
  2166. for (i=0; i<numberOfPorts; i++, p++) {
  2167. if (p->DeviceObject) {
  2168. USBH_KdPrint((1,"'Marking PDO %x for removal\n", p->DeviceObject));
  2169. deviceExtensionPort = p->DeviceObject->DeviceExtension;
  2170. deviceExtensionPort->PortPdoFlags |= PORTPDO_DELETE_PENDING;
  2171. }
  2172. //
  2173. // Note that we hold onto the hub's reference to the device object here.
  2174. // We need to do this for the case where a hub fails, and the downstream device
  2175. // had open references to it (open files on USB storage device). In this case
  2176. // PnP won't send the remove to the device until the files have been closed, and
  2177. // we need the reference to the device object in the hub device extension so
  2178. // that we can properly cleanup after the device in USBH_FdoCleanup when the
  2179. // hub is removed. If we do not do this then we will fault in
  2180. // USBH_PdoRemoveDevice when trying to dereference the pointer to the hub
  2181. // device extension that this device in connected to because the hub is long
  2182. // gone by then.
  2183. //
  2184. // p->DeviceObject = NULL;
  2185. p->ConnectionStatus = NoDeviceConnected;
  2186. }
  2187. // Tell PnP that there are no devices on this hub.
  2188. // FdoQueryBusRelations will return zero devices for this hub
  2189. // if the HUBFLAG_HUB_HAS_LOST_BRAINS is set.
  2190. USBH_IoInvalidateDeviceRelations(DeviceExtensionHub->PhysicalDeviceObject,
  2191. BusRelations);
  2192. // Start timer to start workitem to reset hub and attempt recovery.
  2193. USBH_ScheduleESDRecovery(DeviceExtensionHub);
  2194. goto USBH_ChangeIndicationWorker_Exit;
  2195. }
  2196. } else {
  2197. // No ESD, hub was removed.
  2198. LOGENTRY(LOG_PNP, "HubY", DeviceExtensionHub,
  2199. DeviceExtensionHub->HubFlags, 0);
  2200. USBH_KdPrint((1,"'Looks like hub was removed\n"));
  2201. DeviceExtensionHub->HubFlags |= HUBFLAG_HUB_GONE;
  2202. // set the abort event since we will not be
  2203. // submitting another transfer (parent hub may already be
  2204. // selectively suspended)
  2205. KeSetEvent(&DeviceExtensionHub->AbortEvent,
  2206. 1,
  2207. FALSE);
  2208. goto USBH_ChangeIndicationWorker_Exit;
  2209. }
  2210. }
  2211. USBH_CIW_NoESD:
  2212. //
  2213. // request reset flag is set indicating that the device
  2214. // needs some attention
  2215. //
  2216. if (workItem->Flags & USBH_WKFLAG_REQUEST_RESET) {
  2217. // reset the hub
  2218. LOGENTRY(LOG_PNP, "rrST", DeviceExtensionHub, Context, 0);
  2219. USBH_ResetHub(DeviceExtensionHub);
  2220. // re-submit the interrupt transfer
  2221. newTransfer = TRUE;
  2222. goto USBH_ChangeIndicationWorker_Exit;
  2223. }
  2224. #if DBG
  2225. {
  2226. ULONG i;
  2227. for (i=0; i< DeviceExtensionHub->TransferBufferLength; i++) {
  2228. USBH_KdPrint((2,"'Data[%d] = %x\n", i,
  2229. workItem->Data[i]));
  2230. }
  2231. }
  2232. #endif
  2233. //
  2234. // Check to see what has changed
  2235. //
  2236. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  2237. for (portNumber = 0; portNumber <= numberOfPorts; portNumber++) {
  2238. if (IsBitSet( &workItem->Data[0],
  2239. portNumber)) {
  2240. break;
  2241. }
  2242. }
  2243. #if 0
  2244. //
  2245. // Work around for Philips hub bug. This should be temporary because it
  2246. // will cost extraneous
  2247. // CPU cycles for spec conformant external hubs. Remove the code for
  2248. // formal build.
  2249. //
  2250. #pragma message( "!!!!! Workaround for Philips external hub Vid==0471 && Rev==0030 !!!!!")
  2251. if ((0x0471 == DeviceExtensionHub->DeviceDescriptor.idVendor) &&
  2252. // (0x0101 == DeviceExtensionHub->DeviceDescriptor.idProduct) &&
  2253. (0x0030 == DeviceExtensionHub->DeviceDescriptor.bcdDevice)) {
  2254. //
  2255. // This phillips external hub reports port status-change shift by 1
  2256. // bit
  2257. //
  2258. USBH_KdBreak(("Shift By One hack fo philips hub\n"));
  2259. portNumber--;
  2260. }
  2261. #endif /* PHILIPS_HACK_ENABLED */
  2262. if (portNumber > numberOfPorts) {
  2263. USBH_KdPrint((2,"'StatusChangeIndication nothing has changed\n"));
  2264. //
  2265. // nothing to do here
  2266. // put the listen back down and get out.
  2267. //
  2268. newTransfer = TRUE;
  2269. goto USBH_ChangeIndicationWorker_Exit;
  2270. }
  2271. USBH_KdPrint((2,"'Port number %x changed (0 indicates hub)\n", portNumber));
  2272. LOGENTRY(LOG_PNP, "pCHG", DeviceExtensionHub, Context, portNumber);
  2273. if (portNumber != 0) {
  2274. ntStatus = USBH_SyncGetPortStatus(DeviceExtensionHub,
  2275. (USHORT)portNumber,
  2276. (PUCHAR) &state,
  2277. sizeof(state));
  2278. } else {
  2279. ntStatus = USBH_SyncGetHubStatus(DeviceExtensionHub,
  2280. (PUCHAR) &state,
  2281. sizeof(state));
  2282. }
  2283. if (!NT_SUCCESS(ntStatus)) {
  2284. USBH_KdBreak(("ChangeIndication GetStatus failed code %x\n", ntStatus));
  2285. //
  2286. // an error occured getting the status for the port
  2287. // put the listen back down and get out
  2288. //
  2289. // this condition may be temoprary in which case the
  2290. // listen will cause us to retry
  2291. DeviceExtensionHub->ErrorCount++;
  2292. if (DeviceExtensionHub->ErrorCount > USBH_MAX_ERRORS) {
  2293. HUB_FAILURE(DeviceExtensionHub);
  2294. } else {
  2295. newTransfer = TRUE;
  2296. }
  2297. goto USBH_ChangeIndicationWorker_Exit;
  2298. }
  2299. //
  2300. // no error
  2301. // process the status change
  2302. //
  2303. USBH_KdPrint((2,"'Process State = %x\n", state));
  2304. if (portNumber != 0) {
  2305. USBH_ProcessPortStateChange((PPORT_STATE)&state, (USHORT)portNumber, DeviceExtensionHub);
  2306. } else {
  2307. USBH_ProcessHubStateChange((PHUB_STATE)&state, DeviceExtensionHub);
  2308. }
  2309. newTransfer = TRUE;
  2310. USBH_ChangeIndicationWorker_Exit:
  2311. UsbhExFreePool(workItem);
  2312. //
  2313. // The stopping thread can be signaled even though there is a transfer irp
  2314. // pending. because of this the stopping thread also waits on the
  2315. // AbortEvent for the pending transfer.
  2316. //
  2317. if (newTransfer) {
  2318. //
  2319. // Put our listen transfer back down now that
  2320. // we have acknowledged the change.
  2321. //
  2322. // NOTE: This could result in another work item being queued,
  2323. // but only if the device stopping flag clear.
  2324. USBH_SubmitInterruptTransfer(DeviceExtensionHub);
  2325. }
  2326. //
  2327. // allow others to access the ports
  2328. //
  2329. USBH_KdPrint((2,"'***RELEASE hub mutex %x\n", DeviceExtensionHub));
  2330. KeReleaseSemaphore(&DeviceExtensionHub->HubMutex,
  2331. LOW_REALTIME_PRIORITY,
  2332. 1,
  2333. FALSE);
  2334. //
  2335. // the pending count can only go to zero if the stopping flag has been set
  2336. //
  2337. USBH_DEC_PENDING_IO_COUNT(DeviceExtensionHub);
  2338. pendingWorkitem = InterlockedDecrement(
  2339. &DeviceExtensionHub->ChangeIndicationWorkitemPending);
  2340. // If USBH_FdoPower or USBH_FdoCleanup is waiting on this
  2341. // ChangeIndicationWorker, then signal the thread that it
  2342. // may now continue.
  2343. if (!pendingWorkitem) {
  2344. KeSetEvent(&DeviceExtensionHub->CWKEvent, 1, FALSE);
  2345. }
  2346. if (!pendingWorkitem &&
  2347. DeviceExtensionHub->HubFlags & HUBFLAG_NEED_IDLE_CHECK) {
  2348. USBH_CheckHubIdle(DeviceExtensionHub);
  2349. }
  2350. LOGENTRY(LOG_PNP, "cWK-", DeviceExtensionHub, Context, 0);
  2351. USBH_KdPrint((2,"'Exit ChangeIndicationWorker\n"));
  2352. }
  2353. VOID
  2354. USBH_ProcessHubStateChange(
  2355. IN PHUB_STATE CurrentHubState,
  2356. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub)
  2357. /* ++
  2358. *
  2359. * Description:
  2360. *
  2361. * Process a hub change indictaion from a hub
  2362. *
  2363. * Arguments:
  2364. *
  2365. * Return:
  2366. *
  2367. * -- */
  2368. {
  2369. //
  2370. // process hub status
  2371. //
  2372. PHUB_STATE hubExtensionState;
  2373. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  2374. ULONG statusBit;
  2375. PPORT_DATA p;
  2376. ULONG numberOfPorts, i;
  2377. PAGED_CODE();
  2378. if (CurrentHubState->HubChange & HUB_STATUS_LOCAL_POWER) {
  2379. TEST_TRAP();
  2380. USBH_KdPrint((2,"'StatusIndication hub local power changed\n"));
  2381. statusBit = CurrentHubState->HubStatus & HUB_STATUS_LOCAL_POWER;
  2382. hubExtensionState = &DeviceExtensionHub->HubState;
  2383. USBH_KdPrint((2,"'Hub local power bit was %d is %d\n", \
  2384. hubExtensionState->HubStatus & HUB_STATUS_LOCAL_POWER, statusBit));
  2385. //
  2386. // update our record
  2387. //
  2388. hubExtensionState->HubStatus &= ~HUB_STATUS_LOCAL_POWER;
  2389. hubExtensionState->HubStatus |= statusBit;
  2390. //
  2391. // ack the change
  2392. //
  2393. USBH_SyncClearHubStatus(DeviceExtensionHub,
  2394. FEATURE_C_HUB_LOCAL_POWER);
  2395. //
  2396. // jd
  2397. // What further action should be taken here?
  2398. //
  2399. } else if (CurrentHubState->HubChange & HUB_STATUS_OVER_CURRENT) {
  2400. USBH_KdPrint(( 1, "Hub is reporting overcurrent\n"));
  2401. #ifdef MAX_DEBUG
  2402. TEST_TRAP();
  2403. #endif
  2404. statusBit = CurrentHubState->HubStatus & HUB_STATUS_OVER_CURRENT;
  2405. hubExtensionState = &DeviceExtensionHub->HubState;
  2406. USBH_KdPrint((2,"'Hub over current bit was %d is %d\n",
  2407. hubExtensionState->HubStatus & HUB_STATUS_OVER_CURRENT, statusBit));
  2408. //
  2409. // update our record
  2410. //
  2411. hubExtensionState->HubStatus &= ~HUB_STATUS_OVER_CURRENT;
  2412. hubExtensionState->HubStatus |= statusBit;
  2413. //
  2414. // set the ack status change
  2415. //
  2416. USBH_SyncClearHubStatus(DeviceExtensionHub,
  2417. FEATURE_C_HUB_OVER_CURRENT);
  2418. //
  2419. // We have a global overcurrent condition for the hub itself
  2420. // chances are the entire hub has lost it -- we will mark the
  2421. // hub as failed
  2422. //
  2423. if (hubExtensionState->HubStatus & HUB_STATUS_OVER_CURRENT) {
  2424. USBH_KdPrint(( 1, "Hub disabled by overcurrent --> this is bad\n"));
  2425. USBH_WriteFailReason(
  2426. DeviceExtensionHub->PhysicalDeviceObject,
  2427. USBH_FAILREASON_HUB_OVERCURRENT);
  2428. HUB_FAILURE(DeviceExtensionHub);
  2429. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  2430. USBH_ASSERT(DeviceExtensionHub->PortData != NULL);
  2431. p = DeviceExtensionHub->PortData;
  2432. for (i=0; i<numberOfPorts; i++, p++) {
  2433. if (p->DeviceObject) {
  2434. USBH_KdPrint((1,"'Marking PDO %x for removal\n", p->DeviceObject));
  2435. deviceExtensionPort = p->DeviceObject->DeviceExtension;
  2436. deviceExtensionPort->PortPdoFlags |= PORTPDO_DELETE_PENDING;
  2437. }
  2438. //
  2439. // Note that we hold onto the hub's reference to the device object here.
  2440. // We need to do this for the case where a hub fails, and the downstream device
  2441. // had open references to it (open files on USB storage device). In this case
  2442. // PnP won't send the remove to the device until the files have been closed, and
  2443. // we need the reference to the device object in the hub device extension so
  2444. // that we can properly cleanup after the device in USBH_FdoCleanup when the
  2445. // hub is removed. If we do not do this then we will fault in
  2446. // USBH_PdoRemoveDevice when trying to dereference the pointer to the hub
  2447. // device extension that this device in connected to because the hub is long
  2448. // gone by then.
  2449. //
  2450. // p->DeviceObject = NULL;
  2451. p->ConnectionStatus = NoDeviceConnected;
  2452. }
  2453. // Tell PnP that there are no devices on this hub.
  2454. // FdoQueryBusRelations will return zero devices for this hub
  2455. // if the HUBFLAG_HUB_HAS_LOST_BRAINS is set.
  2456. USBH_IoInvalidateDeviceRelations(DeviceExtensionHub->PhysicalDeviceObject,
  2457. BusRelations);
  2458. // Try to recover the hub.
  2459. USBH_ScheduleESDRecovery(DeviceExtensionHub);
  2460. }
  2461. } else {
  2462. USBH_KdBreak(("Unrecognized hub change code %x\n", CurrentHubState->HubChange));
  2463. }
  2464. }
  2465. NTSTATUS
  2466. USBH_FlushPortChange(
  2467. IN OUT PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  2468. IN OUT PDEVICE_EXTENSION_PORT DeviceExtensionPort
  2469. )
  2470. /* ++
  2471. *
  2472. * Description:
  2473. *
  2474. * Arguments:
  2475. *
  2476. * Return:
  2477. *
  2478. * -- */
  2479. {
  2480. NTSTATUS ntStatus = STATUS_SUCCESS;
  2481. // PPORT_DATA portData;
  2482. PORT_STATE portState;
  2483. ASSERT_HUB(DeviceExtensionHub);
  2484. // portData = &deviceExtensionHub->PortData[
  2485. // DeviceExtensionPort->PortNumber - 1];
  2486. LOGENTRY(LOG_PNP, "Pfls", DeviceExtensionPort,
  2487. DeviceExtensionHub,
  2488. DeviceExtensionPort->PortNumber);
  2489. USBH_KdPrint((1,"'USBH_FlushPortChange, port number %x\n",
  2490. DeviceExtensionPort->PortNumber));
  2491. //
  2492. // we need to refresh the port data since it was lost on the stop
  2493. //
  2494. ntStatus = USBH_SyncGetPortStatus(DeviceExtensionHub,
  2495. DeviceExtensionPort->PortNumber,
  2496. (PUCHAR) &portState,
  2497. sizeof(portState));
  2498. LOGENTRY(LOG_PNP, "PfST", DeviceExtensionPort,
  2499. portState.PortChange,
  2500. portState.PortStatus);
  2501. if (NT_SUCCESS(ntStatus) &&
  2502. portState.PortChange & PORT_STATUS_CONNECT) {
  2503. LOGENTRY(LOG_PNP, "PfCL", DeviceExtensionPort,
  2504. DeviceExtensionPort->PortNumber,
  2505. ntStatus);
  2506. ntStatus = USBH_SyncClearPortStatus(DeviceExtensionHub,
  2507. DeviceExtensionPort->PortNumber,
  2508. FEATURE_C_PORT_CONNECT);
  2509. }
  2510. return ntStatus;
  2511. }
  2512. VOID
  2513. USBH_ProcessPortStateChange(
  2514. IN PPORT_STATE CurrentPortState,
  2515. IN USHORT PortNumber,
  2516. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub)
  2517. /* ++
  2518. *
  2519. * Description:
  2520. *
  2521. * Process a port change indication from the hub
  2522. *
  2523. * this code assumes that only one change bit is set at a time
  2524. *
  2525. * Arguments:
  2526. *
  2527. * Return:
  2528. *
  2529. * -- */
  2530. {
  2531. //
  2532. // this code assumes that only one change bit is set at a time
  2533. //
  2534. PPORT_STATE hubExtensionPortState;
  2535. USHORT statusBit;
  2536. PPORT_DATA portData;
  2537. BOOLEAN validConnectChange = TRUE;
  2538. PDEVICE_EXTENSION_PORT deviceExtensionPort = NULL;
  2539. PIRP irp;
  2540. PIRP hubWaitWake = NULL;
  2541. KIRQL irql;
  2542. LONG pendingPortWWs;
  2543. PWCHAR sernumbuf;
  2544. PKEVENT suspendEvent;
  2545. #ifdef EARLY_RESOURCE_RELEASE
  2546. PVOID deviceData;
  2547. #endif
  2548. // Can't acquire (cancel) spin locks in paged code!
  2549. // TODO: isolate the pieces of code that require the spin lock into helper
  2550. // functions
  2551. // PAGED_CODE();
  2552. USBH_ASSERT(DeviceExtensionHub->PortData != NULL);
  2553. hubExtensionPortState = &(DeviceExtensionHub->PortData + PortNumber - 1)->PortState;
  2554. USBH_KdPrint((2,"'USBH_ProcessPortStateChange for Port %x Old Dword %x\n", PortNumber, *(ULONG *) hubExtensionPortState));
  2555. LOGENTRY(LOG_PNP, "PSCn", DeviceExtensionHub,
  2556. CurrentPortState->PortStatus, CurrentPortState->PortChange);
  2557. if (CurrentPortState->PortChange & PORT_STATUS_CONNECT) {
  2558. //
  2559. // bit 0, connect status change
  2560. //
  2561. USHORT oldStatusBit;
  2562. USBH_KdPrint((2,"'Status Indication port connect changed\n"));
  2563. statusBit = CurrentPortState->PortStatus & PORT_STATUS_CONNECT;
  2564. oldStatusBit = hubExtensionPortState->PortStatus & PORT_STATUS_CONNECT;
  2565. USBH_KdPrint((2,"'Port connect was %x is %x\n", oldStatusBit, statusBit));
  2566. *hubExtensionPortState = *CurrentPortState;
  2567. //
  2568. // ack the change
  2569. //
  2570. USBH_SyncClearPortStatus(DeviceExtensionHub,
  2571. PortNumber,
  2572. FEATURE_C_PORT_CONNECT);
  2573. //
  2574. // If the conn status stays the same, clear the change. Otherwise,
  2575. // tell to reenumerate.
  2576. // A disconn->conn->disconn sequence is neglegible.
  2577. // A conn->discconn->conn is considered only possible with the same
  2578. // device.
  2579. //
  2580. USBH_ASSERT(PortNumber > 0);
  2581. portData = &DeviceExtensionHub->PortData[PortNumber-1];
  2582. if (!(oldStatusBit ^ statusBit)) {
  2583. // we should only see this in the case where
  2584. // the hub has lost power
  2585. USBH_KdPrint((1,"'status change but nothing has changed\n"));
  2586. LOGENTRY(LOG_PNP, "Pchg", DeviceExtensionHub,
  2587. PortNumber, validConnectChange);
  2588. }
  2589. if (portData->DeviceObject) {
  2590. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  2591. if (deviceExtensionPort->PortPdoFlags & PORTPDO_OVERCURRENT) {
  2592. USBH_KdPrint((1,"'port overcurrent detected\n"));
  2593. validConnectChange = FALSE;
  2594. LOGENTRY(LOG_PNP, "Povr", DeviceExtensionHub,
  2595. PortNumber, validConnectChange);
  2596. }
  2597. }
  2598. if (validConnectChange) {
  2599. // we have a valid port connect status change
  2600. LOGENTRY(LOG_PNP, "CONc", DeviceExtensionHub, PortNumber, 0);
  2601. //
  2602. // Notify PnP to enumerate this PDO for the device
  2603. // that has arrived or left
  2604. //
  2605. // if a pdo exists for this port we must delete it since the
  2606. // device may arrive agian before we get to QueryBusRelations
  2607. if (portData->DeviceObject) {
  2608. //
  2609. // see if the PDO has not been started if so we can ignore
  2610. // connect change on this port since the device will have to be
  2611. // reset.
  2612. //
  2613. deviceExtensionPort =
  2614. portData->DeviceObject->DeviceExtension;
  2615. if (!(deviceExtensionPort->PortPdoFlags & PORTPDO_NEED_RESET)) {
  2616. PDEVICE_OBJECT pdo;
  2617. pdo = portData->DeviceObject;
  2618. portData->DeviceObject = NULL;
  2619. portData->ConnectionStatus = NoDeviceConnected;
  2620. LOGENTRY(LOG_PNP, "pd2", pdo, 0, 0);
  2621. // legacy flags
  2622. DeviceExtensionHub->HubFlags |= HUBFLAG_CHILD_DELETES_PENDING;
  2623. if (pdo) {
  2624. // place the removed PDO on our list
  2625. InsertTailList(&DeviceExtensionHub->DeletePdoList,
  2626. &PDO_EXT(pdo)->DeletePdoLink);
  2627. }
  2628. // Prevent double free of SerialNumberBuffer in
  2629. // USBH_FdoQueryBusRelations.
  2630. sernumbuf = InterlockedExchangePointer(
  2631. &deviceExtensionPort->SerialNumberBuffer,
  2632. NULL);
  2633. if (sernumbuf) {
  2634. UsbhExFreePool(sernumbuf);
  2635. }
  2636. #ifdef EARLY_RESOURCE_RELEASE
  2637. //
  2638. // Remove the device data now to free
  2639. // up the bus resources.
  2640. //
  2641. deviceData = InterlockedExchangePointer(
  2642. &deviceExtensionPort->DeviceData,
  2643. NULL);
  2644. if (deviceData) {
  2645. #ifdef USB2
  2646. USBD_RemoveDeviceEx(DeviceExtensionHub,
  2647. deviceData,
  2648. DeviceExtensionHub->RootHubPdo,
  2649. 0);
  2650. #else
  2651. USBD_RemoveDevice(deviceData,
  2652. DeviceExtensionHub->RootHubPdo,
  2653. 0);
  2654. #endif
  2655. USBH_SyncDisablePort(DeviceExtensionHub,
  2656. PortNumber);
  2657. }
  2658. #endif // EARLY_RESOURCE_RELEASE
  2659. }
  2660. }
  2661. USBH_KdPrint((2,"'Notify BusCheck by FDO extension %x\n", DeviceExtensionHub));
  2662. USBH_IoInvalidateDeviceRelations(DeviceExtensionHub->PhysicalDeviceObject,
  2663. BusRelations);
  2664. USBH_KdPrint((2,"'StatusIndication Conn Changed port %x\n", PortNumber));
  2665. }
  2666. } else if (CurrentPortState->PortChange & PORT_STATUS_RESET) {
  2667. //
  2668. // bit 4 RESET completed
  2669. //
  2670. //
  2671. // we simply ack the change and signal the PnP thread that is
  2672. // waiting.
  2673. //
  2674. USBH_KdPrint((2,"'Status Indication port reset changed\n"));
  2675. statusBit = CurrentPortState->PortStatus & PORT_STATUS_RESET;
  2676. USBH_KdPrint((2,"'Port reset was %x is %x\n",
  2677. hubExtensionPortState->PortStatus & PORT_STATUS_RESET, statusBit));
  2678. // port status will not be enabled if the device failed
  2679. #if DBG
  2680. if (!(CurrentPortState->PortStatus & PORT_STATUS_ENABLE)) {
  2681. USBH_KdPrint((1, "'Device failed after reset\n"));
  2682. }
  2683. #endif
  2684. *hubExtensionPortState = *CurrentPortState;
  2685. //
  2686. // ack the change
  2687. //
  2688. USBH_SyncClearPortStatus(DeviceExtensionHub,
  2689. PortNumber,
  2690. FEATURE_C_PORT_RESET);
  2691. //
  2692. // Signal the PNP thread that a the reset has completed
  2693. //
  2694. LOGENTRY(LOG_PNP, "RESp", DeviceExtensionHub, PortNumber, 0);
  2695. } else if (CurrentPortState->PortChange & PORT_STATUS_ENABLE) {
  2696. //
  2697. // ways to hit this code:
  2698. // 1. frame babble causes the port to be disabled
  2699. // 2. overcurrent causes a port disable
  2700. // bit 1 port has been enabled
  2701. USBH_KdPrint((2,"'Status Indication port enable changed\n"));
  2702. statusBit = CurrentPortState->PortStatus & PORT_STATUS_ENABLE;
  2703. USBH_KdPrint((2,"'Port enable was %x is %x\n",
  2704. hubExtensionPortState->PortStatus & PORT_STATUS_ENABLE,
  2705. statusBit));
  2706. //
  2707. // update our record
  2708. //
  2709. *hubExtensionPortState = *CurrentPortState;
  2710. //
  2711. // ack the change
  2712. //
  2713. USBH_SyncClearPortStatus(DeviceExtensionHub,
  2714. PortNumber,
  2715. FEATURE_C_PORT_ENABLE);
  2716. LOGENTRY(LOG_PNP, "ENAc", DeviceExtensionHub, PortNumber, 0);
  2717. } else if (CurrentPortState->PortChange & PORT_STATUS_SUSPEND) {
  2718. //
  2719. // bit 2 suspend changed
  2720. //
  2721. USBH_KdPrint((2,"'Status Indication port suspend changed\n"));
  2722. statusBit = CurrentPortState->PortStatus & PORT_STATUS_SUSPEND;
  2723. USBH_KdPrint((2,"'Port suspend was %x is %x\n",
  2724. hubExtensionPortState->PortStatus & PORT_STATUS_SUSPEND,
  2725. statusBit));
  2726. //
  2727. // update our record
  2728. //
  2729. *hubExtensionPortState = *CurrentPortState;
  2730. //
  2731. // ack the change
  2732. //
  2733. USBH_SyncClearPortStatus(DeviceExtensionHub,
  2734. PortNumber,
  2735. FEATURE_C_PORT_SUSPEND);
  2736. LOGENTRY(LOG_PNP, "SUSc", DeviceExtensionHub, PortNumber, 0);
  2737. suspendEvent = InterlockedExchangePointer(&DeviceExtensionHub->Event, NULL);
  2738. if (suspendEvent) {
  2739. LOGENTRY(LOG_PNP, "WAKs", DeviceExtensionHub, PortNumber, 0);
  2740. KeSetEvent(suspendEvent,
  2741. 1,
  2742. FALSE);
  2743. }
  2744. // Complete the WW IRP, if any, for this port.
  2745. //
  2746. // Note that we only want to do this for the selective suspend case
  2747. // and not the general resume case. (We don't want to be completing
  2748. // port WW IRPs while the system is suspending just because someone
  2749. // moved the mouse.)
  2750. //
  2751. // Note also that in our current Selective Suspend implementation,
  2752. // this code is not really even necessary because we only suspend
  2753. // when the entire bus can suspend, which includes the root hub,
  2754. // and if the root hub is suspended then the bus is reawoken by
  2755. // USBPORT completing the WW IRP for the root hub. This code here
  2756. // is only used if a child device indicates resume signalling while
  2757. // the parent hub is powered and fully operational.
  2758. USBH_ASSERT(PortNumber > 0);
  2759. portData = &DeviceExtensionHub->PortData[PortNumber-1];
  2760. if (portData->DeviceObject) {
  2761. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  2762. }
  2763. if (deviceExtensionPort && deviceExtensionPort->IdleNotificationIrp) {
  2764. IoAcquireCancelSpinLock(&irql);
  2765. irp = deviceExtensionPort->WaitWakeIrp;
  2766. deviceExtensionPort->WaitWakeIrp = NULL;
  2767. // signal the waitwake irp if we have one
  2768. if (irp) {
  2769. USBH_KdPrint((1,"'Signaling WaitWake IRP (%x) (resume signalling)\n", irp));
  2770. LOGENTRY(LOG_PNP, "rsWW", deviceExtensionPort,
  2771. deviceExtensionPort->DeviceState, DeviceExtensionHub->HubFlags);
  2772. IoSetCancelRoutine(irp, NULL);
  2773. deviceExtensionPort->PortPdoFlags &=
  2774. ~PORTPDO_REMOTE_WAKEUP_ENABLED;
  2775. pendingPortWWs =
  2776. InterlockedDecrement(&DeviceExtensionHub->NumberPortWakeIrps);
  2777. if (0 == pendingPortWWs && DeviceExtensionHub->PendingWakeIrp) {
  2778. hubWaitWake = DeviceExtensionHub->PendingWakeIrp;
  2779. DeviceExtensionHub->PendingWakeIrp = NULL;
  2780. }
  2781. IoReleaseCancelSpinLock(irql);
  2782. //
  2783. // If there are no more outstanding WW irps, we need to cancel the WW
  2784. // to the hub.
  2785. //
  2786. if (hubWaitWake) {
  2787. USBH_HubCancelWakeIrp(DeviceExtensionHub, hubWaitWake);
  2788. }
  2789. USBH_CompletePowerIrp(DeviceExtensionHub, irp, STATUS_SUCCESS);
  2790. } else {
  2791. IoReleaseCancelSpinLock(irql);
  2792. }
  2793. }
  2794. } else if (CurrentPortState->PortChange & PORT_STATUS_OVER_CURRENT) {
  2795. //
  2796. // bit 3
  2797. //
  2798. USBH_KdPrint((2,"'Status Indication port over current changed\n"));
  2799. statusBit = CurrentPortState->PortStatus & PORT_STATUS_OVER_CURRENT;
  2800. USBH_KdPrint((2,"'Port over current was %x is %x\n",
  2801. hubExtensionPortState->PortStatus & PORT_STATUS_OVER_CURRENT,
  2802. statusBit));
  2803. //
  2804. // update our record
  2805. //
  2806. *hubExtensionPortState = *CurrentPortState;
  2807. //
  2808. // ack the change
  2809. //
  2810. USBH_SyncClearPortStatus(DeviceExtensionHub,
  2811. PortNumber,
  2812. FEATURE_C_PORT_OVER_CURRENT);
  2813. LOGENTRY(LOG_PNP, "OVRc", DeviceExtensionHub, PortNumber, 0);
  2814. // The hub has reported overcurrent contion on the port, we will note
  2815. // this for the PDO. note that if a true overcurrent condition has occurred
  2816. // the port should be disabled and powered off as well.
  2817. // for some reason the NEC controller will report an overcurrent
  2818. // condition if the MS USB mouse is plugged in during boot
  2819. //
  2820. if (!(hubExtensionPortState->PortStatus & PORT_STATUS_POWER)) {
  2821. USBH_ASSERT(PortNumber > 0);
  2822. portData = &DeviceExtensionHub->PortData[PortNumber-1];
  2823. USBH_KdPrint((1,"'warning: overcurrent detected for port %d\n",
  2824. PortNumber));
  2825. USBH_SyncRefreshPortAttributes(DeviceExtensionHub);
  2826. // ignore overcurrent on CC ports
  2827. if (!(portData->PortAttributes &
  2828. USB_PORTATTR_NO_OVERCURRENT_UI)) {
  2829. if (portData->DeviceObject != NULL) {
  2830. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  2831. //xxx ?? IoInvalidateDeviceRelations?
  2832. // ignore overcurrent on CC ports
  2833. deviceExtensionPort->PortPdoFlags |= PORTPDO_OVERCURRENT;
  2834. UsbhWarning(NULL,
  2835. "port disabled/off due to overcurrent\n",
  2836. FALSE);
  2837. USBH_InvalidatePortDeviceState(
  2838. DeviceExtensionHub,
  2839. UsbhGetConnectionStatus(deviceExtensionPort),
  2840. deviceExtensionPort->PortNumber);
  2841. } else {
  2842. // NOTE: for some reason the NEC controller on Toshiba laptops
  2843. // does this.
  2844. USBH_KdPrint((1,"'warning: port has no device attached! %d\n",
  2845. PortNumber));
  2846. USBH_InvalidatePortDeviceState(
  2847. DeviceExtensionHub,
  2848. DeviceCausedOvercurrent,
  2849. PortNumber);
  2850. }
  2851. }
  2852. }
  2853. } else {
  2854. LOGENTRY(LOG_PNP, "???c", DeviceExtensionHub, PortNumber, 0);
  2855. USBH_KdBreak(("Unknown chnage bit, ignore\n"));
  2856. }
  2857. USBH_KdPrint((2,"'Exit ProcessPortState\n"));
  2858. }
  2859. NTSTATUS
  2860. USBH_GetNameFromPdo(
  2861. IN PDEVICE_OBJECT PdoDeviceObject,
  2862. IN OUT PUNICODE_STRING DeviceNameUnicodeString
  2863. )
  2864. /*++
  2865. Routine Description:
  2866. Returns the device name for the give instance of the HCD
  2867. Arguments:
  2868. DeviceObject -
  2869. DeviceNameUnicodeString - ptr to unicode string to initialize
  2870. with device name.
  2871. Return Value:
  2872. NT status code
  2873. --*/
  2874. {
  2875. ULONG actualSize;
  2876. NTSTATUS ntStatus;
  2877. PAGED_CODE();
  2878. ntStatus=IoGetDeviceProperty(PdoDeviceObject,
  2879. DevicePropertyPhysicalDeviceObjectName,
  2880. 0,
  2881. NULL,
  2882. &actualSize);
  2883. if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
  2884. DeviceNameUnicodeString->Length=(USHORT)actualSize-sizeof(UNICODE_NULL);
  2885. DeviceNameUnicodeString->MaximumLength=(USHORT)actualSize;
  2886. //
  2887. // Must use ExAllocatePool directly here because we call
  2888. // RtlFreeUnicode string to free the buffer
  2889. //
  2890. DeviceNameUnicodeString->Buffer =
  2891. ExAllocatePoolWithTag(PagedPool, actualSize, USBHUB_HEAP_TAG);
  2892. if (!DeviceNameUnicodeString->Buffer) {
  2893. ntStatus=STATUS_INSUFFICIENT_RESOURCES;
  2894. } else {
  2895. ntStatus=IoGetDeviceProperty(PdoDeviceObject,
  2896. DevicePropertyPhysicalDeviceObjectName,
  2897. actualSize,
  2898. DeviceNameUnicodeString->Buffer,
  2899. &actualSize);
  2900. if (!NT_SUCCESS(ntStatus)) {
  2901. ExFreePool(DeviceNameUnicodeString->Buffer);
  2902. } else {
  2903. // now strip off the "\Device\"
  2904. RtlCopyMemory(DeviceNameUnicodeString->Buffer,
  2905. DeviceNameUnicodeString->Buffer+8,
  2906. actualSize-8*sizeof(WCHAR));
  2907. DeviceNameUnicodeString->Length -= 16;
  2908. }
  2909. }
  2910. } else {
  2911. ntStatus=STATUS_INSUFFICIENT_RESOURCES;
  2912. }
  2913. USBH_KdPrint((2,"'USBH_GetNameFromPdo = %x\n", ntStatus));
  2914. return(ntStatus);
  2915. }
  2916. #if 0
  2917. NTSTATUS
  2918. USBH_MakeName(
  2919. PDEVICE_OBJECT PdoDeviceObject,
  2920. ULONG NameLength,
  2921. PWCHAR Name,
  2922. PUNICODE_STRING UnicodeString
  2923. )
  2924. /*++
  2925. Routine Description:
  2926. Creates a hub name unicode string from uncode 'Name' string passed in
  2927. and the unique name associated with the Pdo.
  2928. Arguments:
  2929. PdoDeviceObject - a PDO
  2930. NameLength - length (in bytes) of 'Name' unicode string,
  2931. including NULL.
  2932. Name - NULL terminated unicode string suffix
  2933. Return Value:
  2934. None
  2935. --*/
  2936. {
  2937. UNICODE_STRING keyUnicodeString;
  2938. NTSTATUS ntStatus;
  2939. PWCHAR buffer;
  2940. USHORT length;
  2941. PAGED_CODE();
  2942. //
  2943. // get the name from the Pdo
  2944. //
  2945. ntStatus = USBH_GetNameFromPdo(PdoDeviceObject,
  2946. &keyUnicodeString);
  2947. USBH_ASSERT(NameLength > 0);
  2948. if (NT_SUCCESS(ntStatus)) {
  2949. // ok we have the unique name, now we
  2950. // need to allocate a buffer big enough
  2951. // for it plus the 'Name' string
  2952. // keyname + prefix + NULL (Namelength includes NULL)
  2953. length = keyUnicodeString.Length +
  2954. (USHORT) NameLength;
  2955. //
  2956. // Must use normal api so that caller can use RtlFreeUnicodeString
  2957. //
  2958. buffer = ExAllocatePool(PagedPool, length, USBHUB_HEAP_TAG);
  2959. if (buffer) {
  2960. RtlCopyMemory(buffer, Name, NameLength);
  2961. RtlInitUnicodeString(UnicodeString,
  2962. buffer);
  2963. UnicodeString->MaximumLength = length;
  2964. USBH_ASSERT(*(buffer+((NameLength/2)-1)) == NULL);
  2965. ntStatus = RtlAppendUnicodeStringToString(UnicodeString,
  2966. &keyUnicodeString);
  2967. USBH_KdPrint((2,"'USBH_MakeName = key string = %x %x\n", &keyUnicodeString,
  2968. UnicodeString));
  2969. } else {
  2970. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  2971. TEST_TRAP();
  2972. }
  2973. RtlFreeUnicodeString(&keyUnicodeString);
  2974. }
  2975. return ntStatus;
  2976. }
  2977. NTSTATUS
  2978. USBH_GenerateDeviceName(
  2979. PDEVICE_OBJECT PdoDeviceObject,
  2980. PUNICODE_STRING DeviceNameUnicodeString,
  2981. PUNICODE_STRING DeviceLinkUnicodeString,
  2982. PUNICODE_STRING NameUnicodeString
  2983. )
  2984. /*++
  2985. Routine Description:
  2986. Generates device name strings for use with IoCreateDevice and
  2987. IoCreateSymbolicLink.
  2988. Arguments:
  2989. PdoDeviceObject - a PDO
  2990. DeviceNameUnicodeString
  2991. DeviceLinkUnicodeString -
  2992. NameUnicodeString
  2993. Return Value:
  2994. NT Status code.
  2995. --*/
  2996. {
  2997. WCHAR deviceLink[] = L"\\DosDevices\\";
  2998. WCHAR deviceName[] = L"\\Device\\";
  2999. WCHAR name[] = L"";
  3000. NTSTATUS ntStatus = STATUS_SUCCESS;
  3001. BOOLEAN gotDevname = FALSE, gotName = FALSE, gotLinkname = FALSE;
  3002. PAGED_CODE();
  3003. if (DeviceNameUnicodeString) {
  3004. ntStatus = USBH_MakeName(PdoDeviceObject,
  3005. sizeof(deviceName),
  3006. deviceName,
  3007. DeviceNameUnicodeString);
  3008. if (NT_SUCCESS(ntStatus)) {
  3009. gotDevname = TRUE;
  3010. }
  3011. }
  3012. if (DeviceLinkUnicodeString && NT_SUCCESS(ntStatus)) {
  3013. ntStatus = USBH_MakeName(PdoDeviceObject,
  3014. sizeof(deviceLink),
  3015. deviceLink,
  3016. DeviceLinkUnicodeString);
  3017. if (NT_SUCCESS(ntStatus)) {
  3018. gotLinkname = TRUE;
  3019. }
  3020. }
  3021. if (NameUnicodeString && NT_SUCCESS(ntStatus)) {
  3022. ntStatus = USBH_MakeName(PdoDeviceObject,
  3023. sizeof(name),
  3024. name,
  3025. NameUnicodeString);
  3026. if (NT_SUCCESS(ntStatus)) {
  3027. gotName = TRUE;
  3028. }
  3029. }
  3030. if (!NT_SUCCESS(ntStatus)) {
  3031. //
  3032. // cleanup all the strings if we fail
  3033. //
  3034. // an error here is most likely a bug
  3035. USBH_KdTrap(("failed to generate Hub device name\n"));
  3036. if (gotDevname) {
  3037. RtlFreeUnicodeString(DeviceNameUnicodeString);
  3038. }
  3039. if (gotLinkname) {
  3040. RtlFreeUnicodeString(DeviceLinkUnicodeString);
  3041. }
  3042. if (gotName) {
  3043. RtlFreeUnicodeString(NameUnicodeString);
  3044. }
  3045. }
  3046. USBH_KdPrint((2,"'USBH_GenerateDeviceName = %x\n", ntStatus));
  3047. return ntStatus;
  3048. }
  3049. #endif
  3050. NTSTATUS
  3051. USBH_AddDevice(
  3052. IN PDRIVER_OBJECT DriverObject,
  3053. IN PDEVICE_OBJECT PhysicalDeviceObject)
  3054. /* ++ Description:
  3055. *
  3056. * Called whenever the hub driver is loaded to control a device.
  3057. * Possible reasons:
  3058. * 1. a hub was attached to the USB
  3059. * 2. we where loaded as the generic parent for a composite device
  3060. * 3. we were loaded as a configuring driver.
  3061. *
  3062. * Arguments:
  3063. *
  3064. * PhysicalDeviceObject - Parent device object PDO created to handle us.
  3065. * DriverObject - Store the pointer to the object representing us.
  3066. *
  3067. * Return:
  3068. *
  3069. * STATUS_SUCCESS - if successful STATUS_UNSUCCESSFUL - otherwise
  3070. *
  3071. * -- */
  3072. {
  3073. NTSTATUS ntStatus = STATUS_SUCCESS;
  3074. PDEVICE_OBJECT deviceObject = NULL, rootHubPdo = NULL, dummyPdo = NULL;
  3075. PDEVICE_OBJECT topOfStackDeviceObject = NULL;
  3076. PDEVICE_EXTENSION_HUB deviceExtensionHub; // pointer to our device
  3077. // extension
  3078. USBH_KdPrint((2,"'Enter AddDevice\n"));
  3079. LOGENTRY(LOG_PNP, "hADD", PhysicalDeviceObject, 0, 0);
  3080. #if DBG
  3081. USBH_GetClassGlobalDebugRegistryParameters();
  3082. #endif
  3083. //
  3084. // Create a new hub on the USB
  3085. //
  3086. //
  3087. USBH_KdBreak(("Add Device for hub\n"));
  3088. if (NT_SUCCESS(ntStatus)) {
  3089. USBH_ASSERT(sizeof(DEVICE_EXTENSION_HUB) >= sizeof(DEVICE_EXTENSION_PARENT));
  3090. ntStatus = IoCreateDevice(DriverObject, // our driver object
  3091. sizeof(DEVICE_EXTENSION_HUB), // extension size for us
  3092. NULL, // name for this device
  3093. FILE_DEVICE_USB_HUB, // HUB type
  3094. FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics
  3095. FALSE, // Not exclusive
  3096. &deviceObject); // Our device object
  3097. if (NT_SUCCESS(ntStatus)) {
  3098. deviceExtensionHub = (PDEVICE_EXTENSION_HUB) deviceObject->DeviceExtension;
  3099. deviceExtensionHub->ExtensionType = EXTENSION_TYPE_HUB;
  3100. }
  3101. }
  3102. if (NT_SUCCESS(ntStatus)) {
  3103. topOfStackDeviceObject =
  3104. IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
  3105. if (!topOfStackDeviceObject) {
  3106. ntStatus = STATUS_UNSUCCESSFUL;
  3107. }
  3108. }
  3109. if (NT_SUCCESS(ntStatus)) {
  3110. // Initialize the rest of the hub device extension
  3111. deviceExtensionHub->FunctionalDeviceObject = deviceObject;
  3112. deviceExtensionHub->PhysicalDeviceObject = PhysicalDeviceObject;
  3113. deviceExtensionHub->TopOfStackDeviceObject = topOfStackDeviceObject;
  3114. USBH_KdPrint((2,"'stack device object stack size = %x\n",
  3115. deviceExtensionHub->TopOfStackDeviceObject->StackSize));
  3116. deviceObject->Flags |= DO_POWER_PAGABLE;
  3117. deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
  3118. #ifdef WMI_SUPPORT
  3119. {
  3120. PWMILIB_CONTEXT wmiLibInfo;
  3121. extern WMIGUIDREGINFO USB_WmiGuidList[NUM_WMI_SUPPORTED_GUIDS];
  3122. wmiLibInfo = &deviceExtensionHub->WmiLibInfo;
  3123. wmiLibInfo->GuidCount = sizeof (USB_WmiGuidList) /
  3124. sizeof (WMIGUIDREGINFO);
  3125. ASSERT(NUM_WMI_SUPPORTED_GUIDS == wmiLibInfo->GuidCount);
  3126. // Omit the last GUID in the list if this is not a Root Hub.
  3127. USBH_SyncGetRootHubPdo(deviceExtensionHub->TopOfStackDeviceObject,
  3128. &rootHubPdo,
  3129. &dummyPdo,
  3130. NULL);
  3131. if (rootHubPdo != PhysicalDeviceObject) {
  3132. // Dump the last WMI GUID.
  3133. wmiLibInfo->GuidCount--;
  3134. }
  3135. wmiLibInfo->GuidList = USB_WmiGuidList;
  3136. wmiLibInfo->QueryWmiRegInfo = USBH_QueryWmiRegInfo;
  3137. wmiLibInfo->QueryWmiDataBlock = USBH_QueryWmiDataBlock;
  3138. wmiLibInfo->SetWmiDataBlock = USBH_SetWmiDataBlock;
  3139. wmiLibInfo->SetWmiDataItem = NULL;
  3140. wmiLibInfo->ExecuteWmiMethod = USBH_ExecuteWmiMethod;
  3141. wmiLibInfo->WmiFunctionControl = NULL;
  3142. IoWMIRegistrationControl(deviceObject,
  3143. WMIREG_ACTION_REGISTER
  3144. );
  3145. }
  3146. #endif
  3147. } else {
  3148. // failed to create device object or symbolic link
  3149. TEST_TRAP();
  3150. if (deviceObject) {
  3151. IoDeleteDevice(deviceObject);
  3152. }
  3153. }
  3154. USBH_KdPrint((2,"'AddDevice return %x\n", ntStatus));
  3155. return ntStatus;
  3156. }
  3157. #if DBG
  3158. NTSTATUS
  3159. USBH_GetClassGlobalDebugRegistryParameters(
  3160. )
  3161. /*++
  3162. Routine Description:
  3163. Arguments:
  3164. Return Value:
  3165. --*/
  3166. {
  3167. NTSTATUS ntStatus;
  3168. RTL_QUERY_REGISTRY_TABLE QueryTable[3];
  3169. PWCHAR usb = L"usb";
  3170. #define DEBUG_LEVEL L"debuglevel"
  3171. #define DEBUG_WIN9X L"debugWin9x"
  3172. extern ULONG USBH_W98_Debug_Trace;
  3173. PAGED_CODE();
  3174. //
  3175. // Set up QueryTable to do the following:
  3176. //
  3177. // spew level
  3178. QueryTable[0].QueryRoutine = USBH_GetConfigValue;
  3179. QueryTable[0].Flags = 0;
  3180. QueryTable[0].Name = DEBUG_LEVEL;
  3181. QueryTable[0].EntryContext = &USBH_Debug_Trace_Level;
  3182. QueryTable[0].DefaultType = REG_DWORD;
  3183. QueryTable[0].DefaultData = &USBH_Debug_Trace_Level;
  3184. QueryTable[0].DefaultLength = sizeof(USBH_Debug_Trace_Level);
  3185. // ntkern trace buffer
  3186. QueryTable[1].QueryRoutine = USBH_GetConfigValue;
  3187. QueryTable[1].Flags = 0;
  3188. QueryTable[1].Name = DEBUG_WIN9X;
  3189. QueryTable[1].EntryContext = &USBH_W98_Debug_Trace;
  3190. QueryTable[1].DefaultType = REG_DWORD;
  3191. QueryTable[1].DefaultData = &USBH_W98_Debug_Trace;
  3192. QueryTable[1].DefaultLength = sizeof(USBH_W98_Debug_Trace);
  3193. //
  3194. // Stop
  3195. //
  3196. QueryTable[2].QueryRoutine = NULL;
  3197. QueryTable[2].Flags = 0;
  3198. QueryTable[2].Name = NULL;
  3199. ntStatus = RtlQueryRegistryValues(
  3200. RTL_REGISTRY_SERVICES,
  3201. usb,
  3202. QueryTable, // QueryTable
  3203. NULL, // Context
  3204. NULL); // Environment
  3205. if (NT_SUCCESS(ntStatus)) {
  3206. USBH_KdPrint((1, "'Debug Trace Level Set: (%d)\n", USBH_Debug_Trace_Level));
  3207. if (USBH_W98_Debug_Trace) {
  3208. USBH_KdPrint((1, "'NTKERN Trace is ON\n"));
  3209. } else {
  3210. USBH_KdPrint((1, "'NTKERN Trace is OFF\n"));
  3211. }
  3212. if (USBH_Debug_Trace_Level > 0) {
  3213. ULONG UHCD_Debug_Asserts = 1;
  3214. }
  3215. }
  3216. if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) {
  3217. ntStatus = STATUS_SUCCESS;
  3218. }
  3219. return ntStatus;
  3220. }
  3221. #endif
  3222. #if 0
  3223. NTSTATUS
  3224. USBH_FdoStartDevice(
  3225. IN OUT PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  3226. IN PIRP Irp
  3227. )
  3228. /* ++ Description:
  3229. *
  3230. * This routine is called by PnP via (IRP_MJ_PNP, IRP_MN_START_DEVICE). We will
  3231. * initialize the hub and ready all ports.
  3232. *
  3233. * Argument:
  3234. *
  3235. * Return:
  3236. *
  3237. * STATUS_SUCCESS - if successful STATUS_UNSUCCESSFUL - otherwise
  3238. *
  3239. * -- */
  3240. {
  3241. NTSTATUS ntStatus;
  3242. USBH_ASSERT(EXTENSION_TYPE_HUB == DeviceExtensionHub->ExtensionType);
  3243. LOGENTRY(LOG_PNP, "STRT", DeviceExtensionHub, 0, 0);
  3244. ntStatus = USBH_FdoHubStartDevice(DeviceExtensionHub,
  3245. Irp);
  3246. return ntStatus;
  3247. }
  3248. #endif
  3249. // keep gen parent for .NET and SP1
  3250. // since we no longer use the hub as parent we will always treat
  3251. // the device as a hub
  3252. NTSTATUS
  3253. USBH_FdoStartDevice(
  3254. IN OUT PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  3255. IN PIRP Irp
  3256. )
  3257. /* ++ Description:
  3258. *
  3259. * This routine is called by PnP via (IRP_MJ_PNP, IRP_MN_START_DEVICE). We will
  3260. * initialize the hub and ready all ports.
  3261. *
  3262. * Argument:
  3263. *
  3264. * Return:
  3265. *
  3266. * STATUS_SUCCESS - if successful STATUS_UNSUCCESSFUL - otherwise
  3267. *
  3268. * -- */
  3269. {
  3270. NTSTATUS ntStatus, status;
  3271. PDEVICE_EXTENSION_PARENT parent;
  3272. PAGED_CODE();
  3273. USBH_KdPrint((2,"'Enter StartDevice\n"));
  3274. USBH_ASSERT(EXTENSION_TYPE_HUB == DeviceExtensionHub->ExtensionType);
  3275. LOGENTRY(LOG_PNP, "STRT", DeviceExtensionHub, 0, 0);
  3276. //
  3277. // collect some inforantion from the device, like the root hub
  3278. // pdo for our fast-path
  3279. //
  3280. DeviceExtensionHub->RootHubPdo = NULL;
  3281. ntStatus =
  3282. USBH_SyncGetRootHubPdo(DeviceExtensionHub->TopOfStackDeviceObject,
  3283. &DeviceExtensionHub->RootHubPdo,
  3284. &DeviceExtensionHub->TopOfHcdStackDeviceObject,
  3285. NULL);
  3286. if (!NT_SUCCESS(ntStatus)) {
  3287. KeInitializeEvent(&DeviceExtensionHub->PnpStartEvent, NotificationEvent, FALSE);
  3288. USBH_KdPrint((2,"'Set PnPIrp Completion Routine\n"));
  3289. IoCopyCurrentIrpStackLocationToNext(Irp);
  3290. IoSetCompletionRoutine(Irp,
  3291. USBH_HubPnPIrp_Complete,
  3292. // always pass FDO to completion routine
  3293. DeviceExtensionHub,
  3294. TRUE,
  3295. TRUE,
  3296. TRUE);
  3297. status = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  3298. Irp);
  3299. if (status == STATUS_PENDING) {
  3300. KeWaitForSingleObject(&DeviceExtensionHub->PnpStartEvent,
  3301. Suspended,
  3302. KernelMode,
  3303. FALSE,
  3304. NULL);
  3305. }
  3306. //
  3307. // complete the start Irp now since we pended it with
  3308. // our completion handler.
  3309. //
  3310. LOGENTRY(LOG_PNP, "STR!", DeviceExtensionHub, 0, ntStatus);
  3311. USBH_CompleteIrp(Irp, ntStatus);
  3312. } else if (DeviceExtensionHub->RootHubPdo != NULL) {
  3313. // stack reports a root hub PDO then this
  3314. // is a hub
  3315. ntStatus = USBH_FdoHubStartDevice(DeviceExtensionHub,
  3316. Irp);
  3317. } else {
  3318. //
  3319. // if no root hub PDO then we are being loaded
  3320. // as a configuring parent driver.
  3321. //
  3322. DeviceExtensionHub->ExtensionType = EXTENSION_TYPE_PARENT;
  3323. //
  3324. // Initialize this parent
  3325. //
  3326. parent = (PDEVICE_EXTENSION_PARENT) DeviceExtensionHub;
  3327. parent->PowerIrp = NULL;
  3328. parent->PendingWakeIrp = NULL;
  3329. parent->NumberFunctionWakeIrps = 0;
  3330. parent->FunctionCount = 0;
  3331. parent->FunctionList.Next = NULL;
  3332. parent->ParentFlags = 0;
  3333. parent->NeedCleanup = FALSE;
  3334. parent->ConfigurationDescriptor = NULL;
  3335. KeInitializeSpinLock (&parent->ParentSpinLock);
  3336. //
  3337. // Start it.
  3338. //
  3339. ntStatus = USBH_ParentFdoStartDevice(parent, Irp, TRUE);
  3340. }
  3341. return ntStatus;
  3342. }
  3343. VOID
  3344. USBH_QueryCapabilities(
  3345. IN PDEVICE_OBJECT PdoDeviceObject,
  3346. IN PDEVICE_CAPABILITIES DeviceCapabilities
  3347. )
  3348. /*++
  3349. Routine Description:
  3350. This routine reads or write config space.
  3351. Arguments:
  3352. DeviceObject - Physical DeviceObject for this USB controller.
  3353. Return Value:
  3354. None.
  3355. --*/
  3356. {
  3357. PIO_STACK_LOCATION nextStack;
  3358. PIRP irp;
  3359. NTSTATUS ntStatus;
  3360. KEVENT event;
  3361. PAGED_CODE();
  3362. USBH_KdPrint((2,"'USBH_QueryCapabilities\n"));
  3363. irp = IoAllocateIrp(PdoDeviceObject->StackSize, FALSE);
  3364. if (!irp) {
  3365. USBH_KdTrap(("Allocate Irp failed\n"));
  3366. return;
  3367. }
  3368. // All PnP IRP's need the Status field initialized to STATUS_NOT_SUPPORTED.
  3369. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  3370. KeInitializeEvent(&event, NotificationEvent, FALSE);
  3371. IoSetCompletionRoutine(irp,
  3372. USBH_DeferIrpCompletion,
  3373. &event,
  3374. TRUE,
  3375. TRUE,
  3376. TRUE);
  3377. nextStack = IoGetNextIrpStackLocation(irp);
  3378. USBH_ASSERT(nextStack != NULL);
  3379. nextStack->MajorFunction= IRP_MJ_PNP;
  3380. nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;
  3381. //this is different from the latest version of busdd.doc
  3382. nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
  3383. // IrpAssert: Initialize these fields in the DeviceCapabilities structure
  3384. // before passing down.
  3385. RtlZeroMemory(nextStack->Parameters.DeviceCapabilities.Capabilities,
  3386. sizeof(DEVICE_CAPABILITIES));
  3387. nextStack->Parameters.DeviceCapabilities.Capabilities->Address = -1;
  3388. nextStack->Parameters.DeviceCapabilities.Capabilities->UINumber = -1;
  3389. nextStack->Parameters.DeviceCapabilities.Capabilities->Version = 1;
  3390. nextStack->Parameters.DeviceCapabilities.Capabilities->Size =
  3391. sizeof(DEVICE_CAPABILITIES);
  3392. ntStatus = IoCallDriver(PdoDeviceObject,
  3393. irp);
  3394. USBH_KdPrint((2,"'ntStatus from IoCallDriver to hub PDO = 0x%x\n", ntStatus));
  3395. if (ntStatus == STATUS_PENDING) {
  3396. // TEST_TRAP();
  3397. KeWaitForSingleObject(
  3398. &event,
  3399. Suspended,
  3400. KernelMode,
  3401. FALSE,
  3402. NULL);
  3403. }
  3404. if (!NT_SUCCESS(ntStatus)) {
  3405. USBH_KdTrap(("Query capabilities failed!\n"));
  3406. }
  3407. IoFreeIrp(irp);
  3408. }
  3409. BOOLEAN
  3410. USBH_HubIsBusPowered(
  3411. IN PDEVICE_OBJECT DeviceObject,
  3412. IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor
  3413. )
  3414. /* ++
  3415. *
  3416. * Description:
  3417. *
  3418. * Return:
  3419. *
  3420. * TRUE if the hub is bus powered
  3421. *
  3422. * -- */
  3423. {
  3424. NTSTATUS ntStatus = STATUS_SUCCESS;
  3425. USHORT power, statusBits;
  3426. BOOLEAN busPowered;
  3427. PAGED_CODE();
  3428. USBH_KdPrint((2,"'enter HubIsBusPowered\n"));
  3429. // read the power bits from the config descriptor
  3430. power = ConfigurationDescriptor->bmAttributes &
  3431. USB_CONFIG_POWERED_MASK;
  3432. //
  3433. // now attempt to get the status bits from the device
  3434. //
  3435. ntStatus = USBH_SyncGetStatus(DeviceObject,
  3436. &statusBits,
  3437. URB_FUNCTION_GET_STATUS_FROM_DEVICE,
  3438. 0);
  3439. if (NT_SUCCESS(ntStatus)) {
  3440. USBH_KdPrint((2,"'hub status bits %x\n", statusBits));
  3441. busPowered = !(statusBits & USB_GETSTATUS_SELF_POWERED);
  3442. } else {
  3443. USBH_KdBreak(("device failed get status %x, power bits = %x\n",
  3444. ntStatus, power));
  3445. //
  3446. // device failed get_status, fall back to the values in the
  3447. // config descriptor.
  3448. //
  3449. busPowered = power == USB_CONFIG_BUS_POWERED;
  3450. }
  3451. return busPowered;
  3452. }
  3453. NTSTATUS
  3454. USBH_PnPIrp_Complete(
  3455. IN PDEVICE_OBJECT DeviceObject,
  3456. IN PIRP Irp,
  3457. IN PVOID Context
  3458. )
  3459. /*++
  3460. Routine Description:
  3461. This routine is called when the port driver completes an IRP.
  3462. Arguments:
  3463. DeviceObject - Pointer to the device object for the class device.
  3464. Irp - Irp completed.
  3465. Context - Driver defined context.
  3466. Return Value:
  3467. The function value is the final status from the operation.
  3468. --*/
  3469. {
  3470. NTSTATUS ntStatus = STATUS_SUCCESS;
  3471. PIO_STACK_LOCATION irpStack;
  3472. PDEVICE_EXTENSION_FDO deviceExtension;
  3473. USBH_KdPrint((2,"'enter USBH_PnPIrp_Complete\n"));
  3474. deviceExtension = Context;
  3475. // kenray sez we should be calling IoMarkIrpPending
  3476. // from our completion routine.
  3477. //
  3478. if (Irp->PendingReturned) {
  3479. IoMarkIrpPending(Irp);
  3480. }
  3481. irpStack = IoGetCurrentIrpStackLocation (Irp);
  3482. USBH_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
  3483. USBH_ASSERT(irpStack->MinorFunction == IRP_MN_START_DEVICE);
  3484. USBH_KdPrint((2,"'IRP_MN_START_DEVICE (fdo), completion routine\n"));
  3485. //bugbug this is broken but they won't let us fix it
  3486. // signal the start device dispatch to finsh
  3487. KeSetEvent(&deviceExtension->PnpStartEvent,
  3488. 1,
  3489. FALSE);
  3490. // defer completion
  3491. ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
  3492. Irp->IoStatus.Status = ntStatus;
  3493. USBH_KdPrint((2,"'exit USH_PnPIrp_Complete %x\n", ntStatus));
  3494. return ntStatus;
  3495. }
  3496. NTSTATUS
  3497. USBH_HubPnPIrp_Complete(
  3498. IN PDEVICE_OBJECT DeviceObject,
  3499. IN PIRP Irp,
  3500. IN PVOID Context
  3501. )
  3502. /*++
  3503. Routine Description:
  3504. This routine is called when the port driver completes an IRP.
  3505. Arguments:
  3506. DeviceObject - Pointer to the device object for the class device.
  3507. Irp - Irp completed.
  3508. Context - Driver defined context.
  3509. Return Value:
  3510. The function value is the final status from the operation.
  3511. --*/
  3512. {
  3513. NTSTATUS ntStatus = STATUS_SUCCESS;
  3514. PIO_STACK_LOCATION irpStack;
  3515. PDEVICE_EXTENSION_HUB deviceExtensionHub;
  3516. USBH_KdPrint((2,"'enter USBH_HubPnPIrp_Complete\n"));
  3517. deviceExtensionHub = Context;
  3518. // kenray sez we should be calling IoMarkIrpPending
  3519. // from our completion routine.
  3520. //
  3521. // No. Since this IRP is completed synchronously (on the same thread that
  3522. // created it), we should not do this.
  3523. //
  3524. // if (Irp->PendingReturned) {
  3525. // IoMarkIrpPending(Irp);
  3526. // }
  3527. irpStack = IoGetCurrentIrpStackLocation (Irp);
  3528. USBH_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
  3529. USBH_ASSERT(irpStack->MinorFunction == IRP_MN_START_DEVICE);
  3530. USBH_KdPrint((2,"'IRP_MN_START_DEVICE (fdo), completion routine\n"));
  3531. //bugbug this is broken but they won't let us fix it
  3532. // signal the start device dispatch to finsh
  3533. KeSetEvent(&deviceExtensionHub->PnpStartEvent,
  3534. 1,
  3535. FALSE);
  3536. if (!NT_SUCCESS(Irp->IoStatus.Status)) {
  3537. deviceExtensionHub->HubFlags |= HUBFLAG_HUB_FAILURE;
  3538. }
  3539. // defer completion
  3540. ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
  3541. Irp->IoStatus.Status = ntStatus;
  3542. USBH_KdPrint((2,"'exit USH_HubPnPIrp_Complete %x\n", ntStatus));
  3543. return ntStatus;
  3544. }
  3545. NTSTATUS
  3546. USBH_FdoHubStartDevice(
  3547. IN OUT PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  3548. IN PIRP Irp)
  3549. /* ++ Description:
  3550. *
  3551. * This routine is called by PnP via (IRP_MJ_PNP, IRP_MN_START_DEVICE). We will
  3552. * initialize the hub and ready all ports.
  3553. *
  3554. * Argument:
  3555. *
  3556. * Return:
  3557. *
  3558. * STATUS_SUCCESS - if successful STATUS_UNSUCCESSFUL - otherwise
  3559. *
  3560. * -- */
  3561. {
  3562. NTSTATUS status, ntStatus = STATUS_SUCCESS;
  3563. PDEVICE_OBJECT deviceObject;
  3564. PDEVICE_EXTENSION_PORT hubParentDeviceExtensionPort;
  3565. PPORT_DATA portData;
  3566. DEVICE_CAPABILITIES deviceCapabilities;
  3567. ULONG hubCount = 0, p;
  3568. LONG i;
  3569. #if DBG
  3570. BOOLEAN bWakeSupported = FALSE;
  3571. #endif
  3572. PAGED_CODE();
  3573. USBH_KdPrint((2,"'Enter Hub StartDevice\n"));
  3574. USBH_ASSERT(EXTENSION_TYPE_HUB == DeviceExtensionHub->ExtensionType);
  3575. deviceObject = DeviceExtensionHub->FunctionalDeviceObject;
  3576. //
  3577. // New hub
  3578. //
  3579. LOGENTRY(LOG_PNP, "hSTR", DeviceExtensionHub, 0, 0);
  3580. //
  3581. // initailize allocated structures to NULL;
  3582. //
  3583. DeviceExtensionHub->HubDescriptor = NULL;
  3584. DeviceExtensionHub->Irp = NULL;
  3585. DeviceExtensionHub->TransferBuffer = NULL;
  3586. DeviceExtensionHub->ConfigurationDescriptor = NULL;
  3587. // transition to zero signals the event
  3588. DeviceExtensionHub->PendingRequestCount = 1;
  3589. DeviceExtensionHub->HubFlags = 0;
  3590. DeviceExtensionHub->PendingWakeIrp = NULL;
  3591. DeviceExtensionHub->NumberPortWakeIrps = 0;
  3592. DeviceExtensionHub->PendingIdleIrp = NULL;
  3593. DeviceExtensionHub->ChangeIndicationWorkitemPending = 0;
  3594. // Although this is only used for the Root Hub, we initialize for all hubs.
  3595. DeviceExtensionHub->CurrentSystemPowerState = PowerSystemWorking;
  3596. KeInitializeEvent(&DeviceExtensionHub->PnpStartEvent, NotificationEvent, FALSE);
  3597. KeInitializeSpinLock (&DeviceExtensionHub->CheckIdleSpinLock);
  3598. InitializeListHead(&DeviceExtensionHub->DeletePdoList);
  3599. USBH_KdPrint((2,"'Set PnPIrp Completion Routine\n"));
  3600. IoCopyCurrentIrpStackLocationToNext(Irp);
  3601. IoSetCompletionRoutine(Irp,
  3602. USBH_HubPnPIrp_Complete,
  3603. // always pass FDO to completion routine
  3604. DeviceExtensionHub,
  3605. TRUE,
  3606. TRUE,
  3607. TRUE);
  3608. status = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  3609. Irp);
  3610. if (status == STATUS_PENDING) {
  3611. KeWaitForSingleObject(&DeviceExtensionHub->PnpStartEvent,
  3612. Suspended,
  3613. KernelMode,
  3614. FALSE,
  3615. NULL);
  3616. }
  3617. DeviceExtensionHub->RootHubPdo = NULL;
  3618. ntStatus =
  3619. USBH_SyncGetRootHubPdo(DeviceExtensionHub->TopOfStackDeviceObject,
  3620. &DeviceExtensionHub->RootHubPdo,
  3621. &DeviceExtensionHub->TopOfHcdStackDeviceObject,
  3622. NULL);
  3623. if (!NT_SUCCESS(ntStatus)) {
  3624. USBH_KdBreak(("StartDevice USBH_SyncGetRootHubPdo fail code %x\n",
  3625. ntStatus));
  3626. goto USBH_StartDeviceDone;
  3627. }
  3628. // init our failreason
  3629. USBH_WriteFailReason(
  3630. DeviceExtensionHub->PhysicalDeviceObject,
  3631. USBH_FAILREASON_HUB_GENERAL_FAILURE);
  3632. if (DeviceExtensionHub->HubFlags & HUBFLAG_HUB_FAILURE) {
  3633. ntStatus = STATUS_UNSUCCESSFUL;
  3634. USBH_KdBreak(("Hub Start Failure\n"));
  3635. goto USBH_StartDeviceDone;
  3636. }
  3637. // assume device supports wakeup by default
  3638. DeviceExtensionHub->HubFlags |= HUBFLAG_SUPPORT_WAKEUP;
  3639. {
  3640. NTSTATUS status;
  3641. ULONG disableWakeup = 0;
  3642. WCHAR USBH_RemoteWakeupKey[] = L"DisableRemoteWakeup";
  3643. status =
  3644. USBD_GetPdoRegistryParameter(DeviceExtensionHub->PhysicalDeviceObject,
  3645. &disableWakeup,
  3646. sizeof(disableWakeup),
  3647. USBH_RemoteWakeupKey,
  3648. sizeof(USBH_RemoteWakeupKey));
  3649. if (NT_SUCCESS(status) && disableWakeup) {
  3650. DeviceExtensionHub->HubFlags &= ~HUBFLAG_SUPPORT_WAKEUP;
  3651. USBH_KdPrint((1, "'Warning: remote wakeup disabled in registry\n"));
  3652. }
  3653. }
  3654. DeviceExtensionHub->CurrentPowerState = PowerDeviceD0;
  3655. KeInitializeEvent(&DeviceExtensionHub->AbortEvent, NotificationEvent,
  3656. TRUE);
  3657. // initial state is not signaled
  3658. KeInitializeEvent(&DeviceExtensionHub->PendingRequestEvent, NotificationEvent,
  3659. FALSE);
  3660. KeInitializeEvent(&DeviceExtensionHub->SubmitIdleEvent, NotificationEvent,
  3661. FALSE);
  3662. // This one defaults to signalled.
  3663. KeInitializeEvent(&DeviceExtensionHub->CWKEvent, NotificationEvent,
  3664. TRUE);
  3665. KeInitializeSemaphore(&DeviceExtensionHub->HubMutex, 1, 1);
  3666. KeInitializeSemaphore(&DeviceExtensionHub->HubPortResetMutex, 1, 1);
  3667. KeInitializeSemaphore(&DeviceExtensionHub->ResetDeviceMutex, 1, 1);
  3668. USBH_ASSERT(DeviceExtensionHub->RootHubPdo);
  3669. USBH_SyncGetRootHubPdo(DeviceExtensionHub->TopOfStackDeviceObject,
  3670. NULL,
  3671. NULL,
  3672. &hubCount);
  3673. #ifdef USB2
  3674. ntStatus = USBHUB_GetBusInterface(DeviceExtensionHub->RootHubPdo,
  3675. &DeviceExtensionHub->BusIf);
  3676. if (!NT_SUCCESS(ntStatus)) {
  3677. USBH_KdBreak(("StartDevice USBHUB_GetBusInterface fail code %x\n",
  3678. ntStatus));
  3679. goto USBH_StartDeviceDone;
  3680. }
  3681. ntStatus = USBHUB_GetBusInterfaceUSBDI(DeviceExtensionHub->TopOfStackDeviceObject,
  3682. &DeviceExtensionHub->UsbdiBusIf);
  3683. if (!NT_SUCCESS(ntStatus)) {
  3684. USBH_KdBreak(("StartDevice USBHUB_GetBusInterfaceUSBDI fail code %x\n",
  3685. ntStatus));
  3686. goto USBH_StartDeviceDone;
  3687. }
  3688. USBH_InitializeUSB2Hub(DeviceExtensionHub);
  3689. #endif
  3690. USBH_KdPrint((2,"'Hub Count is %d\n", hubCount));
  3691. //
  3692. // allow no more than five physical hubs plus the root
  3693. // (7.1.16)
  3694. //
  3695. #if DBG
  3696. if (UsbhPnpTest & PNP_TEST_FAIL_HUB_COUNT) {
  3697. hubCount = 7;
  3698. }
  3699. #endif
  3700. if (hubCount > 6) {
  3701. USBH_WriteFailReason(
  3702. DeviceExtensionHub->PhysicalDeviceObject,
  3703. USBH_FAILREASON_MAXHUBS_CONNECTED);
  3704. USBH_KdPrint((1,"'StartDevice: hubs are stacked too deep (%x)\n", hubCount - 1));
  3705. hubParentDeviceExtensionPort = DeviceExtensionHub->PhysicalDeviceObject->DeviceExtension;
  3706. portData = &hubParentDeviceExtensionPort->DeviceExtensionHub->PortData[hubParentDeviceExtensionPort->PortNumber-1];
  3707. portData->ConnectionStatus = DeviceHubNestedTooDeeply;
  3708. // Don't clear the hub's reference to this PDO because we will just try to
  3709. // create a new one when QDR is called and we see that there is still a device
  3710. // connected to the port.
  3711. // portData->DeviceObject = NULL;
  3712. // generate a WMI event so UI can inform the user
  3713. USBH_PdoEvent(hubParentDeviceExtensionPort->DeviceExtensionHub,
  3714. hubParentDeviceExtensionPort->PortNumber);
  3715. // We fail the hub here but don't return an error so that the device
  3716. // is not removed and the UI can display an error message about it.
  3717. HUB_FAILURE(DeviceExtensionHub);
  3718. }
  3719. // Initialize DeviceCapabilities structure in case USBH_QueryCapabilities
  3720. // is unsuccessful.
  3721. RtlZeroMemory(&deviceCapabilities, sizeof(DEVICE_CAPABILITIES));
  3722. USBH_QueryCapabilities(DeviceExtensionHub->TopOfStackDeviceObject,
  3723. &deviceCapabilities);
  3724. //
  3725. // save the system state mapping
  3726. //
  3727. for (i = 0 ; i< PowerSystemMaximum ; i++) {
  3728. DeviceExtensionHub->DeviceState[i] = PowerDeviceD3;
  3729. }
  3730. RtlCopyMemory(&DeviceExtensionHub->DeviceState[0],
  3731. &deviceCapabilities.DeviceState[0],
  3732. sizeof(deviceCapabilities.DeviceState));
  3733. DeviceExtensionHub->SystemWake = deviceCapabilities.SystemWake;
  3734. DeviceExtensionHub->DeviceWake = deviceCapabilities.DeviceWake;
  3735. #if DBG
  3736. USBH_KdPrint((1,"'>>>>>> Hub DeviceCaps\n"));
  3737. USBH_KdPrint((1,"'SystemWake = (%d)\n", DeviceExtensionHub->SystemWake));
  3738. USBH_KdPrint((1,"'DeviceWake = (D%d)\n",
  3739. DeviceExtensionHub->DeviceWake-1));
  3740. for (i=PowerSystemUnspecified; i< PowerSystemHibernate; i++) {
  3741. USBH_KdPrint((1,"'Device State Map: sysstate %d = devstate 0x%x\n", i,
  3742. DeviceExtensionHub->DeviceState[i]));
  3743. }
  3744. USBH_KdBreak(("'>>>>>> Hub DeviceCaps\n"));
  3745. // Spit out message on the debugger indicating whether the Root Hub
  3746. // will support wake, according to the mapping table.
  3747. if (IS_ROOT_HUB(DeviceExtensionHub)) {
  3748. USBH_KdPrint((1,"'\n\tWake support summary for USB Root Hub:\n\n"));
  3749. if (DeviceExtensionHub->SystemWake <= PowerSystemWorking) {
  3750. USBH_KdPrint((1,"'USB Root Hub can't wake machine because SystemWake does not support it.\n"));
  3751. } else {
  3752. for (i = PowerSystemSleeping1, bWakeSupported = FALSE; i <= DeviceExtensionHub->SystemWake; i++) {
  3753. if (DeviceExtensionHub->DeviceState[i] != PowerDeviceUnspecified &&
  3754. DeviceExtensionHub->DeviceState[i] <= DeviceExtensionHub->DeviceWake) {
  3755. bWakeSupported = TRUE;
  3756. USBH_KdPrint((1,"'USB Root Hub can wake machine from S%x (maps to D%x).\n",
  3757. i - 1, DeviceExtensionHub->DeviceState[i] - 1));
  3758. }
  3759. }
  3760. if (!bWakeSupported) {
  3761. USBH_KdPrint((1,"'USB Root Hub can't wake machine because DeviceState table does not support it.\n"));
  3762. }
  3763. }
  3764. }
  3765. #endif
  3766. //
  3767. // get our device descriptor
  3768. //
  3769. ntStatus = USBH_GetDeviceDescriptor(DeviceExtensionHub->FunctionalDeviceObject,
  3770. &DeviceExtensionHub->DeviceDescriptor);
  3771. if (!NT_SUCCESS(ntStatus)) {
  3772. USBH_KdBreak(("StartDevice USBH_GetHubDeviceDescriptor fail code %x\n",
  3773. ntStatus));
  3774. goto USBH_StartDeviceDone;
  3775. }
  3776. ntStatus = USBH_GetConfigurationDescriptor(DeviceExtensionHub->FunctionalDeviceObject,
  3777. &DeviceExtensionHub->ConfigurationDescriptor);
  3778. if (!NT_SUCCESS(ntStatus)) {
  3779. USBH_KdBreak(("Hub StartDevice USBH_GetConfigurationDescriptor fail code %x\n",
  3780. ntStatus));
  3781. goto USBH_StartDeviceDone;
  3782. }
  3783. //
  3784. // Get Hub specific descriptor.
  3785. //
  3786. // port data array allocated by this function
  3787. //
  3788. ntStatus = USBH_SyncGetHubDescriptor(DeviceExtensionHub);
  3789. if (!NT_SUCCESS(ntStatus)) {
  3790. USBH_KdBreak(("StartDevice USBH_GetHubDescriptor fail code %x\n", ntStatus));
  3791. goto USBH_StartDeviceDone;
  3792. }
  3793. if (USBH_HubIsBusPowered(DeviceExtensionHub->FunctionalDeviceObject,
  3794. DeviceExtensionHub->ConfigurationDescriptor)) {
  3795. // we have 500 mA to work with
  3796. DeviceExtensionHub->MaximumPowerPerPort = 100;
  3797. //
  3798. // The amount of current a bus powered hub will draw (mA)
  3799. // should be calculated as follows:
  3800. //
  3801. // NumberOfExternalPorts * 100 + HubCntrolCurrent +
  3802. // power required for embeded functions
  3803. //
  3804. // this value cannot exceed 500 ma, the hub config
  3805. // descriptor should report this value but in most cases
  3806. // does not so we set it to the worst case value to insure
  3807. // that a bus powered hub cannot be connected to another
  3808. // bus powered hub.
  3809. UsbhInfo(DeviceExtensionHub);
  3810. DeviceExtensionHub->ConfigurationDescriptor->MaxPower = 250;
  3811. } else {
  3812. // self powered hub can supply 500 mA per port
  3813. DeviceExtensionHub->MaximumPowerPerPort = 500;
  3814. UsbhInfo(DeviceExtensionHub);
  3815. }
  3816. USBH_KdPrint((2,"'per port power for hub = %d\n", DeviceExtensionHub->MaximumPowerPerPort));
  3817. //
  3818. // attempt to configure the device
  3819. //
  3820. ntStatus = USBH_OpenConfiguration(DeviceExtensionHub);
  3821. if (!NT_SUCCESS(ntStatus)) {
  3822. USBH_KdBreak(("StartDevice USBH_OpenConfiguration fail code %x\n", ntStatus));
  3823. goto USBH_StartDeviceDone;
  3824. }
  3825. // if this is a usb 2 hub
  3826. if (DeviceExtensionHub->HubFlags & HUBFLAG_USB20_HUB) {
  3827. ntStatus = USBD_InitUsb2Hub(DeviceExtensionHub);
  3828. }
  3829. if (!NT_SUCCESS(ntStatus)) {
  3830. USBH_KdBreak(("StartDevice failed USB 2.0 init %x\n", ntStatus));
  3831. goto USBH_StartDeviceDone;
  3832. }
  3833. //
  3834. // Allocate a permanent Irp for this hub
  3835. //
  3836. DeviceExtensionHub->Irp =
  3837. IoAllocateIrp(DeviceExtensionHub->FunctionalDeviceObject->StackSize, FALSE);
  3838. USBH_KdPrint((2,"'StartDevice AllocateIrp Irp %x StackSize %d\n",
  3839. DeviceExtensionHub->Irp, DeviceExtensionHub->FunctionalDeviceObject->StackSize));
  3840. if (NULL == DeviceExtensionHub->Irp) {
  3841. USBH_KdBreak(("StartDevice failed to alloc Irp\n"));
  3842. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  3843. goto USBH_StartDeviceDone;
  3844. }
  3845. //
  3846. // Allocate a transfer buffer which together with the permanent Irp and
  3847. // Urb
  3848. // for the hub will be use to do InterruptTransfer.
  3849. //
  3850. DeviceExtensionHub->TransferBufferLength =
  3851. DeviceExtensionHub->PipeInformation.MaximumPacketSize;
  3852. DeviceExtensionHub->TransferBuffer = UsbhExAllocatePool(NonPagedPool,
  3853. DeviceExtensionHub->TransferBufferLength);
  3854. if (NULL == DeviceExtensionHub->TransferBuffer) {
  3855. USBH_KdBreak(("StartDevice fail alloc TransferBuffer\n"));
  3856. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  3857. goto USBH_StartDeviceDone;
  3858. }
  3859. USBH_KdPrint((2,"'StartDevice TransferBuffer %x size %x\n",
  3860. DeviceExtensionHub->TransferBuffer,
  3861. DeviceExtensionHub->PipeInformation.MaximumPacketSize));
  3862. //
  3863. // Power on all down stream ports.
  3864. // Be it ganged powered, individual powered or none switched
  3865. //
  3866. ntStatus = USBH_SyncPowerOnPorts(DeviceExtensionHub);
  3867. #if DBG
  3868. if (!NT_SUCCESS(ntStatus)) {
  3869. USBH_KdBreak(("StartDevice USBH_SyncPowerOnPorts fail code %x\n", ntStatus));
  3870. }
  3871. #endif
  3872. #if DBG
  3873. if (UsbhPnpTest & PNP_TEST_FAIL_HUB) {
  3874. ntStatus = STATUS_UNSUCCESSFUL;
  3875. }
  3876. #endif
  3877. USBH_StartDeviceDone:
  3878. if (NT_SUCCESS(ntStatus)) {
  3879. //
  3880. // So we are started.
  3881. //
  3882. DeviceExtensionHub->HubFlags |= HUBFLAG_NEED_CLEANUP;
  3883. //
  3884. // first clear any status changes the hub may be asserting
  3885. //
  3886. for (p=1; p<= DeviceExtensionHub->HubDescriptor->bNumberOfPorts; p++) {
  3887. USBH_SyncClearPortStatus(DeviceExtensionHub,
  3888. (USHORT)p,
  3889. FEATURE_C_PORT_CONNECT);
  3890. }
  3891. //
  3892. // Tell the OS that this PDO can have kids.
  3893. //
  3894. //
  3895. // Workaround for PnP bug #406381 - RC3SS: Bluescreen failure when
  3896. // installing/deinstalling communication ports
  3897. //
  3898. //===== Assigned by santoshj on 09/23/99 10:27:20 to kenray =====
  3899. // This is a race condition between IopInitializeSystemDrivers and
  3900. // IoInvalidateDeviceRelations. The real fix is too big a change at this
  3901. // stage of the product and has potential of exposing other problems. This
  3902. // problem can be solved if USBHUB does not invalidate device relations on
  3903. // every start which is redundant anyway (and also exposes this bug).
  3904. //
  3905. // USBH_IoInvalidateDeviceRelations(DeviceExtensionHub->PhysicalDeviceObject,
  3906. // BusRelations);
  3907. //
  3908. // Start polling the hub
  3909. //
  3910. #ifdef NEW_START
  3911. // establish callback to start the hub
  3912. if (IS_ROOT_HUB(DeviceExtensionHub)) {
  3913. USBD_RegisterRhHubCallBack(DeviceExtensionHub);
  3914. } else {
  3915. DeviceExtensionHub->HubFlags |= HUBFLAG_OK_TO_ENUMERATE;
  3916. USBH_SubmitInterruptTransfer(DeviceExtensionHub);
  3917. }
  3918. #else
  3919. USBH_SubmitInterruptTransfer(DeviceExtensionHub);
  3920. #endif
  3921. } else {
  3922. //
  3923. // clean up allocated structures
  3924. //
  3925. USBH_KdBreak(("USBH_FdoStartDevice_Error\n"));
  3926. LOGENTRY(LOG_PNP, "STR!", DeviceExtensionHub, 0, 0);
  3927. if (DeviceExtensionHub->HubDescriptor) {
  3928. UsbhExFreePool(DeviceExtensionHub->HubDescriptor);
  3929. DeviceExtensionHub->HubDescriptor = NULL;
  3930. }
  3931. if (DeviceExtensionHub->Irp) {
  3932. IoFreeIrp(DeviceExtensionHub->Irp);
  3933. DeviceExtensionHub->Irp = NULL;
  3934. }
  3935. if (DeviceExtensionHub->TransferBuffer) {
  3936. UsbhExFreePool(DeviceExtensionHub->TransferBuffer);
  3937. DeviceExtensionHub->TransferBuffer = NULL;
  3938. }
  3939. if (DeviceExtensionHub->ConfigurationDescriptor) {
  3940. UsbhExFreePool(DeviceExtensionHub->ConfigurationDescriptor);
  3941. DeviceExtensionHub->ConfigurationDescriptor = NULL;
  3942. }
  3943. }
  3944. //
  3945. // complete the start Irp now since we pended it with
  3946. // our completion handler.
  3947. //
  3948. LOGENTRY(LOG_PNP, "STRc", DeviceExtensionHub, 0, ntStatus);
  3949. USBH_CompleteIrp(Irp, ntStatus);
  3950. return ntStatus;
  3951. }
  3952. VOID
  3953. UsbhFdoCleanup(
  3954. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  3955. )
  3956. /* ++
  3957. *
  3958. * Description:
  3959. *
  3960. * This routine is called to shut down the hub.
  3961. *
  3962. * All we do here is abort or pending interrupt transfer and wait for it to
  3963. * complete and fre up memeory resources
  3964. *
  3965. * Argument:
  3966. *
  3967. * DeviceExtensionHub - This is a a hub device extension.
  3968. *
  3969. * Return:
  3970. *
  3971. * STATUS_SUCCESS
  3972. *
  3973. * -- */
  3974. {
  3975. PDEVICE_OBJECT deviceObject;
  3976. PPORT_DATA portData;
  3977. USHORT p, numberOfPorts;
  3978. KIRQL irql;
  3979. PIRP wWIrp = NULL;
  3980. PIRP hubIdleIrp = NULL;
  3981. PIRP idleIrp = NULL;
  3982. PIRP waitWakeIrp = NULL;
  3983. PVOID deviceData;
  3984. NTSTATUS status, ntStatus;
  3985. BOOLEAN bRet;
  3986. // Can't acquire (cancel) spin locks in paged code!
  3987. // TODO: isolate the pieces of code that require the spin lock into helper
  3988. // functions
  3989. // PAGED_CODE();
  3990. USBH_KdPrint((2,"'UsbhFdoCleanup Fdo extension %x\n", DeviceExtensionHub));
  3991. LOGENTRY(LOG_PNP, "fdoC", DeviceExtensionHub,0,
  3992. DeviceExtensionHub->HubFlags);
  3993. deviceObject = DeviceExtensionHub->FunctionalDeviceObject;
  3994. USBD_UnRegisterRhHubCallBack(DeviceExtensionHub);
  3995. //
  3996. // set our stop flag so that ChangeIndication does not submit
  3997. // any more transfers or queue more workitems, important to do
  3998. // this before we send the abort.
  3999. //
  4000. DeviceExtensionHub->HubFlags |= HUBFLAG_DEVICE_STOPPING;
  4001. // If there is a ChangeIndicationWorkitem pending, then we
  4002. // must wait for that to complete.
  4003. if (DeviceExtensionHub->ChangeIndicationWorkitemPending) {
  4004. USBH_KdPrint((2,"'Wait for single object\n"));
  4005. ntStatus = KeWaitForSingleObject(&DeviceExtensionHub->CWKEvent,
  4006. Suspended,
  4007. KernelMode,
  4008. FALSE,
  4009. NULL);
  4010. USBH_KdPrint((2,"'Wait for single object, returned %x\n", ntStatus));
  4011. }
  4012. LOGENTRY(LOG_PNP, "fdoX", DeviceExtensionHub, deviceObject,
  4013. DeviceExtensionHub->HubFlags);
  4014. // wait for any idle irps to be submitted before attepting to cancel
  4015. if (DeviceExtensionHub->HubFlags & HUBFLAG_PENDING_IDLE_IRP) {
  4016. USBH_KdPrint((2,"'Wait for single object\n"));
  4017. LOGENTRY(LOG_PNP, "fdIW", DeviceExtensionHub,0,
  4018. DeviceExtensionHub->HubFlags);
  4019. ntStatus = KeWaitForSingleObject(&DeviceExtensionHub->SubmitIdleEvent,
  4020. Suspended,
  4021. KernelMode,
  4022. FALSE,
  4023. NULL);
  4024. USBH_KdPrint((2,"'Wait for single object, returned %x\n", ntStatus));
  4025. }
  4026. //
  4027. // dump our wake request
  4028. //
  4029. IoAcquireCancelSpinLock(&irql);
  4030. if (DeviceExtensionHub->PendingWakeIrp) {
  4031. LOGENTRY(LOG_PNP, "CwkI", DeviceExtensionHub, 0,
  4032. DeviceExtensionHub->PendingWakeIrp);
  4033. USBH_ASSERT(DeviceExtensionHub->HubFlags & HUBFLAG_PENDING_WAKE_IRP);
  4034. wWIrp = DeviceExtensionHub->PendingWakeIrp;
  4035. DeviceExtensionHub->PendingWakeIrp = NULL;
  4036. }
  4037. if (DeviceExtensionHub->PendingIdleIrp) {
  4038. hubIdleIrp = DeviceExtensionHub->PendingIdleIrp;
  4039. DeviceExtensionHub->PendingIdleIrp = NULL;
  4040. }
  4041. IoReleaseCancelSpinLock(irql);
  4042. if (wWIrp) {
  4043. USBH_HubCancelWakeIrp(DeviceExtensionHub, wWIrp);
  4044. }
  4045. USBH_HubCompletePortWakeIrps(DeviceExtensionHub, STATUS_DELETE_PENDING);
  4046. if (hubIdleIrp) {
  4047. USBH_HubCancelIdleIrp(DeviceExtensionHub, hubIdleIrp);
  4048. }
  4049. //
  4050. // wait for all work items to finish...
  4051. // the event is not signaled if a work item is pending
  4052. //
  4053. // this code takes care of work items that may have been queued
  4054. // before the ShutDown flag was set.
  4055. // note: no additional work items will be queued once the
  4056. // HUBFLAG_DEVICE_STOPPING flag is set.
  4057. //
  4058. if (InterlockedDecrement(&DeviceExtensionHub->PendingRequestCount) > 0) {
  4059. //
  4060. // need to wait
  4061. //
  4062. LOGENTRY(LOG_PNP, "hWAT", DeviceExtensionHub,
  4063. &DeviceExtensionHub->PendingRequestEvent,
  4064. DeviceExtensionHub->PendingRequestCount);
  4065. status = KeWaitForSingleObject(
  4066. &DeviceExtensionHub->PendingRequestEvent,
  4067. Suspended,
  4068. KernelMode,
  4069. FALSE,
  4070. NULL);
  4071. LOGENTRY(LOG_PNP, "hwat", DeviceExtensionHub,
  4072. &DeviceExtensionHub->PendingRequestEvent,
  4073. DeviceExtensionHub->PendingRequestCount);
  4074. }
  4075. USBH_KdPrint((2,"'Work Items Finished %x\n", DeviceExtensionHub));
  4076. USBH_ASSERT(DeviceExtensionHub->PendingRequestCount == 0);
  4077. //
  4078. // now cancel any outstanding transfers
  4079. //
  4080. if (DeviceExtensionHub->Irp) {
  4081. status = USBH_AbortInterruptPipe(DeviceExtensionHub);
  4082. // If the ABORT_PIPE request failed then we should cancel
  4083. // the Interrupt IRP before freeing it, otherwise we are likely
  4084. // freeing the IRP while it is still in use.
  4085. if (!NT_SUCCESS(status)) {
  4086. bRet = IoCancelIrp(DeviceExtensionHub->Irp);
  4087. // Only wait on the abort event if the IRP was actually
  4088. // cancelled.
  4089. if (bRet) {
  4090. LOGENTRY(LOG_PNP, "aWAT", DeviceExtensionHub,
  4091. &DeviceExtensionHub->AbortEvent, 0);
  4092. status = KeWaitForSingleObject(
  4093. &DeviceExtensionHub->AbortEvent,
  4094. Suspended,
  4095. KernelMode,
  4096. FALSE,
  4097. NULL);
  4098. }
  4099. }
  4100. IoFreeIrp(DeviceExtensionHub->Irp);
  4101. DeviceExtensionHub->Irp = NULL;
  4102. }
  4103. USBH_KdPrint((2,"'Abort Finished %x\n", DeviceExtensionHub));
  4104. //
  4105. // disable the ports in case we are re-started
  4106. //
  4107. USBH_ASSERT(DeviceExtensionHub->HubDescriptor != NULL);
  4108. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  4109. portData = DeviceExtensionHub->PortData;
  4110. if (portData) {
  4111. for (p = 1;
  4112. p <= numberOfPorts;
  4113. p++, portData++) {
  4114. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  4115. //
  4116. // do this after we abort are interrupt pipe,
  4117. // it doesn't matter if it fails.
  4118. //
  4119. LOGENTRY(LOG_PNP, "offP", DeviceExtensionHub,
  4120. p, portData->DeviceObject);
  4121. //
  4122. // check our PDOs -- if this is a remove then we should have
  4123. // none -- otherwise this is a stop.
  4124. //
  4125. if (portData->DeviceObject) {
  4126. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  4127. //
  4128. // it is possible that the PDO was never actually started
  4129. // if this is the case then the PDO won't be marked for reset
  4130. // we mark it here and free up the associated bus resources
  4131. IoAcquireCancelSpinLock(&irql);
  4132. if (deviceExtensionPort->IdleNotificationIrp) {
  4133. idleIrp = deviceExtensionPort->IdleNotificationIrp;
  4134. deviceExtensionPort->IdleNotificationIrp = NULL;
  4135. deviceExtensionPort->PortPdoFlags &= ~PORTPDO_IDLE_NOTIFIED;
  4136. if (idleIrp->Cancel) {
  4137. idleIrp = NULL;
  4138. }
  4139. if (idleIrp) {
  4140. IoSetCancelRoutine(idleIrp, NULL);
  4141. }
  4142. }
  4143. if (deviceExtensionPort->WaitWakeIrp) {
  4144. waitWakeIrp = deviceExtensionPort->WaitWakeIrp;
  4145. deviceExtensionPort->WaitWakeIrp = NULL;
  4146. deviceExtensionPort->PortPdoFlags &=
  4147. ~PORTPDO_REMOTE_WAKEUP_ENABLED;
  4148. if (waitWakeIrp->Cancel || IoSetCancelRoutine(waitWakeIrp, NULL) == NULL) {
  4149. waitWakeIrp = NULL;
  4150. // Must decrement pending request count here because
  4151. // we don't complete the IRP below and USBH_WaitWakeCancel
  4152. // won't either because we have cleared the IRP pointer
  4153. // in the device extension above.
  4154. USBH_DEC_PENDING_IO_COUNT(DeviceExtensionHub);
  4155. }
  4156. UsbhWarning(deviceExtensionPort,
  4157. "Device Driver did not cancel wait_wake irp on stop/remove\n",
  4158. FALSE);
  4159. }
  4160. //
  4161. // Finally, release the cancel spin lock
  4162. //
  4163. IoReleaseCancelSpinLock(irql);
  4164. if (idleIrp) {
  4165. idleIrp->IoStatus.Status = STATUS_CANCELLED;
  4166. IoCompleteRequest(idleIrp, IO_NO_INCREMENT);
  4167. }
  4168. if (waitWakeIrp) {
  4169. USBH_CompletePowerIrp(DeviceExtensionHub, waitWakeIrp,
  4170. STATUS_CANCELLED);
  4171. }
  4172. if (!(deviceExtensionPort->PortPdoFlags & PORTPDO_NEED_RESET)) {
  4173. USBH_KdPrint((1,
  4174. "'do %x was never started, marking for reset\n",
  4175. portData->DeviceObject));
  4176. deviceData = InterlockedExchangePointer(
  4177. &deviceExtensionPort->DeviceData,
  4178. NULL);
  4179. if (deviceData) {
  4180. #ifdef USB2
  4181. USBD_RemoveDeviceEx(DeviceExtensionHub,
  4182. deviceData,
  4183. deviceExtensionPort->DeviceExtensionHub->RootHubPdo,
  4184. 0);
  4185. #else
  4186. USBD_RemoveDevice(deviceData,
  4187. deviceExtensionPort->DeviceExtensionHub->RootHubPdo,
  4188. 0);
  4189. #endif
  4190. }
  4191. deviceExtensionPort->PortPdoFlags |= PORTPDO_NEED_RESET;
  4192. }
  4193. }
  4194. USBH_SyncDisablePort(DeviceExtensionHub, p);
  4195. }
  4196. }
  4197. //
  4198. // Clean up buffers
  4199. //
  4200. if (DeviceExtensionHub->TransferBuffer) {
  4201. UsbhExFreePool(DeviceExtensionHub->TransferBuffer);
  4202. }
  4203. if (DeviceExtensionHub->HubDescriptor) {
  4204. UsbhExFreePool(DeviceExtensionHub->HubDescriptor);
  4205. }
  4206. if (DeviceExtensionHub->ConfigurationDescriptor) {
  4207. UsbhExFreePool(DeviceExtensionHub->ConfigurationDescriptor);
  4208. }
  4209. //
  4210. // NOTE: we do not free the per port data (PortData) because
  4211. // we will need it if we start up agian
  4212. //
  4213. DeviceExtensionHub->TransferBuffer = NULL;
  4214. DeviceExtensionHub->Irp = NULL;
  4215. DeviceExtensionHub->ConfigurationDescriptor =
  4216. (PVOID) DeviceExtensionHub->HubDescriptor = NULL;
  4217. DeviceExtensionHub->HubFlags &= ~HUBFLAG_NEED_CLEANUP;
  4218. return;
  4219. }
  4220. NTSTATUS
  4221. USBH_FdoStopDevice(
  4222. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  4223. IN PIRP Irp
  4224. )
  4225. /* ++
  4226. *
  4227. * Description:
  4228. *
  4229. * This routine is called by PnP via (IRP_MJ_PNP, IRP_MN_STOP_DEVICE).
  4230. *
  4231. * Argument:
  4232. *
  4233. * DeviceExtensionHub -
  4234. *
  4235. * Return:
  4236. *
  4237. * STATUS_SUCCESS
  4238. *
  4239. * -- */
  4240. {
  4241. NTSTATUS ntStatus;
  4242. PAGED_CODE();
  4243. USBH_KdPrint((2,"'FdoStopDevice Fdo extension %x\n", DeviceExtensionHub));
  4244. LOGENTRY(LOG_PNP, "hSTP", DeviceExtensionHub, DeviceExtensionHub->HubFlags, 0);
  4245. // walk thru our list of PDOs and verify that stop was passed down
  4246. // for each one
  4247. {
  4248. PPORT_DATA portData;
  4249. USHORT nextPortNumber;
  4250. USHORT numberOfPorts;
  4251. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  4252. portData = DeviceExtensionHub->PortData;
  4253. // NOTE:
  4254. // if we get a stop as a result of an error during stop
  4255. // then we may not have allocated portdata or a HubDescriptor
  4256. if (portData &&
  4257. DeviceExtensionHub->HubDescriptor) {
  4258. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  4259. for (nextPortNumber = 1;
  4260. nextPortNumber <= numberOfPorts;
  4261. nextPortNumber++, portData++) {
  4262. LOGENTRY(LOG_PNP, "chkS", DeviceExtensionHub,
  4263. nextPortNumber, portData->DeviceObject);
  4264. USBH_KdPrint((2,"'portdata %x, do %x\n", portData, portData->DeviceObject));
  4265. if (portData->DeviceObject) {
  4266. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  4267. //
  4268. // port is still started, print a warning
  4269. //
  4270. LOGENTRY(LOG_PNP, "chk1", DeviceExtensionHub,
  4271. nextPortNumber, deviceExtensionPort->PortPdoFlags);
  4272. if (deviceExtensionPort->PortPdoFlags & PORTPDO_STARTED) {
  4273. USBH_KdPrint((1,
  4274. "'client driver failed to pass the stop IRP\n"));
  4275. // remove it now
  4276. USBH_PdoStopDevice(deviceExtensionPort, Irp);
  4277. }
  4278. }
  4279. }
  4280. }
  4281. }
  4282. if (DeviceExtensionHub->HubFlags & HUBFLAG_NEED_CLEANUP) {
  4283. (VOID) UsbhFdoCleanup(DeviceExtensionHub);
  4284. }
  4285. //
  4286. // note that some hub structures are free at this point
  4287. //
  4288. if (DeviceExtensionHub->Configuration) {
  4289. (VOID) USBH_CloseConfiguration((PDEVICE_EXTENSION_FDO) DeviceExtensionHub);
  4290. DeviceExtensionHub->Configuration = NULL;
  4291. }
  4292. // note that we are stopped
  4293. DeviceExtensionHub->HubFlags |= HUBFLAG_HUB_STOPPED;
  4294. //
  4295. // And we need to pass this message on to lower level driver
  4296. //
  4297. ntStatus = USBH_PassIrp(Irp,
  4298. DeviceExtensionHub->TopOfStackDeviceObject);
  4299. return ntStatus;
  4300. }
  4301. VOID
  4302. USBH_FdoSurpriseRemoveDevice(
  4303. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  4304. IN PIRP Irp
  4305. )
  4306. /* ++
  4307. *
  4308. * Description:
  4309. *
  4310. * Handle surprise remove. If we get a surprise remove then PnP will know
  4311. * that all of our children are gone (same as a QBR) and remove them before
  4312. * removing us.
  4313. * Therefore we mark the devices as no longer present so that we process the
  4314. * remove properly when we get if for the PDO
  4315. *
  4316. * Argument:
  4317. *
  4318. * DeviceExtensionHub - This is a hub device extension. pIrp - the request
  4319. *
  4320. * Return:
  4321. *
  4322. * This call is non-falable, no status is returned
  4323. * -- */
  4324. {
  4325. PPORT_DATA pd;
  4326. USHORT portNumber;
  4327. USHORT numberOfPorts;
  4328. pd = DeviceExtensionHub->PortData;
  4329. if (pd &&
  4330. DeviceExtensionHub->HubDescriptor) {
  4331. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  4332. for (portNumber = 1;
  4333. portNumber <= numberOfPorts;
  4334. portNumber++, pd++) {
  4335. LOGENTRY(LOG_PNP, "chsX", DeviceExtensionHub,
  4336. portNumber, pd->DeviceObject);
  4337. USBH_KdPrint((2,"'portdata %x, do %x\n", pd, pd->DeviceObject));
  4338. if (pd->DeviceObject != NULL) {
  4339. LOGENTRY(LOG_PNP, "chs", DeviceExtensionHub,
  4340. portNumber, PDO_EXT(pd->DeviceObject));
  4341. // we no longer track this device, it is gone
  4342. //
  4343. PDO_EXT(pd->DeviceObject)->PortPdoFlags |= PORTPDO_DELETE_PENDING;
  4344. PDO_EXT(pd->DeviceObject)->PnPFlags &= ~PDO_PNPFLAG_DEVICE_PRESENT;
  4345. pd->DeviceObject = NULL;
  4346. pd->ConnectionStatus = NoDeviceConnected;
  4347. // note that we leave the device handle in the port extension
  4348. // this will be removed when the remove_device meassage is
  4349. // processed for the PDO
  4350. }
  4351. }
  4352. } else {
  4353. // I would like to know the circumstances where
  4354. // either of these are NULL
  4355. TEST_TRAP();
  4356. }
  4357. }
  4358. NTSTATUS
  4359. USBH_FdoRemoveDevice(
  4360. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  4361. IN PIRP Irp
  4362. )
  4363. /* ++
  4364. *
  4365. * Description:
  4366. *
  4367. * This routine is called by PnP via (IRP_MJ_PNP, IRP_MN_REMOVE_DEVICE).
  4368. *
  4369. * Argument:
  4370. *
  4371. * DeviceExtensionHub - This is a hub device extension. pIrp - the request
  4372. *
  4373. * Return:
  4374. *
  4375. * STATUS_SUCCESS
  4376. *
  4377. * -- */
  4378. {
  4379. PDEVICE_OBJECT deviceObject;
  4380. NTSTATUS ntStatus = STATUS_SUCCESS;
  4381. PAGED_CODE();
  4382. deviceObject = DeviceExtensionHub->FunctionalDeviceObject;
  4383. USBH_KdPrint((2,"'FdoRemoveDevice Fdo %x\n", deviceObject));
  4384. LOGENTRY(LOG_PNP, "hREM", DeviceExtensionHub, DeviceExtensionHub->HubFlags, 0);
  4385. // walk thru our list of PDOs and verify that remove was passed down
  4386. // for each one
  4387. {
  4388. PPORT_DATA portData;
  4389. USHORT nextPortNumber;
  4390. USHORT numberOfPorts;
  4391. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  4392. portData = DeviceExtensionHub->PortData;
  4393. //
  4394. // hub descriptor will be null if the hub is already stopped
  4395. //
  4396. if (portData &&
  4397. DeviceExtensionHub->HubDescriptor) {
  4398. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  4399. for (nextPortNumber = 1;
  4400. nextPortNumber <= numberOfPorts;
  4401. nextPortNumber++, portData++) {
  4402. LOGENTRY(LOG_PNP, "chkX", DeviceExtensionHub,
  4403. nextPortNumber, portData->DeviceObject);
  4404. USBH_KdPrint((2,"'portdata %x, do %x\n", portData, portData->DeviceObject));
  4405. if (portData->DeviceObject) {
  4406. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  4407. //
  4408. // port is still started, print a warning
  4409. //
  4410. // vinma, jdunn:
  4411. // there is one legitimate case for this code being hit.
  4412. // If the hub FDO is being removed the the child devices are
  4413. // still present (like during setup). The child PDOs may or may
  4414. // not have been sent a remove by PNP.
  4415. //
  4416. // In the case of setup PnP sends the remove to the children
  4417. // then to the FDO.
  4418. //
  4419. // In the case of a surprise remove for the hub the child PDOs
  4420. // are marked NOT 'PDO_PNPFLAG_DEVICE_PRESENT' when the
  4421. // IRP_MN_SURPRISE_REMOVE is handled.
  4422. //
  4423. LOGENTRY(LOG_PNP, "chk2", DeviceExtensionHub,
  4424. nextPortNumber, deviceExtensionPort->PortPdoFlags);
  4425. // device is considered gone
  4426. portData->DeviceObject = NULL;
  4427. portData->ConnectionStatus = NoDeviceConnected;
  4428. // this will cause the PDO to be deleted
  4429. deviceExtensionPort->PnPFlags &= ~PDO_PNPFLAG_DEVICE_PRESENT;
  4430. // if the parent receives a remove IRP,
  4431. // it subsequently deletes its children.
  4432. USBH_PdoRemoveDevice(deviceExtensionPort,
  4433. DeviceExtensionHub,
  4434. Irp);
  4435. }
  4436. }
  4437. }
  4438. }
  4439. //
  4440. // see if we need cleanup
  4441. //
  4442. if (DeviceExtensionHub->HubFlags & HUBFLAG_NEED_CLEANUP) {
  4443. UsbhFdoCleanup(DeviceExtensionHub);
  4444. }
  4445. //
  4446. // free the per port data now
  4447. //
  4448. if (DeviceExtensionHub->PortData) {
  4449. UsbhExFreePool(DeviceExtensionHub->PortData);
  4450. DeviceExtensionHub->PortData = NULL;
  4451. }
  4452. #ifdef WMI_SUPPORT
  4453. // de-register with WMI
  4454. IoWMIRegistrationControl(deviceObject,
  4455. WMIREG_ACTION_DEREGISTER);
  4456. #endif
  4457. //
  4458. // And we need to pass this message on to lower level driver
  4459. //
  4460. ntStatus = USBH_PassIrp(Irp, DeviceExtensionHub->TopOfStackDeviceObject);
  4461. //
  4462. // Detach FDO from PDO
  4463. //
  4464. IoDetachDevice(DeviceExtensionHub->TopOfStackDeviceObject);
  4465. // delete FDO
  4466. LOGENTRY(LOG_PNP, "hXXX", DeviceExtensionHub, 0, 0);
  4467. IoDeleteDevice(deviceObject);
  4468. return ntStatus;
  4469. }
  4470. BOOLEAN
  4471. USBH_DeviceIs2xDualMode(
  4472. IN PDEVICE_EXTENSION_PORT DeviceExtensionPort
  4473. )
  4474. /* ++
  4475. *
  4476. * Description:
  4477. *
  4478. * This function determines if the device is a 2.x compliant dual-mode device.
  4479. *
  4480. * Arguments:
  4481. *
  4482. * DeviceExtensionPort
  4483. *
  4484. * Return:
  4485. *
  4486. * BOOLEAN indicating whether the given device is a 2.x compliant dual-mode
  4487. * device or not.
  4488. *
  4489. * -- */
  4490. {
  4491. USB_DEVICE_QUALIFIER_DESCRIPTOR DeviceQualifierDescriptor;
  4492. NTSTATUS ntStatus;
  4493. BOOLEAN bDeviceIs2xDualMode = FALSE;
  4494. if (DeviceExtensionPort->DeviceDescriptor.bcdUSB >= 0x0200) {
  4495. ntStatus = USBH_GetDeviceQualifierDescriptor(
  4496. DeviceExtensionPort->PortPhysicalDeviceObject,
  4497. &DeviceQualifierDescriptor);
  4498. if (NT_SUCCESS(ntStatus) &&
  4499. DeviceQualifierDescriptor.bcdUSB >= 0x0200) {
  4500. bDeviceIs2xDualMode = TRUE;
  4501. }
  4502. }
  4503. return bDeviceIs2xDualMode;
  4504. }
  4505. PDEVICE_EXTENSION_HUB
  4506. USBH_GetRootHubDevExt(
  4507. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  4508. )
  4509. /* ++
  4510. *
  4511. * Description:
  4512. *
  4513. * This function gets the DevExt for the RootHub upstream of the given
  4514. * DeviceExtensionHub
  4515. *
  4516. * Arguments:
  4517. *
  4518. * DeviceExtensionHub
  4519. *
  4520. * Return:
  4521. *
  4522. * DeviceExtensionHub for the RootHub FDO.
  4523. *
  4524. * -- */
  4525. {
  4526. PDEVICE_OBJECT rootHubPdo, rootHubFdo;
  4527. PDEVICE_EXTENSION_HUB rootHubDevExt;
  4528. PDRIVER_OBJECT hubDriver;
  4529. hubDriver = DeviceExtensionHub->FunctionalDeviceObject->DriverObject;
  4530. if (IS_ROOT_HUB(DeviceExtensionHub)) {
  4531. rootHubDevExt = DeviceExtensionHub;
  4532. } else {
  4533. rootHubPdo = DeviceExtensionHub->RootHubPdo;
  4534. do {
  4535. rootHubFdo = rootHubPdo->AttachedDevice;
  4536. rootHubPdo = rootHubFdo;
  4537. } while (rootHubFdo->DriverObject != hubDriver);
  4538. rootHubDevExt = rootHubFdo->DeviceExtension;
  4539. }
  4540. USBH_ASSERT(rootHubDevExt &&
  4541. rootHubDevExt->ExtensionType == EXTENSION_TYPE_HUB);
  4542. return rootHubDevExt;
  4543. }
  4544. NTSTATUS
  4545. USBH_FdoQueryBusRelations(
  4546. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  4547. IN PIRP Irp
  4548. )
  4549. /* ++
  4550. *
  4551. * Description:
  4552. *
  4553. * This function responds to Bus_Reference_Next_Device, Bus_Query_Bus_Check,
  4554. * //Bus_Query_Id: Bus_Id, HardwareIDs, CompatibleIDs and InstanceID.
  4555. *
  4556. * Arguments:
  4557. *
  4558. * DeviceExtensionHub - should be the FDO we created for ourselves pIrp - the
  4559. * Irp
  4560. *
  4561. * Return:
  4562. *
  4563. * NtStatus
  4564. *
  4565. * -- */
  4566. {
  4567. PIO_STACK_LOCATION ioStack;
  4568. PPORT_DATA portData;
  4569. USHORT nextPortNumber;
  4570. USHORT numberOfPorts;
  4571. NTSTATUS ntStatus = STATUS_SUCCESS;
  4572. //BOOLEAN IsLowSpeed;
  4573. USHORT portStatus;
  4574. PDEVICE_RELATIONS deviceRelations = NULL;
  4575. PDEVICE_OBJECT deviceObject;
  4576. PDEVICE_EXTENSION_PORT deviceExtensionPort;
  4577. PWCHAR sernumbuf;
  4578. #ifdef EARLY_RESOURCE_RELEASE
  4579. PVOID deviceData;
  4580. #endif
  4581. PAGED_CODE();
  4582. USBH_KdPrint((1, "'Query Bus Relations (HUB) %x\n",
  4583. DeviceExtensionHub->PhysicalDeviceObject));
  4584. //
  4585. // Get a pointer to the current location in the Irp. This is where
  4586. // the function codes and parameters are located.
  4587. //
  4588. ioStack = IoGetCurrentIrpStackLocation(Irp);
  4589. USBH_KdPrint((2,"'FdoQueryBusRelations %x\n", ioStack->Parameters.QueryDeviceRelations.Type));
  4590. LOGENTRY(LOG_PNP, "QBR+", DeviceExtensionHub, 0, 0);
  4591. USBH_ASSERT(ioStack->Parameters.QueryDeviceRelations.Type == BusRelations);
  4592. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_NEED_CLEANUP)) {
  4593. // Hub device has not been started yet. Fail the IRP.
  4594. UsbhWarning(NULL,
  4595. "Hub device not started in FdoQueryBusRelations\n",
  4596. FALSE);
  4597. ntStatus = STATUS_INVALID_DEVICE_STATE;
  4598. goto USBH_FdoQueryBusRelations_Done2;
  4599. }
  4600. if (!DeviceExtensionHub->HubDescriptor) {
  4601. // Sometimes HubDescriptor is NULL when running Test's
  4602. // "Rebalance" test.
  4603. UsbhWarning(NULL,
  4604. "NULL HubDescriptor in FdoQueryBusRelations\n",
  4605. FALSE);
  4606. ntStatus = STATUS_UNSUCCESSFUL;
  4607. goto USBH_FdoQueryBusRelations_Done2;
  4608. }
  4609. USBH_KdPrint((2,"'FdoQueryBusRelations enumerate device\n"));
  4610. #ifdef NEW_START
  4611. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_OK_TO_ENUMERATE)) {
  4612. USBH_KdPrint((1,"'Defer enumeration\n"));
  4613. ntStatus = STATUS_SUCCESS;
  4614. goto USBH_FdoQueryBusRelations_Done2;
  4615. }
  4616. #endif
  4617. //
  4618. // It should be Function device object.
  4619. //
  4620. USBH_ASSERT(EXTENSION_TYPE_HUB == DeviceExtensionHub->ExtensionType);
  4621. // Sometimes during rebalance we will receive a QBR for a hub while one
  4622. // of the devices attached to hub is being restored. This will cause us to
  4623. // toss the PDO for that port because GetPortStatus for that port will show
  4624. // that there is no device connected. So, we synchronize with ResetDevice
  4625. // here.
  4626. USBH_KdPrint((2,"'***WAIT reset device mutex %x\n", DeviceExtensionHub));
  4627. USBH_INC_PENDING_IO_COUNT(DeviceExtensionHub);
  4628. KeWaitForSingleObject(&DeviceExtensionHub->ResetDeviceMutex,
  4629. Executive,
  4630. KernelMode,
  4631. FALSE,
  4632. NULL);
  4633. USBH_KdPrint((2,"'***WAIT reset device mutex done %x\n", DeviceExtensionHub));
  4634. numberOfPorts = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  4635. //
  4636. // Must use ExAllocatePool directly here because the OS
  4637. // will free the buffer
  4638. //
  4639. deviceRelations = ExAllocatePoolWithTag(
  4640. PagedPool, sizeof(*deviceRelations) + (numberOfPorts - 1) *
  4641. sizeof(PDEVICE_OBJECT), USBHUB_HEAP_TAG);
  4642. if (deviceRelations == NULL) {
  4643. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  4644. goto USBH_FdoQueryBusRelations_Done;
  4645. }
  4646. USBH_FdoQueryBusRelations_Start:
  4647. deviceRelations->Count = 0;
  4648. if (DeviceExtensionHub->HubFlags & HUBFLAG_HUB_HAS_LOST_BRAINS) {
  4649. // If we are trying to recover from ESD failure, then just tell PnP
  4650. // that there are no devices.
  4651. USBH_KdPrint((1,"'FdoQueryBusRelations: ESD recovery, returning no devices\n"));
  4652. ntStatus = STATUS_SUCCESS;
  4653. goto USBH_FdoQueryBusRelations_Done;
  4654. }
  4655. // Allow selective suspend once again if we were suppressing it waiting
  4656. // for post-ESD enumeration to occur.
  4657. DeviceExtensionHub->HubFlags &= ~HUBFLAG_POST_ESD_ENUM_PENDING;
  4658. //
  4659. // This is the first call for enumeration
  4660. //
  4661. //
  4662. // Find a ready device on our ports
  4663. //
  4664. portData = DeviceExtensionHub->PortData;
  4665. for (nextPortNumber = 1;
  4666. nextPortNumber <= numberOfPorts;
  4667. nextPortNumber++, portData++) {
  4668. //
  4669. // This query is redundant since we go here due to a change
  4670. // indication from the hub, however since we check all
  4671. // ports it will allow us to process another change may occur after
  4672. // the first one but before we get to this routine.
  4673. DBG_ONLY(USBH_ShowPortState( nextPortNumber,
  4674. &portData->PortState));
  4675. //
  4676. // don't bother to query the hub if it has failed
  4677. //
  4678. if (!(DeviceExtensionHub->HubFlags & HUBFLAG_HUB_FAILURE)) {
  4679. ntStatus = USBH_SyncGetPortStatus(DeviceExtensionHub,
  4680. nextPortNumber,
  4681. (PUCHAR) &portData->PortState,
  4682. sizeof(portData->PortState));
  4683. LOGENTRY(LOG_PNP, "nwPS", nextPortNumber,
  4684. portData->PortState.PortStatus,
  4685. portData->PortState.PortChange);
  4686. if (NT_SUCCESS(ntStatus)) {
  4687. //
  4688. // mark the port status as connected if we show
  4689. // overcurrent on this port, this will prevent us
  4690. // from tossing the PDO.
  4691. // Since the port is powered off we can't really
  4692. // know if anything is connected.
  4693. //
  4694. if (portData->DeviceObject) {
  4695. deviceExtensionPort =
  4696. portData->DeviceObject->DeviceExtension;
  4697. if (deviceExtensionPort->PortPdoFlags &
  4698. PORTPDO_OVERCURRENT) {
  4699. LOGENTRY(LOG_PNP, "mOVR", deviceExtensionPort, 0, 0);
  4700. portData->PortState.PortStatus |= PORT_STATUS_CONNECT;
  4701. } else if (!(deviceExtensionPort->PortPdoFlags &
  4702. PORTPDO_DELETE_PENDING)) {
  4703. // We now handle resetting ConnectionStatus in
  4704. // USBH_ResetPortOvercurrent.
  4705. // portData->ConnectionStatus = DeviceConnected;
  4706. USBH_ASSERT(portData->ConnectionStatus != NoDeviceConnected);
  4707. }
  4708. }
  4709. } else {
  4710. //
  4711. // NOTE (Doron Holan, 12/21/00):
  4712. // Setting the failure bit here will mean that ntStatus will not
  4713. // be touched again until the loop has exited and this function
  4714. // will complete this request with an error.
  4715. //
  4716. // Perhaps it would be more clear if we broke out of the loop
  4717. // here instead of starting over.
  4718. //
  4719. USBH_KdPrint((2,"'SyncGetPortStatus failed %x\n", ntStatus));
  4720. HUB_FAILURE(DeviceExtensionHub);
  4721. goto USBH_FdoQueryBusRelations_Start;
  4722. }
  4723. DBG_ONLY(USBH_ShowPortState( nextPortNumber,
  4724. &portData->PortState));
  4725. }
  4726. //
  4727. // do we have a device on this port?
  4728. //
  4729. if (DeviceExtensionHub->HubFlags & HUBFLAG_HUB_FAILURE) {
  4730. // if the hub has failed just return what we know about
  4731. deviceObject = portData->DeviceObject;
  4732. } else if (portData->PortState.PortStatus & PORT_STATUS_CONNECT) {
  4733. // Yes,
  4734. // did we already know about this device?
  4735. //
  4736. // check to see if the pdo is an orphan, if so toss the PDO
  4737. //
  4738. deviceObject = portData->DeviceObject;
  4739. if (portData->DeviceObject) {
  4740. // Yes,
  4741. // return the old PDO
  4742. deviceObject = portData->DeviceObject;
  4743. ObReferenceObject(deviceObject);
  4744. deviceObject->Flags |= DO_POWER_PAGABLE;
  4745. deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
  4746. deviceRelations->Objects[deviceRelations->Count] = deviceObject;
  4747. deviceRelations->Count++;
  4748. deviceExtensionPort = deviceObject->DeviceExtension;
  4749. deviceExtensionPort->PortPdoFlags &= ~PORTPDO_USB_SUSPEND;
  4750. LOGENTRY(LOG_PNP, "PDO1", DeviceExtensionHub, deviceObject
  4751. , deviceRelations->Count);
  4752. USBH_KdPrint((2,"'DoBusExtension Enum Return old device on port %x PDO=%x\n", \
  4753. nextPortNumber, portData->DeviceObject));
  4754. } else {
  4755. NTSTATUS status;
  4756. // No
  4757. // This means we have a new device on the bus
  4758. //
  4759. // wait 100ms for port device power to stablize before
  4760. // we assert Reset.
  4761. //
  4762. UsbhWait(100);
  4763. // USB 1.1 make sure we get speed of device after reset
  4764. status = USBH_SyncResetPort(DeviceExtensionHub, nextPortNumber);
  4765. // failure of reset is normal on USB 2.0 so we must ignore it.
  4766. if ((DeviceExtensionHub->HubFlags & HUBFLAG_USB20_HUB) &&
  4767. !NT_SUCCESS(status)) {
  4768. portData->DeviceObject = NULL;
  4769. portData->ConnectionStatus = NoDeviceConnected;
  4770. continue;
  4771. }
  4772. if (NT_SUCCESS(status)) {
  4773. // get the speed of the device
  4774. status = USBH_SyncGetPortStatus(DeviceExtensionHub,
  4775. nextPortNumber,
  4776. (PUCHAR) &portData->PortState,
  4777. sizeof(portData->PortState));
  4778. LOGENTRY(LOG_PNP, "gps1", nextPortNumber,
  4779. portData->PortState.PortChange,
  4780. portData->PortState.PortStatus);
  4781. // createDevice now figures out if it is low speed
  4782. // IsLowSpeed = (portData->PortState.PortStatus &
  4783. // PORT_STATUS_LOW_SPEED) ? TRUE : FALSE;
  4784. portStatus = portData->PortState.PortStatus;
  4785. }
  4786. if (NT_SUCCESS(status)) {
  4787. ULONG count = 0;
  4788. // reset completed
  4789. //
  4790. // A successful return has the PortData->DeviceObject set
  4791. //
  4792. //
  4793. // we will make three attempts to enumerate this device
  4794. //
  4795. for(;;) {
  4796. status = USBH_CreateDevice(DeviceExtensionHub,
  4797. nextPortNumber,
  4798. portStatus,
  4799. count);
  4800. if (!NT_SUCCESS(status)) {
  4801. count++;
  4802. USBH_KdPrint((1,"'Enumeration Failed count = %d, %x\n",
  4803. count, status));
  4804. #if DBG
  4805. if (count == 1) {
  4806. UsbhWarning(NULL,
  4807. "USB device failed first enumeration attempt\n",
  4808. (BOOLEAN)((USBH_Debug_Trace_Level >= 3) ? TRUE : FALSE));
  4809. }
  4810. #endif
  4811. UsbhWait(500);
  4812. if (count >= USBH_MAX_ENUMERATION_ATTEMPTS) {
  4813. USBH_KdBreak(("Max tries exceeded\n"));
  4814. break;
  4815. }
  4816. if (portData->DeviceObject) {
  4817. //
  4818. // clean up the device object we created
  4819. //
  4820. IoDeleteDevice(portData->DeviceObject);
  4821. portData->DeviceObject = NULL;
  4822. portData->ConnectionStatus = NoDeviceConnected;
  4823. }
  4824. //
  4825. // enumeration failed, reset the port and try again
  4826. //
  4827. USBH_SyncResetPort(DeviceExtensionHub, nextPortNumber);
  4828. } else {
  4829. // enumeration success
  4830. // If this is a high-speed capable 2.x device
  4831. // connected to a legacy 1.x hub, then inform
  4832. // the UI.
  4833. if (portData->DeviceObject) {
  4834. deviceExtensionPort =
  4835. portData->DeviceObject->DeviceExtension;
  4836. if (!(deviceExtensionPort->PortPdoFlags &
  4837. PORTPDO_LOW_SPEED_DEVICE) &&
  4838. !(deviceExtensionPort->PortPdoFlags &
  4839. PORTPDO_HIGH_SPEED_DEVICE) &&
  4840. !(DeviceExtensionHub->HubFlags &
  4841. HUBFLAG_USB20_HUB)) {
  4842. // We have a device in full-speed mode
  4843. // connected to a 1.x hub. Determine if
  4844. // the device is high-speed capable.
  4845. if (USBH_DeviceIs2xDualMode(deviceExtensionPort)) {
  4846. deviceExtensionPort->PortPdoFlags |=
  4847. PORTPDO_USB20_DEVICE_IN_LEGACY_HUB;
  4848. USBH_KdPrint((1,"'USB 2.x dual-mode device connected to legacy hub (%x)\n", deviceExtensionPort));
  4849. // Generate a WMI event so UI can inform
  4850. // the user.
  4851. USBH_PdoEvent(DeviceExtensionHub,
  4852. nextPortNumber);
  4853. }
  4854. }
  4855. }
  4856. break;
  4857. }
  4858. }
  4859. } else {
  4860. // unable to reset the port
  4861. #if DBG
  4862. USBH_SyncGetPortStatus(DeviceExtensionHub,
  4863. nextPortNumber,
  4864. (PUCHAR) &portData->PortState,
  4865. sizeof(portData->PortState));
  4866. LOGENTRY(LOG_PNP, "gps2", nextPortNumber,
  4867. portData->PortState.PortChange,
  4868. portData->PortState.PortStatus);
  4869. #endif
  4870. // we will assume this is due to a jittery
  4871. // connection
  4872. USBH_KdPrint((0,"'Unable to reset port %d\n",
  4873. nextPortNumber));
  4874. }
  4875. if (NT_SUCCESS(status)) {
  4876. //
  4877. // A successful return from CreateDevice the
  4878. // PortData->DeviceObject
  4879. // set.
  4880. //
  4881. USBH_ASSERT(portData->DeviceObject != NULL);
  4882. deviceObject = portData->DeviceObject;
  4883. ObReferenceObject(deviceObject);
  4884. deviceRelations->Objects[deviceRelations->Count] = deviceObject;
  4885. deviceObject->Flags |= DO_POWER_PAGABLE;
  4886. deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
  4887. deviceRelations->Count++;
  4888. deviceExtensionPort = deviceObject->DeviceExtension;
  4889. portData->ConnectionStatus = DeviceConnected;
  4890. LOGENTRY(LOG_PNP, "PDO2", DeviceExtensionHub, deviceObject
  4891. , deviceRelations->Count);
  4892. USBH_KdPrint((2,"'DoBusExtension Enum Return device on port %x\n",
  4893. nextPortNumber));
  4894. } else {
  4895. USBH_KdBreak(("ResetPort or CreateDevice failed, disable port\n"));
  4896. UsbhWarning(NULL,
  4897. "Device Failed Enumeration\n",
  4898. FALSE);
  4899. portData->ConnectionStatus = DeviceFailedEnumeration;
  4900. // generate a WMI event so UI can inform the user
  4901. USBH_PdoEvent(DeviceExtensionHub, nextPortNumber);
  4902. // failed to initialize the device
  4903. // disable the port here.
  4904. status = USBH_SyncDisablePort(DeviceExtensionHub,
  4905. nextPortNumber);
  4906. if (NT_ERROR(status)) {
  4907. HUB_FAILURE(DeviceExtensionHub);
  4908. }
  4909. //
  4910. // return the deviceObject even for errors
  4911. // so that PnP knows there is something on the
  4912. // bus.
  4913. //
  4914. deviceObject = portData->DeviceObject;
  4915. //
  4916. // NOTE: we won't have a device object if we failed to reset
  4917. // the port
  4918. //
  4919. if (deviceObject) {
  4920. ObReferenceObject(deviceObject);
  4921. deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
  4922. deviceRelations->Objects[deviceRelations->Count] = deviceObject;
  4923. deviceRelations->Count++;
  4924. deviceExtensionPort = deviceObject->DeviceExtension;
  4925. LOGENTRY(LOG_PNP, "PDO3", DeviceExtensionHub, deviceObject
  4926. , deviceRelations->Count);
  4927. }
  4928. }
  4929. }
  4930. } else {
  4931. //
  4932. // No,
  4933. // There is no device on this port now
  4934. //
  4935. // if there was a device here mark the PDO as delete pending
  4936. if (portData->DeviceObject) {
  4937. deviceExtensionPort = portData->DeviceObject->DeviceExtension;
  4938. deviceExtensionPort->PortPdoFlags |= PORTPDO_DELETE_PENDING;
  4939. // pnp will no longer see this device as present
  4940. deviceExtensionPort->PnPFlags &= ~PDO_PNPFLAG_DEVICE_PRESENT;
  4941. // Prevent double free of SerialNumberBuffer in
  4942. // USBH_ProcessPortStateChange.
  4943. sernumbuf = InterlockedExchangePointer(
  4944. &deviceExtensionPort->SerialNumberBuffer,
  4945. NULL);
  4946. if (sernumbuf) {
  4947. UsbhExFreePool(sernumbuf);
  4948. }
  4949. #ifdef EARLY_RESOURCE_RELEASE
  4950. //
  4951. // Remove the device data now to free
  4952. // up the bus resources.
  4953. //
  4954. deviceData = InterlockedExchangePointer(
  4955. &deviceExtensionPort->DeviceData,
  4956. NULL);
  4957. if (deviceData) {
  4958. #ifdef USB2
  4959. USBD_RemoveDeviceEx(DeviceExtensionHub,
  4960. deviceData,
  4961. DeviceExtensionHub->RootHubPdo,
  4962. 0);
  4963. #else
  4964. USBD_RemoveDevice(deviceData,
  4965. DeviceExtensionHub->RootHubPdo,
  4966. 0);
  4967. #endif
  4968. USBH_SyncDisablePort(DeviceExtensionHub,
  4969. nextPortNumber);
  4970. }
  4971. #endif // EARLY_RESOURCE_RELEASE
  4972. }
  4973. // indicate no device
  4974. portData->DeviceObject = NULL;
  4975. portData->ConnectionStatus = NoDeviceConnected;
  4976. }
  4977. } /* for */
  4978. USBH_FdoQueryBusRelations_Done:
  4979. LOGENTRY(LOG_PNP, "QBR-", DeviceExtensionHub, 0, 0);
  4980. USBH_KdPrint((2,"'***RELEASE reset device mutex %x\n", DeviceExtensionHub));
  4981. KeReleaseSemaphore(&DeviceExtensionHub->ResetDeviceMutex,
  4982. LOW_REALTIME_PRIORITY,
  4983. 1,
  4984. FALSE);
  4985. USBH_DEC_PENDING_IO_COUNT(DeviceExtensionHub);
  4986. USBH_FdoQueryBusRelations_Done2:
  4987. Irp->IoStatus.Status = ntStatus;
  4988. if (NT_SUCCESS(ntStatus)) {
  4989. USHORT p, n=0, c=0;
  4990. PPORT_DATA pd;
  4991. // we may return NULL relations on success
  4992. if (deviceRelations != NULL &&
  4993. deviceRelations->Count) {
  4994. USBH_KdPrint((1, "'Query Bus Relations (HUB) %x passed on\n",
  4995. DeviceExtensionHub->PhysicalDeviceObject));
  4996. // we have crafted device relations, set the PnP flags
  4997. // to indicate that PnP now knows about these PDOs
  4998. if (DeviceExtensionHub->HubDescriptor) {
  4999. n = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  5000. }
  5001. pd = DeviceExtensionHub->PortData;
  5002. for (p = 1;
  5003. pd && p <= n;
  5004. p++, pd++) {
  5005. if (pd->DeviceObject) {
  5006. PDO_EXT(pd->DeviceObject)->PnPFlags |= PDO_PNPFLAG_DEVICE_PRESENT;
  5007. c++;
  5008. }
  5009. }
  5010. // we should be reporing all PDOs
  5011. USBH_ASSERT(c == deviceRelations->Count);
  5012. } else {
  5013. // retrning NULL relations or zero count, any PDO previously reported will
  5014. // be lost -- PnP will consider them removed
  5015. USBH_KdPrint((1, "'Query Bus Relations (HUB) %x passed on (NULL)\n",
  5016. DeviceExtensionHub->PhysicalDeviceObject));
  5017. pd = DeviceExtensionHub->PortData;
  5018. for (p = 1;
  5019. pd && p <= n;
  5020. p++, pd++) {
  5021. if (pd->DeviceObject) {
  5022. TEST_TRAP();
  5023. PDO_EXT(pd->DeviceObject)->PnPFlags &= ~PDO_PNPFLAG_DEVICE_PRESENT;
  5024. }
  5025. }
  5026. }
  5027. Irp->IoStatus.Information=(ULONG_PTR) deviceRelations;
  5028. // flush the deleted PDO list, as soon as we return no
  5029. // error PNP will realize these are gone.
  5030. {
  5031. PDEVICE_EXTENSION_PORT dePort;
  5032. PLIST_ENTRY listEntry;
  5033. while (!IsListEmpty(&DeviceExtensionHub->DeletePdoList)) {
  5034. listEntry = RemoveHeadList(&DeviceExtensionHub->DeletePdoList);
  5035. dePort = CONTAINING_RECORD(listEntry, DEVICE_EXTENSION_PORT,
  5036. DeletePdoLink);
  5037. dePort->PnPFlags &= ~PDO_PNPFLAG_DEVICE_PRESENT;
  5038. }
  5039. }
  5040. ntStatus = USBH_PassIrp(Irp,
  5041. DeviceExtensionHub->TopOfStackDeviceObject);
  5042. } else {
  5043. // returning error and no relations
  5044. // PnP will interpret an error with no relations as a NOOP
  5045. // all devices will still be present
  5046. USHORT p, n=0;
  5047. PPORT_DATA pd;
  5048. DEVICE_EXTENSION_PORT portDevExt;
  5049. // we are reporting that everything is gone
  5050. // mark the PDOs as gone now.
  5051. if (DeviceExtensionHub->HubDescriptor) {
  5052. n = DeviceExtensionHub->HubDescriptor->bNumberOfPorts;
  5053. }
  5054. Irp->IoStatus.Information=0;
  5055. if (deviceRelations != NULL) {
  5056. ExFreePool(deviceRelations);
  5057. deviceRelations = NULL;
  5058. }
  5059. USBH_CompleteIrp(Irp, ntStatus);
  5060. }
  5061. return ntStatus;
  5062. }
  5063. NTSTATUS
  5064. USBH_FdoPnP(
  5065. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  5066. IN PIRP Irp,
  5067. IN UCHAR MinorFunction)
  5068. /* ++
  5069. *
  5070. * Description:
  5071. *
  5072. * This function responds to IoControl PnPPower for the FDO. This function is
  5073. * synchronous.
  5074. *
  5075. * Arguments:
  5076. *
  5077. * DeviceExtensionHub - the FDO extension pIrp - the request packet
  5078. * MinorFunction - the minor function of the PnP Power request.
  5079. *
  5080. * Return:
  5081. *
  5082. * NTSTATUS
  5083. *
  5084. * -- */
  5085. {
  5086. NTSTATUS ntStatus;
  5087. PDEVICE_OBJECT deviceObject;
  5088. PIO_STACK_LOCATION irpStack;
  5089. BOOLEAN bDoCheckHubIdle = FALSE;
  5090. irpStack = IoGetCurrentIrpStackLocation(Irp);
  5091. deviceObject = DeviceExtensionHub->FunctionalDeviceObject;
  5092. USBH_KdPrint((2,"'PnP Power Fdo %x minor %x\n", deviceObject, MinorFunction));
  5093. // If this hub is currently Selective Suspended, then we need to
  5094. // power up the hub first before sending any PnP requests along to it.
  5095. // Make sure hub has been started, though.
  5096. // Actually, in the case where a hub has been started, stopped, and now
  5097. // restarted, we want to power up the parent hub to handle the restart.
  5098. // Actually, we really don't need to resume for some PnP IRPS. Handle
  5099. // those special cases here.
  5100. if (DeviceExtensionHub->CurrentPowerState != PowerDeviceD0 &&
  5101. (DeviceExtensionHub->HubFlags &
  5102. (HUBFLAG_NEED_CLEANUP | HUBFLAG_HUB_STOPPED)) &&
  5103. !(MinorFunction == IRP_MN_QUERY_DEVICE_RELATIONS &&
  5104. irpStack->Parameters.QueryDeviceRelations.Type ==
  5105. TargetDeviceRelation)) {
  5106. bDoCheckHubIdle = TRUE;
  5107. USBH_HubSetD0(DeviceExtensionHub);
  5108. }
  5109. switch (MinorFunction) {
  5110. case IRP_MN_START_DEVICE:
  5111. USBH_KdPrint((2,"'IRP_MN_START_DEVICE Fdo %x\n", deviceObject));
  5112. bDoCheckHubIdle = FALSE;
  5113. Irp->IoStatus.Status = STATUS_SUCCESS;
  5114. ntStatus = USBH_FdoStartDevice(DeviceExtensionHub, Irp);
  5115. break;
  5116. case IRP_MN_STOP_DEVICE:
  5117. USBH_KdPrint((2,"'IRP_MN_STOP_DEVICE Fdo %x", deviceObject));
  5118. bDoCheckHubIdle = FALSE;
  5119. Irp->IoStatus.Status = STATUS_SUCCESS;
  5120. ntStatus = USBH_FdoStopDevice(DeviceExtensionHub, Irp);
  5121. break;
  5122. case IRP_MN_REMOVE_DEVICE:
  5123. USBH_KdPrint((2,"'IRP_MN_REMOVE_DEVICE Fdo %x\n", deviceObject));
  5124. bDoCheckHubIdle = FALSE;
  5125. DeviceExtensionHub->HubFlags |= HUBFLAG_HUB_GONE;
  5126. Irp->IoStatus.Status = STATUS_SUCCESS;
  5127. ntStatus = USBH_FdoRemoveDevice(DeviceExtensionHub, Irp);
  5128. break;
  5129. case IRP_MN_QUERY_DEVICE_RELATIONS:
  5130. switch (irpStack->Parameters.QueryDeviceRelations.Type) {
  5131. case BusRelations:
  5132. bDoCheckHubIdle = TRUE;
  5133. ASSERT(!( DeviceExtensionHub->HubFlags & HUBFLAG_HUB_BUSY));
  5134. DeviceExtensionHub->HubFlags |= HUBFLAG_HUB_BUSY;
  5135. ntStatus = USBH_FdoQueryBusRelations(DeviceExtensionHub, Irp);
  5136. DeviceExtensionHub->HubFlags &= ~HUBFLAG_HUB_BUSY;
  5137. break;
  5138. case TargetDeviceRelation:
  5139. //
  5140. // this one gets passed on
  5141. //
  5142. USBH_KdPrint((1, "'Query Relations, TargetDeviceRelation(HUB) %x\n",
  5143. DeviceExtensionHub->PhysicalDeviceObject));
  5144. ntStatus = USBH_PassIrp(Irp,
  5145. DeviceExtensionHub->TopOfStackDeviceObject);
  5146. break;
  5147. default:
  5148. USBH_KdPrint((1, "'Query Relations ? (HUB) %x complete\n",
  5149. DeviceExtensionHub->PhysicalDeviceObject));
  5150. ntStatus = USBH_PassIrp(Irp,
  5151. DeviceExtensionHub->TopOfStackDeviceObject);
  5152. }
  5153. break;
  5154. case IRP_MN_QUERY_CAPABILITIES:
  5155. USBH_KdPrint((2,"'IRP_MN_QUERY_CAPABILITIES on fdo %x %x\n",
  5156. deviceObject, MinorFunction));
  5157. IoCopyCurrentIrpStackLocationToNext(Irp);
  5158. IoSetCompletionRoutine(Irp, // Irp
  5159. USBH_QueryCapsComplete,
  5160. DeviceExtensionHub, // context
  5161. TRUE, // invoke on success
  5162. FALSE, // invoke on error
  5163. FALSE); // invoke on cancel
  5164. ntStatus = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject, Irp);
  5165. break;
  5166. //
  5167. // or pass the Irp down
  5168. //
  5169. case IRP_MN_QUERY_PNP_DEVICE_STATE:
  5170. if (DeviceExtensionHub->HubFlags & HUBFLAG_HUB_FAILURE) {
  5171. USBH_KdPrint((2,"'reporting failed hub\n"));
  5172. Irp->IoStatus.Information
  5173. |= PNP_DEVICE_FAILED;
  5174. LOGENTRY(LOG_PNP, "pnpS", DeviceExtensionHub,
  5175. Irp->IoStatus.Information, 0);
  5176. // note that (at least on memphis) this will result in
  5177. // a stop message being sent to the device.
  5178. }
  5179. ntStatus = USBH_PassIrp(Irp,
  5180. DeviceExtensionHub->TopOfStackDeviceObject);
  5181. break;
  5182. case IRP_MN_SURPRISE_REMOVAL:
  5183. USBH_KdPrint((1,"'IRP_MN_SURPRISE_REMOVAL on fdo %x\n", deviceObject));
  5184. USBH_FdoSurpriseRemoveDevice(DeviceExtensionHub,
  5185. Irp);
  5186. ntStatus = USBH_PassIrp(Irp,
  5187. DeviceExtensionHub->TopOfStackDeviceObject);
  5188. break;
  5189. case IRP_MN_QUERY_STOP_DEVICE:
  5190. case IRP_MN_CANCEL_STOP_DEVICE:
  5191. case IRP_MN_QUERY_REMOVE_DEVICE:
  5192. case IRP_MN_CANCEL_REMOVE_DEVICE:
  5193. case IRP_MN_DEVICE_USAGE_NOTIFICATION:
  5194. Irp->IoStatus.Status = STATUS_SUCCESS;
  5195. // Fall through
  5196. default:
  5197. USBH_KdPrint((2,"'PnP request on fdo %x %x\n",
  5198. deviceObject, MinorFunction));
  5199. ntStatus = USBH_PassIrp(Irp,
  5200. DeviceExtensionHub->TopOfStackDeviceObject);
  5201. break;
  5202. }
  5203. if (bDoCheckHubIdle) {
  5204. USBH_CheckHubIdle(DeviceExtensionHub);
  5205. }
  5206. DeviceExtensionHub->HubFlags &= ~HUBFLAG_CHILD_DELETES_PENDING;
  5207. USBH_KdPrint((2,"'FdoPnP exit %x\n", ntStatus));
  5208. return ntStatus;
  5209. }
  5210. NTSTATUS
  5211. USBH_DeferIrpCompletion(
  5212. IN PDEVICE_OBJECT DeviceObject,
  5213. IN PIRP Irp,
  5214. IN PVOID Context
  5215. )
  5216. /*++
  5217. Routine Description:
  5218. This routine is called when the port driver completes an IRP.
  5219. Arguments:
  5220. DeviceObject - Pointer to the device object for the class device.
  5221. Irp - Irp completed.
  5222. Context - Driver defined context.
  5223. Return Value:
  5224. The function value is the final status from the operation.
  5225. --*/
  5226. {
  5227. PKEVENT event = Context;
  5228. KeSetEvent(event,
  5229. 1,
  5230. FALSE);
  5231. return STATUS_MORE_PROCESSING_REQUIRED;
  5232. }
  5233. NTSTATUS
  5234. USBH_ResetInterruptPipe(
  5235. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  5236. )
  5237. /*++
  5238. Routine Description:
  5239. Reset The ubs interrupt pipe.
  5240. Arguments:
  5241. Return Value:
  5242. --*/
  5243. {
  5244. NTSTATUS ntStatus;
  5245. PURB urb;
  5246. USBH_KdPrint((2,"'Reset Pipe\n"));
  5247. urb = UsbhExAllocatePool(NonPagedPool,
  5248. sizeof(struct _URB_PIPE_REQUEST));
  5249. if (urb) {
  5250. urb->UrbHeader.Length = (USHORT) sizeof (struct _URB_PIPE_REQUEST);
  5251. urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
  5252. urb->UrbPipeRequest.PipeHandle =
  5253. DeviceExtensionHub->PipeInformation.PipeHandle;
  5254. ntStatus = USBH_FdoSyncSubmitUrb(DeviceExtensionHub->FunctionalDeviceObject,
  5255. urb);
  5256. UsbhExFreePool(urb);
  5257. } else {
  5258. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  5259. }
  5260. //
  5261. // if the reset pipe request is successful,
  5262. // reset our error counter
  5263. //
  5264. if (NT_SUCCESS(ntStatus)) {
  5265. DeviceExtensionHub->ErrorCount = 0;
  5266. LOGENTRY(LOG_PNP, "rZER", DeviceExtensionHub, ntStatus, 0);
  5267. }
  5268. LOGENTRY(LOG_PNP, "rPIP", DeviceExtensionHub, ntStatus,
  5269. DeviceExtensionHub->ErrorCount);
  5270. return ntStatus;
  5271. }
  5272. NTSTATUS
  5273. USBH_GetPortStatus(
  5274. IN IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  5275. IN PULONG PortStatus
  5276. )
  5277. /*++
  5278. Routine Description:
  5279. Passes a URB to the USBD class driver
  5280. Arguments:
  5281. DeviceExtension - pointer to the device extension for this instance of an USB camera
  5282. Urb - pointer to Urb request block
  5283. Return Value:
  5284. STATUS_SUCCESS if successful,
  5285. STATUS_UNSUCCESSFUL otherwise
  5286. --*/
  5287. {
  5288. NTSTATUS ntStatus, status = STATUS_SUCCESS;
  5289. PIRP irp;
  5290. KEVENT event;
  5291. IO_STATUS_BLOCK ioStatus;
  5292. PIO_STACK_LOCATION nextStack;
  5293. USBH_KdPrint((2,"'enter USBH_GetPortStatus\n"));
  5294. *PortStatus = 0;
  5295. //
  5296. // issue a synchronous request
  5297. //
  5298. KeInitializeEvent(&event, NotificationEvent, FALSE);
  5299. irp = IoBuildDeviceIoControlRequest(
  5300. IOCTL_INTERNAL_USB_GET_PORT_STATUS,
  5301. DeviceExtensionHub->TopOfStackDeviceObject,
  5302. NULL,
  5303. 0,
  5304. NULL,
  5305. 0,
  5306. TRUE, /* INTERNAL */
  5307. &event,
  5308. &ioStatus);
  5309. if (irp == NULL) {
  5310. return STATUS_INSUFFICIENT_RESOURCES;
  5311. }
  5312. //
  5313. // Call the class driver to perform the operation. If the returned status
  5314. // is PENDING, wait for the request to complete.
  5315. //
  5316. nextStack = IoGetNextIrpStackLocation(irp);
  5317. USBH_ASSERT(nextStack != NULL);
  5318. nextStack->Parameters.Others.Argument1 = PortStatus;
  5319. USBH_KdPrint((2,"'calling USBD port status api\n"));
  5320. ntStatus = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  5321. irp);
  5322. USBH_KdPrint((2,"'return from IoCallDriver USBD %x\n", ntStatus));
  5323. if (ntStatus == STATUS_PENDING) {
  5324. USBH_KdPrint((2, "'Wait for single object\n"));
  5325. status = KeWaitForSingleObject(
  5326. &event,
  5327. Suspended,
  5328. KernelMode,
  5329. FALSE,
  5330. NULL);
  5331. USBH_KdPrint((2,"'Wait for single object, returned %x\n", status));
  5332. } else {
  5333. ioStatus.Status = ntStatus;
  5334. }
  5335. USBH_KdPrint((2,"'Port status = %x\n", *PortStatus));
  5336. //
  5337. // USBD maps the error code for us
  5338. //
  5339. ntStatus = ioStatus.Status;
  5340. USBH_KdPrint((2,"'USBH_GetPortStatus (%x)\n", ntStatus));
  5341. return ntStatus;
  5342. }
  5343. NTSTATUS
  5344. USBH_EnableParentPort(
  5345. IN IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  5346. )
  5347. /*++
  5348. Routine Description:
  5349. Passes a URB to the USBD class driver
  5350. Arguments:
  5351. DeviceExtension - pointer to the device extension for this instance of an USB camera
  5352. Urb - pointer to Urb request block
  5353. Return Value:
  5354. STATUS_SUCCESS if successful,
  5355. STATUS_UNSUCCESSFUL otherwise
  5356. --*/
  5357. {
  5358. NTSTATUS ntStatus, status = STATUS_SUCCESS;
  5359. PIRP irp;
  5360. KEVENT event;
  5361. IO_STATUS_BLOCK ioStatus;
  5362. USBH_KdPrint((2,"'enter USBH_EnablePort\n"));
  5363. //
  5364. // issue a synchronous request
  5365. //
  5366. KeInitializeEvent(&event, NotificationEvent, FALSE);
  5367. irp = IoBuildDeviceIoControlRequest(
  5368. IOCTL_INTERNAL_USB_ENABLE_PORT,
  5369. DeviceExtensionHub->TopOfStackDeviceObject,
  5370. NULL,
  5371. 0,
  5372. NULL,
  5373. 0,
  5374. TRUE, /* INTERNAL */
  5375. &event,
  5376. &ioStatus);
  5377. if (irp == NULL) {
  5378. return STATUS_INSUFFICIENT_RESOURCES;
  5379. }
  5380. //
  5381. // Call the class driver to perform the operation. If the returned status
  5382. // is PENDING, wait for the request to complete.
  5383. //
  5384. USBH_KdPrint((2,"'calling USBD enable port api\n"));
  5385. ntStatus = IoCallDriver(DeviceExtensionHub->TopOfStackDeviceObject,
  5386. irp);
  5387. USBH_KdPrint((2,"'return from IoCallDriver USBD %x\n", ntStatus));
  5388. if (ntStatus == STATUS_PENDING) {
  5389. USBH_KdPrint((2, "'Wait for single object\n"));
  5390. status = KeWaitForSingleObject(
  5391. &event,
  5392. Suspended,
  5393. KernelMode,
  5394. FALSE,
  5395. NULL);
  5396. USBH_KdPrint((2,"'Wait for single object, returned %x\n", status));
  5397. } else {
  5398. ioStatus.Status = ntStatus;
  5399. }
  5400. //
  5401. // USBD maps the error code for us
  5402. //
  5403. ntStatus = ioStatus.Status;
  5404. LOGENTRY(LOG_PNP, "hEPP", DeviceExtensionHub, ntStatus, 0);
  5405. USBH_KdPrint((2,"'USBH_EnablePort (%x)\n", ntStatus));
  5406. return ntStatus;
  5407. }
  5408. NTSTATUS
  5409. USBH_ResetHub(
  5410. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub
  5411. )
  5412. /*++
  5413. Routine Description:
  5414. Reset The ubs interrupt pipe.
  5415. Arguments:
  5416. Return Value:
  5417. --*/
  5418. {
  5419. NTSTATUS ntStatus;
  5420. ULONG portStatus;
  5421. //
  5422. // Check the port state, if it is disabled we will need
  5423. // to re-enable it
  5424. //
  5425. ntStatus = USBH_GetPortStatus(DeviceExtensionHub, &portStatus);
  5426. if (NT_SUCCESS(ntStatus) &&
  5427. !(portStatus & USBD_PORT_ENABLED) &&
  5428. (portStatus & USBD_PORT_CONNECTED)) {
  5429. //
  5430. // port is disabled, attempt reset
  5431. //
  5432. LOGENTRY(LOG_PNP, "rEPP", DeviceExtensionHub, portStatus,
  5433. DeviceExtensionHub->ErrorCount);
  5434. USBH_EnableParentPort(DeviceExtensionHub);
  5435. }
  5436. //
  5437. // now attempt to reset the stalled pipe, this will clear the stall
  5438. // on the device as well.
  5439. //
  5440. if (NT_SUCCESS(ntStatus)) {
  5441. ntStatus = USBH_ResetInterruptPipe(DeviceExtensionHub);
  5442. }
  5443. //
  5444. // send the feature command to clear endpoint stall
  5445. //
  5446. return ntStatus;
  5447. }
  5448. #if 0
  5449. NTSTATUS
  5450. USBH_WriteRegistryKeyString (
  5451. IN HANDLE Handle,
  5452. IN PWCHAR KeyNameString,
  5453. IN ULONG KeyNameStringLength,
  5454. IN PVOID Data,
  5455. IN ULONG DataLength
  5456. )
  5457. /*++
  5458. Routine Description:
  5459. Arguments:
  5460. Return Value:
  5461. --*/
  5462. {
  5463. NTSTATUS ntStatus;
  5464. UNICODE_STRING keyName;
  5465. PAGED_CODE();
  5466. RtlInitUnicodeString(&keyName, KeyNameString);
  5467. ntStatus = ZwSetValueKey(Handle,
  5468. &keyName,
  5469. 0,
  5470. REG_SZ,
  5471. Data,
  5472. DataLength);
  5473. return ntStatus;
  5474. }
  5475. #endif
  5476. NTSTATUS
  5477. USBH_WriteRegistryKeyValue (
  5478. IN HANDLE Handle,
  5479. IN PWCHAR KeyNameString,
  5480. IN ULONG KeyNameStringLength,
  5481. IN ULONG Data
  5482. )
  5483. /*++
  5484. Routine Description:
  5485. Arguments:
  5486. Return Value:
  5487. --*/
  5488. {
  5489. NTSTATUS ntStatus;
  5490. UNICODE_STRING keyName;
  5491. PAGED_CODE();
  5492. RtlInitUnicodeString(&keyName, KeyNameString);
  5493. ntStatus = ZwSetValueKey(Handle,
  5494. &keyName,
  5495. 0,
  5496. REG_DWORD,
  5497. &Data,
  5498. sizeof(ULONG));
  5499. return ntStatus;
  5500. }
  5501. NTSTATUS
  5502. USBH_WriteFailReason(
  5503. IN PDEVICE_OBJECT PhysicalDeviceObject,
  5504. IN ULONG FailReason
  5505. )
  5506. /*++
  5507. Routine Description:
  5508. Reset The ubs interrupt pipe.
  5509. Arguments:
  5510. Return Value:
  5511. --*/
  5512. {
  5513. NTSTATUS ntStatus;
  5514. HANDLE handle;
  5515. WCHAR USBH_FailReasonKey[] = L"FailReasonID";
  5516. PAGED_CODE();
  5517. ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
  5518. PLUGPLAY_REGKEY_DEVICE,
  5519. STANDARD_RIGHTS_ALL,
  5520. &handle);
  5521. if (NT_SUCCESS(ntStatus)) {
  5522. USBH_WriteRegistryKeyValue(handle,
  5523. USBH_FailReasonKey,
  5524. sizeof(USBH_FailReasonKey),
  5525. FailReason);
  5526. ZwClose(handle);
  5527. }
  5528. return ntStatus;
  5529. }
  5530. #if 0 // NOT USED
  5531. NTSTATUS
  5532. USBH_WriteFailReasonString(
  5533. IN PDEVICE_OBJECT PhysicalDeviceObject,
  5534. IN PWCHAR FailReasonString,
  5535. IN ULONG FailReasonStringLength
  5536. )
  5537. /*++
  5538. Routine Description:
  5539. Reset The ubs interrupt pipe.
  5540. Arguments:
  5541. Return Value:
  5542. --*/
  5543. {
  5544. NTSTATUS ntStatus;
  5545. HANDLE handle;
  5546. WCHAR USBH_FailReasonKey[] = L"FailReasonString";
  5547. PAGED_CODE();
  5548. ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
  5549. PLUGPLAY_REGKEY_DEVICE,
  5550. STANDARD_RIGHTS_ALL,
  5551. &handle);
  5552. if (NT_SUCCESS(ntStatus)) {
  5553. USBH_WriteRegistryKeyString(handle,
  5554. USBH_FailReasonKey,
  5555. sizeof(USBH_FailReasonKey),
  5556. FailReasonString,
  5557. FailReasonStringLength);
  5558. ZwClose(handle);
  5559. }
  5560. return ntStatus;
  5561. }
  5562. #endif
  5563. NTSTATUS
  5564. USBH_InvalidatePortDeviceState(
  5565. IN PDEVICE_EXTENSION_HUB DeviceExtensionHub,
  5566. IN USB_CONNECTION_STATUS ConnectStatus,
  5567. IN USHORT PortNumber
  5568. )
  5569. /*++
  5570. Routine Description:
  5571. This function updates the connection status for a
  5572. port. It inavlidates the PDO if there is one, writes
  5573. a failreason to the registry and triggers a WMI event.
  5574. Invalidating the PDO should trigger a Q_PNP_DEVICE_STATE
  5575. Arguments:
  5576. Return Value:
  5577. --*/
  5578. {
  5579. NTSTATUS ntStatus = STATUS_SUCCESS;
  5580. ULONG failReason = 0;
  5581. PPORT_DATA portData;
  5582. USBH_ASSERT(DeviceExtensionHub != NULL);
  5583. portData = &DeviceExtensionHub->PortData[PortNumber-1];
  5584. portData->ConnectionStatus = ConnectStatus;
  5585. //
  5586. // figure out if we have a failreason we can write
  5587. // to the registry
  5588. //
  5589. switch(ConnectStatus) {
  5590. case DeviceConnected:
  5591. // this will reset the failreasonId
  5592. break;
  5593. case DeviceFailedEnumeration:
  5594. failReason = USBH_FAILREASON_ENUM_FAILED;
  5595. break;
  5596. case DeviceGeneralFailure:
  5597. failReason = USBH_FAILREASON_GEN_DEVICE_FAILURE;
  5598. break;
  5599. case DeviceCausedOvercurrent:
  5600. failReason = USBH_FAILREASON_PORT_OVERCURRENT;
  5601. break;
  5602. case DeviceNotEnoughPower:
  5603. failReason = USBH_FAILREASON_NOT_ENOUGH_POWER;
  5604. break;
  5605. default:
  5606. TEST_TRAP();
  5607. }
  5608. if (failReason) {
  5609. // this writes a code to the registry so that win98 devman
  5610. // can display an error message
  5611. if (portData->DeviceObject) {
  5612. USBH_WriteFailReason(portData->DeviceObject,
  5613. failReason);
  5614. }
  5615. }
  5616. // generate a WMI event so UI can inform the user
  5617. USBH_PdoEvent(DeviceExtensionHub, PortNumber);
  5618. //
  5619. // Invalidate the state of the PDO -- this should
  5620. // trigger a Q_PNP_DEVICE_STATE
  5621. //
  5622. if (portData->DeviceObject) {
  5623. IoInvalidateDeviceState(portData->DeviceObject);
  5624. }
  5625. return ntStatus;
  5626. }
  5627. // JD <New>
  5628. PDEVICE_EXTENSION_PORT
  5629. PdoExt(
  5630. PDEVICE_OBJECT DeviceObject
  5631. )
  5632. {
  5633. USBH_ASSERT(DeviceObject);
  5634. if (DeviceObject == NULL) {
  5635. return (PDEVICE_EXTENSION_PORT) -1;
  5636. } else {
  5637. return (PDEVICE_EXTENSION_PORT) DeviceObject->DeviceExtension;
  5638. }
  5639. }
  5640. #if 0
  5641. PPORT_DATA
  5642. USBH_PortDataDataFromPdo(
  5643. PDEVICE_EXTENSION_HUB HubDevExt,
  5644. PDEVICE_OBJECT Pdo
  5645. )
  5646. {
  5647. PPORT_DATA portData;
  5648. USBH_ASSERT(Pdo);
  5649. portData = &HubDevExt->PortData[PDO_EXT(Pdo)->PortNumber - 1];
  5650. return portData;
  5651. }
  5652. #endif