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

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