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.

1982 lines
63 KiB

  1. /*
  2. *************************************************************************
  3. * File: FUNCTION.C
  4. *
  5. * Module: USBCCGP.SYS
  6. * USB Common Class Generic Parent driver.
  7. *
  8. * Copyright (c) 1998 Microsoft Corporation
  9. *
  10. *
  11. * Author: ervinp
  12. *
  13. *************************************************************************
  14. */
  15. #include <wdm.h>
  16. #include <stdio.h>
  17. #include <usbdi.h>
  18. #include <usbdlib.h>
  19. #include <usbioctl.h>
  20. #include "usbccgp.h"
  21. #include "security.h"
  22. #include "debug.h"
  23. #ifdef ALLOC_PRAGMA
  24. #pragma alloc_text(PAGE, CreateStaticFunctionPDOs)
  25. #pragma alloc_text(PAGE, BuildCompatibleIDs)
  26. #pragma alloc_text(PAGE, QueryFunctionPdoID)
  27. #pragma alloc_text(PAGE, QueryFunctionDeviceRelations)
  28. #pragma alloc_text(PAGE, QueryFunctionCapabilities)
  29. #pragma alloc_text(PAGE, HandleFunctionPdoPower)
  30. #pragma alloc_text(PAGE, FreeFunctionPDOResources)
  31. #pragma alloc_text(PAGE, InstallExtPropDesc)
  32. #pragma alloc_text(PAGE, InstallExtPropDescSections)
  33. #endif
  34. /*
  35. * CreateStaticFunctionPDOs
  36. *
  37. *
  38. * Create a PDO for each function on the device.
  39. * Treat each interface as a function (with exceptions for audio).
  40. */
  41. NTSTATUS CreateStaticFunctionPDOs(PPARENT_FDO_EXT parentFdoExt)
  42. {
  43. NTSTATUS status;
  44. PUSB_CONFIGURATION_DESCRIPTOR configDesc;
  45. ULONG numFuncIfaces;
  46. ULONG i;
  47. PAGED_CODE();
  48. configDesc = parentFdoExt->selectedConfigDesc;
  49. ASSERT(configDesc);
  50. ASSERT(configDesc->bNumInterfaces > 0);
  51. ASSERT(!parentFdoExt->deviceRelations);
  52. ASSERT(parentFdoExt->interfaceList);
  53. /*
  54. * See if there is a Content Security Interface and if so initialize.
  55. * (There should be at most one CS interface).
  56. */
  57. for (i = 0; i < configDesc->bNumInterfaces; i++){
  58. UCHAR ifaceClass = parentFdoExt->interfaceList[i].InterfaceDescriptor->bInterfaceClass;
  59. if (ifaceClass == USB_DEVICE_CLASS_CONTENT_SECURITY){
  60. ULONG ifaceNum = parentFdoExt->interfaceList[i].InterfaceDescriptor->bInterfaceNumber;
  61. DBGVERBOSE(("Found CS interface #%d.", ifaceNum));
  62. ASSERT(!parentFdoExt->haveCSInterface);
  63. InitCSInfo(parentFdoExt, ifaceNum);
  64. ASSERT(parentFdoExt->haveCSInterface);
  65. }
  66. }
  67. /*
  68. * The configuration descriptor contains a series of interfaces,
  69. * which are grouped into some number of "functions".
  70. * Count the number of "function" interface groupings.
  71. */
  72. if (ISPTR(parentFdoExt->msExtConfigDesc))
  73. {
  74. parentFdoExt->numFunctions = parentFdoExt->msExtConfigDesc->Header.bCount;
  75. }
  76. else
  77. {
  78. for (i = 0; GetFunctionInterfaceListBase(parentFdoExt, i, &numFuncIfaces); i++);
  79. parentFdoExt->numFunctions = i;
  80. }
  81. ASSERT(parentFdoExt->numFunctions);
  82. DBGVERBOSE((" Device has %d interfaces and %d functions", (ULONG)configDesc->bNumInterfaces, parentFdoExt->numFunctions));
  83. /*
  84. * Allocate a PDO for each function
  85. */
  86. parentFdoExt->deviceRelations =
  87. ALLOCPOOL( NonPagedPool,
  88. sizeof(DEVICE_RELATIONS) + (parentFdoExt->numFunctions *
  89. sizeof(PDEVICE_OBJECT)));
  90. if (parentFdoExt->deviceRelations){
  91. for (i = 0; i < parentFdoExt->numFunctions; i++){
  92. PDEVICE_OBJECT functionPdo = NULL;
  93. status = IoCreateDevice(parentFdoExt->driverObj,
  94. sizeof(DEVEXT), // Device Extension size
  95. NULL, // device name
  96. FILE_DEVICE_UNKNOWN,
  97. FILE_AUTOGENERATED_DEVICE_NAME,// Device Chars
  98. FALSE,
  99. &functionPdo);
  100. if (NT_SUCCESS(status)){
  101. PDEVEXT devExt;
  102. PFUNCTION_PDO_EXT functionPdoExt;
  103. ASSERT(functionPdo);
  104. devExt = functionPdo->DeviceExtension;
  105. RtlZeroMemory(devExt, sizeof(DEVEXT));
  106. devExt->signature = USBCCGP_TAG;
  107. devExt->isParentFdo = FALSE;
  108. functionPdo->Flags |= DO_POWER_PAGABLE;
  109. functionPdoExt = &devExt->functionPdoExt;
  110. functionPdoExt->functionIndex = i;
  111. functionPdoExt->pdo = functionPdo;
  112. functionPdoExt->parentFdoExt = parentFdoExt;
  113. functionPdoExt->idleNotificationIrp = NULL;
  114. KeInitializeSpinLock(&functionPdoExt->functionPdoExtSpinLock);
  115. /*
  116. * The parent's config descriptor contains a list
  117. * of interface descriptors, sorted by function.
  118. * Get a pointer to the first interface descriptor
  119. * for this function.
  120. */
  121. if (ISPTR(parentFdoExt->msExtConfigDesc))
  122. {
  123. functionPdoExt->baseInterfaceNumber =
  124. parentFdoExt->msExtConfigDesc->Function[i].bFirstInterfaceNumber;
  125. functionPdoExt->numInterfaces =
  126. parentFdoExt->msExtConfigDesc->Function[i].bInterfaceCount;
  127. functionPdoExt->functionInterfaceList =
  128. &parentFdoExt->interfaceList[functionPdoExt->baseInterfaceNumber];
  129. }
  130. else
  131. {
  132. functionPdoExt->functionInterfaceList = GetFunctionInterfaceListBase(parentFdoExt, i, &numFuncIfaces);
  133. functionPdoExt->numInterfaces = numFuncIfaces;
  134. ASSERT(functionPdoExt->functionInterfaceList);
  135. functionPdoExt->baseInterfaceNumber = functionPdoExt->functionInterfaceList[0].InterfaceDescriptor->bInterfaceNumber;
  136. }
  137. /*
  138. * Create the device descriptor that clients see for this function.
  139. * This is the same as the parent's device descriptor except:
  140. * if the first interface of this function has an iInterface string
  141. * descriptor, then we substitute the parent device descriptor's iProduct
  142. * string with the iInterface string. That way, the client's view of
  143. * the device will only represent the interfaces in that function.
  144. */
  145. RtlCopyMemory( &functionPdoExt->functionDeviceDesc,
  146. &parentFdoExt->deviceDesc,
  147. sizeof(USB_DEVICE_DESCRIPTOR));
  148. ASSERT(functionPdoExt->numInterfaces > 0);
  149. if (functionPdoExt->functionInterfaceList[0].InterfaceDescriptor->iInterface){
  150. functionPdoExt->functionDeviceDesc.iProduct =
  151. functionPdoExt->functionInterfaceList[0].InterfaceDescriptor->iInterface;
  152. }
  153. parentFdoExt->deviceRelations->Objects[i] = functionPdo;
  154. /*
  155. * We may pass IRPs from the function PDO to the
  156. * parent FDO. Since we are not calling
  157. * IoAttachDeviceToDeviceStack to physically
  158. * attach this function PDO to a device stack,
  159. * we must set the "height" of this PDO ourselves,
  160. * so that any IRPs sent to this PDO have enough
  161. * IRP stack locations to go all the way down
  162. * the parent FDO's stack.
  163. */
  164. functionPdo->StackSize = parentFdoExt->fdo->StackSize+1;
  165. DBGVERBOSE(("Created function PDO %p (#%d)", (ULONG_PTR)functionPdo, i));
  166. }
  167. else {
  168. break;
  169. }
  170. }
  171. if (i == parentFdoExt->numFunctions){
  172. parentFdoExt->deviceRelations->Count = parentFdoExt->numFunctions;
  173. status = STATUS_SUCCESS;
  174. }
  175. else {
  176. FREEPOOL(parentFdoExt->deviceRelations);
  177. parentFdoExt->deviceRelations = NULL;
  178. }
  179. }
  180. else {
  181. status = STATUS_INSUFFICIENT_RESOURCES;
  182. }
  183. ASSERT(NT_SUCCESS(status));
  184. return status;
  185. }
  186. #define NibbleToHexW( byte ) (NibbleW[byte])
  187. WCHAR NibbleW[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  188. typedef struct _CLASS_COMAPTIBLE_IDS
  189. {
  190. // L"USB\\Class_nn&SubClass_nn&Prot_nn\0"
  191. //
  192. WCHAR ClassStr1[sizeof(L"USB\\Class_")/sizeof(WCHAR)-1];
  193. WCHAR ClassHex1[2];
  194. WCHAR SubClassStr1[sizeof(L"&SubClass_")/sizeof(WCHAR)-1];
  195. WCHAR SubClassHex1[2];
  196. WCHAR Prot1[sizeof(L"&Prot_")/sizeof(WCHAR)-1];
  197. WCHAR ProtHex1[2];
  198. WCHAR Null1[1];
  199. // L"USB\\Class_nn&SubClass_nn\0"
  200. //
  201. WCHAR ClassStr2[sizeof(L"USB\\Class_")/sizeof(WCHAR)-1];
  202. WCHAR ClassHex2[2];
  203. WCHAR SubClassStr2[sizeof(L"&SubClass_")/sizeof(WCHAR)-1];
  204. WCHAR SubClassHex2[2];
  205. WCHAR Null2[1];
  206. // L"USB\\Class_nn&SubClass_nn\0"
  207. //
  208. WCHAR ClassStr3[sizeof(L"USB\\Class_")/sizeof(WCHAR)-1];
  209. WCHAR ClassHex3[2];
  210. WCHAR Null3[1];
  211. WCHAR DoubleNull[1];
  212. } CLASS_COMAPTIBLE_IDS, *PCLASS_COMAPTIBLE_IDS;
  213. static CLASS_COMAPTIBLE_IDS ClassCompatibleIDs =
  214. {
  215. // L"USB\\Class_nn&SubClass_nn&Prot_nn\0"
  216. //
  217. {'U','S','B','\\','C','l','a','s','s','_'},
  218. {'n','n'},
  219. {'&','S','u','b','C','l','a','s','s','_'},
  220. {'n','n'},
  221. {'&','P','r','o','t','_'},
  222. {'n','n'},
  223. {0},
  224. // L"USB\\Class_nn&SubClass_nn\0"
  225. //
  226. {'U','S','B','\\','C','l','a','s','s','_'},
  227. {'n','n'},
  228. {'&','S','u','b','C','l','a','s','s','_'},
  229. {'n','n'},
  230. {0},
  231. // L"USB\\Class_nn\0"
  232. //
  233. {'U','S','B','\\','C','l','a','s','s','_'},
  234. {'n','n'},
  235. {0},
  236. {0}
  237. };
  238. PWCHAR
  239. BuildCompatibleIDs(
  240. IN PUCHAR CompatibleID,
  241. IN PUCHAR SubCompatibleID,
  242. IN UCHAR Class,
  243. IN UCHAR SubClass,
  244. IN UCHAR Protocol
  245. )
  246. {
  247. ULONG ulTotal;
  248. PWCHAR pwch;
  249. PWCHAR pwchTmp;
  250. PCLASS_COMAPTIBLE_IDS pClassIds;
  251. ULONG i;
  252. WCHAR ClassHi = NibbleToHexW((Class) >> 4);
  253. WCHAR ClassLo = NibbleToHexW((Class) & 0x0f);
  254. WCHAR SubClassHi = NibbleToHexW((SubClass) >> 4);
  255. WCHAR SubClassLo = NibbleToHexW((SubClass) & 0x0f);
  256. WCHAR ProtocolHi = NibbleToHexW((Protocol) >> 4);
  257. WCHAR ProtocolLo = NibbleToHexW((Protocol) & 0x0f);
  258. PAGED_CODE();
  259. ulTotal = sizeof(CLASS_COMAPTIBLE_IDS);
  260. if (SubCompatibleID && SubCompatibleID[0] != 0)
  261. {
  262. ulTotal += sizeof(L"USB\\MS_COMP_xxxxxxxx&MS_SUBCOMP_xxxxxxxx");
  263. }
  264. if (CompatibleID && CompatibleID[0] != 0)
  265. {
  266. ulTotal += sizeof(L"USB\\MS_COMP_xxxxxxxx");
  267. }
  268. pwch = ALLOCPOOL(PagedPool, ulTotal);
  269. if (pwch)
  270. {
  271. pwchTmp = pwch;
  272. if (SubCompatibleID && SubCompatibleID[0] != 0)
  273. {
  274. RtlCopyMemory(pwchTmp,
  275. L"USB\\MS_COMP_",
  276. sizeof(L"USB\\MS_COMP_")-sizeof(WCHAR));
  277. (PUCHAR)pwchTmp += sizeof(L"USB\\MS_COMP_")-sizeof(WCHAR);
  278. for (i = 0; i < 8 && CompatibleID[i] != 0; i++)
  279. {
  280. *pwchTmp++ = (WCHAR)CompatibleID[i];
  281. }
  282. RtlCopyMemory(pwchTmp,
  283. L"&MS_SUBCOMP_",
  284. sizeof(L"&MS_SUBCOMP_")-sizeof(WCHAR));
  285. (PUCHAR)pwchTmp += sizeof(L"&MS_SUBCOMP_")-sizeof(WCHAR);
  286. for (i = 0; i < 8 && SubCompatibleID[i] != 0; i++)
  287. {
  288. *pwchTmp++ = (WCHAR)SubCompatibleID[i];
  289. }
  290. *pwchTmp++ = '\0';
  291. }
  292. if (CompatibleID && CompatibleID[0] != 0)
  293. {
  294. RtlCopyMemory(pwchTmp,
  295. L"USB\\MS_COMP_",
  296. sizeof(L"USB\\MS_COMP_")-sizeof(WCHAR));
  297. (PUCHAR)pwchTmp += sizeof(L"USB\\MS_COMP_")-sizeof(WCHAR);
  298. for (i = 0; i < 8 && CompatibleID[i] != 0; i++)
  299. {
  300. *pwchTmp++ = (WCHAR)CompatibleID[i];
  301. }
  302. *pwchTmp++ = '\0';
  303. }
  304. pClassIds = (PCLASS_COMAPTIBLE_IDS)pwchTmp;
  305. // Copy over the constant set of strings:
  306. // L"USB\\Class_nn&SubClass_nn&Prot_nn\0"
  307. // L"USB\\Class_nn&SubClass_nn\0"
  308. // L"USB\\Class_nn\0"
  309. //
  310. RtlCopyMemory(pClassIds,
  311. &ClassCompatibleIDs,
  312. sizeof(CLASS_COMAPTIBLE_IDS));
  313. // Fill in the 'nn' blanks
  314. //
  315. pClassIds->ClassHex1[0] =
  316. pClassIds->ClassHex2[0] =
  317. pClassIds->ClassHex3[0] = ClassHi;
  318. pClassIds->ClassHex1[1] =
  319. pClassIds->ClassHex2[1] =
  320. pClassIds->ClassHex3[1] = ClassLo;
  321. pClassIds->SubClassHex1[0] =
  322. pClassIds->SubClassHex2[0] = SubClassHi;
  323. pClassIds->SubClassHex1[1] =
  324. pClassIds->SubClassHex2[1] = SubClassLo;
  325. pClassIds->ProtHex1[0] = ProtocolHi;
  326. pClassIds->ProtHex1[1] = ProtocolLo;
  327. }
  328. return pwch;
  329. }
  330. /*
  331. ********************************************************************************
  332. * QueryFunctionPdoID
  333. ********************************************************************************
  334. *
  335. *
  336. *
  337. */
  338. NTSTATUS QueryFunctionPdoID(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp)
  339. {
  340. PIO_STACK_LOCATION irpSp;
  341. NTSTATUS status;
  342. PAGED_CODE();
  343. irpSp = IoGetCurrentIrpStackLocation(irp);
  344. switch (irpSp->Parameters.QueryId.IdType){
  345. case BusQueryHardwareIDs:
  346. /*
  347. * Call down the parent FDO's stack to get a multi-string of hardware ids for the PDO.
  348. */
  349. IoCopyCurrentIrpStackLocationToNext(irp);
  350. status = CallDriverSync(functionPdoExt->parentFdoExt->fdo, irp);
  351. if (NT_SUCCESS(status)){
  352. PWCHAR oldIDs, newIDs;
  353. /*
  354. * Append '&MI_xx' to each hardware id in the multi-string,
  355. * where 'xx' is the function number.
  356. */
  357. oldIDs = (PWCHAR)irp->IoStatus.Information;
  358. irp->IoStatus.Information = (ULONG_PTR)BAD_POINTER;
  359. newIDs = AppendInterfaceNumber(oldIDs, functionPdoExt->baseInterfaceNumber);
  360. ExFreePool(oldIDs);
  361. if (newIDs){
  362. irp->IoStatus.Information = (ULONG_PTR)newIDs;
  363. status = STATUS_SUCCESS;
  364. }
  365. else {
  366. irp->IoStatus.Information = (ULONG_PTR)BAD_POINTER;
  367. status = STATUS_UNSUCCESSFUL;
  368. }
  369. }
  370. ASSERT(NT_SUCCESS(status));
  371. break;
  372. case BusQueryDeviceID:
  373. /*
  374. * Call down the parent FDO's stack to get a the device id for the device's PDO.
  375. */
  376. IoCopyCurrentIrpStackLocationToNext(irp);
  377. status = CallDriverSync(functionPdoExt->parentFdoExt->fdo, irp);
  378. if (NT_SUCCESS(status)){
  379. PWCHAR oldId, newId, tmpId;
  380. /*
  381. * Append '&MI_xx' to the device id,
  382. * where 'xx' is the function number.
  383. */
  384. /*
  385. * First make this string into a multi-string.
  386. */
  387. oldId = (PWCHAR)irp->IoStatus.Information;
  388. tmpId = ALLOCPOOL(PagedPool, (WStrLen(oldId)+2)*sizeof(WCHAR));
  389. if (tmpId){
  390. ULONG len = WStrCpy(tmpId, oldId);
  391. /*
  392. * Add the extra NULL to terminate the multi-string.
  393. */
  394. tmpId[len+1] = UNICODE_NULL;
  395. /*
  396. * Append the function-number ending '&MI_xx' .
  397. */
  398. newId = AppendInterfaceNumber(tmpId, functionPdoExt->baseInterfaceNumber);
  399. if (newId){
  400. irp->IoStatus.Information = (ULONG_PTR)newId;
  401. }
  402. else {
  403. status = STATUS_DEVICE_DATA_ERROR;
  404. irp->IoStatus.Information = (ULONG_PTR)BAD_POINTER;
  405. }
  406. ExFreePool(tmpId);
  407. }
  408. else {
  409. status = STATUS_DEVICE_DATA_ERROR;
  410. irp->IoStatus.Information = (ULONG_PTR)BAD_POINTER;
  411. }
  412. ExFreePool(oldId);
  413. }
  414. ASSERT(NT_SUCCESS(status));
  415. break;
  416. case BusQueryInstanceID:
  417. /*
  418. * Produce an instance-id for this function-PDO.
  419. *
  420. * Note: NTKERN frees the returned pointer, so we must provide a fresh pointer.
  421. */
  422. {
  423. PWSTR instanceId = MemDup(L"0000", sizeof(L"0000"));
  424. if (instanceId){
  425. swprintf(instanceId, L"%04x", functionPdoExt->baseInterfaceNumber);
  426. irp->IoStatus.Information = (ULONG_PTR)instanceId;
  427. status = STATUS_SUCCESS;
  428. }
  429. else {
  430. status = STATUS_INSUFFICIENT_RESOURCES;
  431. }
  432. }
  433. ASSERT(NT_SUCCESS(status));
  434. break;
  435. case BusQueryCompatibleIDs:
  436. {
  437. PUSB_INTERFACE_DESCRIPTOR ifaceDesc;
  438. PUCHAR compatibleID;
  439. PUCHAR subCompatibleID;
  440. /*
  441. * Build the MS Extended Compatible ID strings.
  442. */
  443. if (ISPTR(functionPdoExt->parentFdoExt->msExtConfigDesc))
  444. {
  445. PMS_EXT_CONFIG_DESC msExtConfigDesc;
  446. ULONG i;
  447. msExtConfigDesc = functionPdoExt->parentFdoExt->msExtConfigDesc;
  448. i = functionPdoExt->functionIndex;
  449. ASSERT(i < msExtConfigDesc->Header.bCount);
  450. compatibleID = msExtConfigDesc->Function[i].CompatibleID;
  451. subCompatibleID = msExtConfigDesc->Function[i].SubCompatibleID;
  452. }
  453. else
  454. {
  455. compatibleID = NULL;
  456. subCompatibleID = NULL;
  457. }
  458. ifaceDesc = functionPdoExt->functionInterfaceList->InterfaceDescriptor;
  459. ASSERT(ifaceDesc);
  460. irp->IoStatus.Information = (ULONG_PTR)
  461. BuildCompatibleIDs(compatibleID,
  462. subCompatibleID,
  463. ifaceDesc->bInterfaceClass,
  464. ifaceDesc->bInterfaceSubClass,
  465. ifaceDesc->bInterfaceProtocol);
  466. if (irp->IoStatus.Information)
  467. {
  468. status = STATUS_SUCCESS;
  469. }
  470. else
  471. {
  472. status = STATUS_INSUFFICIENT_RESOURCES;
  473. }
  474. }
  475. break;
  476. default:
  477. /*
  478. * Do not return STATUS_NOT_SUPPORTED;
  479. * keep the default status
  480. * (this allows filter drivers to work).
  481. */
  482. status = irp->IoStatus.Status;
  483. break;
  484. }
  485. return status;
  486. }
  487. /*
  488. ********************************************************************************
  489. * QueryFunctionDeviceRelations
  490. ********************************************************************************
  491. *
  492. *
  493. */
  494. NTSTATUS QueryFunctionDeviceRelations(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp)
  495. {
  496. PIO_STACK_LOCATION irpSp;
  497. NTSTATUS status;
  498. PAGED_CODE();
  499. irpSp = IoGetCurrentIrpStackLocation(irp);
  500. if (irpSp->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation){
  501. /*
  502. * Return a reference to this PDO
  503. */
  504. PDEVICE_RELATIONS devRel = ALLOCPOOL(PagedPool, sizeof(DEVICE_RELATIONS));
  505. if (devRel){
  506. /*
  507. * Add a reference to the PDO, since CONFIGMG will free it.
  508. */
  509. ObReferenceObject(functionPdoExt->pdo);
  510. devRel->Objects[0] = functionPdoExt->pdo;
  511. devRel->Count = 1;
  512. irp->IoStatus.Information = (ULONG_PTR)devRel;
  513. status = STATUS_SUCCESS;
  514. }
  515. else {
  516. status = STATUS_INSUFFICIENT_RESOURCES;
  517. }
  518. }
  519. else {
  520. /*
  521. * Fail this Irp by returning the default
  522. * status (typically STATUS_NOT_SUPPORTED).
  523. */
  524. status = irp->IoStatus.Status;
  525. }
  526. return status;
  527. }
  528. /*
  529. ********************************************************************************
  530. * QueryFunctionCapabilities
  531. ********************************************************************************
  532. *
  533. *
  534. */
  535. NTSTATUS QueryFunctionCapabilities(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp)
  536. {
  537. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
  538. PDEVICE_CAPABILITIES deviceCapabilities = irpSp->Parameters.DeviceCapabilities.Capabilities;
  539. PAGED_CODE();
  540. /*
  541. * Copy the parent device's capabilities,
  542. * then set the flags we care about
  543. */
  544. ASSERT(deviceCapabilities);
  545. *deviceCapabilities = functionPdoExt->parentFdoExt->deviceCapabilities;
  546. deviceCapabilities->Removable = TRUE;
  547. deviceCapabilities->UniqueID = FALSE;
  548. // SurpriseRemovalOK is FALSE by default, and some clients (NDIS)
  549. // set it to true on the way down, in accordance with the DDK.
  550. // Also, some clients (USBSTOR) leave it as the default, FALSE, and
  551. // expect it to remain so.
  552. // deviceCapabilities->SurpriseRemovalOK = TRUE;
  553. deviceCapabilities->RawDeviceOK = FALSE;
  554. return STATUS_SUCCESS;
  555. }
  556. /*
  557. * HandleFunctionPdoPower
  558. *
  559. *
  560. */
  561. NTSTATUS HandleFunctionPdoPower(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp)
  562. {
  563. PIO_STACK_LOCATION irpSp;
  564. BOOLEAN queuedIrp = FALSE;
  565. BOOLEAN calledPoStartNextPowerIrp = FALSE;
  566. NTSTATUS status;
  567. PAGED_CODE();
  568. irpSp = IoGetCurrentIrpStackLocation(irp);
  569. switch (irpSp->MinorFunction){
  570. case IRP_MN_SET_POWER:
  571. switch (irpSp->Parameters.Power.Type) {
  572. case SystemPowerState:
  573. status = STATUS_SUCCESS;
  574. break;
  575. case DevicePowerState:
  576. // If the parent has been selectively suspended, then
  577. // power it up now to service this request.
  578. if (functionPdoExt->parentFdoExt->state == STATE_SUSPENDED ||
  579. functionPdoExt->parentFdoExt->pendingIdleIrp) {
  580. ParentSetD0(functionPdoExt->parentFdoExt);
  581. }
  582. ASSERT(functionPdoExt->parentFdoExt->state != STATE_SUSPENDED);
  583. switch (irpSp->Parameters.Power.State.DeviceState){
  584. case PowerDeviceD0:
  585. if (functionPdoExt->state == STATE_SUSPENDED){
  586. functionPdoExt->state = STATE_STARTED;
  587. }
  588. CompleteFunctionIdleNotification(functionPdoExt);
  589. status = STATUS_SUCCESS;
  590. break;
  591. case PowerDeviceD1:
  592. case PowerDeviceD2:
  593. case PowerDeviceD3:
  594. /*
  595. * Suspend
  596. */
  597. if (functionPdoExt->state == STATE_STARTED){
  598. functionPdoExt->state = STATE_SUSPENDED;
  599. }
  600. status = STATUS_SUCCESS;
  601. break;
  602. default:
  603. /*
  604. * Return the default status.
  605. */
  606. status = irp->IoStatus.Status;
  607. break;
  608. }
  609. break;
  610. default:
  611. /*
  612. * Return the default status.
  613. */
  614. status = irp->IoStatus.Status;
  615. break;
  616. }
  617. break;
  618. case IRP_MN_WAIT_WAKE:
  619. /*
  620. * We queue all WW irps on function PDO's and issue
  621. * just one irp down to the parent.
  622. */
  623. /*
  624. * Call PoStartNextPowerIrp first since we can't
  625. * touch the irp after queuing it.
  626. */
  627. PoStartNextPowerIrp(irp);
  628. calledPoStartNextPowerIrp = TRUE;
  629. status = EnqueueFunctionWaitWakeIrp(functionPdoExt, irp);
  630. if (status == STATUS_PENDING){
  631. queuedIrp = TRUE;
  632. }
  633. break;
  634. case IRP_MN_POWER_SEQUENCE:
  635. TRAP("IRP_MN_POWER_SEQUENCE (coverage trap)");
  636. status = irp->IoStatus.Status;
  637. break;
  638. case IRP_MN_QUERY_POWER:
  639. /*
  640. * We allow all power transitions
  641. */
  642. status = STATUS_SUCCESS;
  643. break;
  644. default:
  645. /*
  646. * Return the default status;
  647. */
  648. status = irp->IoStatus.Status;
  649. break;
  650. }
  651. if (!calledPoStartNextPowerIrp){
  652. PoStartNextPowerIrp(irp);
  653. }
  654. if (!queuedIrp){
  655. irp->IoStatus.Status = status;
  656. IoCompleteRequest(irp, IO_NO_INCREMENT);
  657. }
  658. return status;
  659. }
  660. /*
  661. * BuildFunctionConfigurationDescriptor
  662. *
  663. *
  664. * Note: this function cannot be pageable because internal
  665. * ioctls may be sent at IRQL==DISPATCH_LEVEL.
  666. */
  667. NTSTATUS BuildFunctionConfigurationDescriptor(
  668. PFUNCTION_PDO_EXT functionPdoExt,
  669. PUCHAR buffer,
  670. ULONG bufferLength,
  671. PULONG bytesReturned)
  672. {
  673. PUSB_CONFIGURATION_DESCRIPTOR parentConfigDesc;
  674. PUSB_CONFIGURATION_DESCRIPTOR functionConfigDesc;
  675. PUCHAR parentConfigDescEnd;
  676. PUSB_COMMON_DESCRIPTOR commonDesc;
  677. PUSB_INTERFACE_DESCRIPTOR thisIfaceDesc;
  678. PUCHAR scratch;
  679. ULONG totalLength;
  680. NTSTATUS status;
  681. ULONG i;
  682. // BUGBUG - change this to use the USBD ParseConfiguration function
  683. /*
  684. * The function's configuration descriptor will include
  685. * a subset of the interface descriptors in the parent's
  686. * configuration descriptor.
  687. */
  688. parentConfigDesc = functionPdoExt->parentFdoExt->selectedConfigDesc;
  689. parentConfigDescEnd = (PUCHAR)((PUCHAR)parentConfigDesc + parentConfigDesc->wTotalLength);
  690. /*
  691. * First calculate the total length of what we'll be copying.
  692. * It will include a configuration descriptor followed by
  693. * some number of interface descriptors.
  694. * Each interface descriptor may be followed by a some number
  695. * of class-specific descriptors.
  696. */
  697. totalLength = sizeof(USB_CONFIGURATION_DESCRIPTOR);
  698. for (i = 0; i < functionPdoExt->numInterfaces; i++) {
  699. /*
  700. * We will copy the interface descriptor and all following
  701. * descriptors until either the next interface
  702. * descriptor or the end of the entire
  703. * configuration descriptor.
  704. */
  705. thisIfaceDesc = functionPdoExt->functionInterfaceList[i].InterfaceDescriptor;
  706. ASSERT(thisIfaceDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE);
  707. commonDesc = (PUSB_COMMON_DESCRIPTOR)thisIfaceDesc;
  708. do {
  709. totalLength += commonDesc->bLength;
  710. commonDesc = (PUSB_COMMON_DESCRIPTOR)(((PUCHAR)commonDesc) + commonDesc->bLength);
  711. }
  712. while (((PUCHAR)commonDesc < parentConfigDescEnd) &&
  713. ((commonDesc->bDescriptorType != USB_INTERFACE_DESCRIPTOR_TYPE) ||
  714. (((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceNumber == thisIfaceDesc->bInterfaceNumber)));
  715. }
  716. scratch = ALLOCPOOL(NonPagedPool, totalLength);
  717. if (scratch){
  718. PUCHAR pch;
  719. pch = scratch;
  720. RtlCopyMemory(pch, parentConfigDesc, sizeof(USB_CONFIGURATION_DESCRIPTOR));
  721. pch += sizeof(USB_CONFIGURATION_DESCRIPTOR);
  722. for (i = 0; i < functionPdoExt->numInterfaces; i++) {
  723. /*
  724. * Copy the interface descriptor and all following
  725. * descriptors until either the next interface
  726. * descriptor or the end of the entire
  727. * configuration descriptor.
  728. */
  729. thisIfaceDesc = functionPdoExt->functionInterfaceList[i].InterfaceDescriptor;
  730. ASSERT(thisIfaceDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE);
  731. commonDesc = (PUSB_COMMON_DESCRIPTOR)thisIfaceDesc;
  732. do {
  733. RtlCopyMemory(pch, commonDesc, commonDesc->bLength);
  734. pch += commonDesc->bLength;
  735. commonDesc = (PUSB_COMMON_DESCRIPTOR)(((PUCHAR)commonDesc) + commonDesc->bLength);
  736. }
  737. while (((PUCHAR)commonDesc < parentConfigDescEnd) &&
  738. ((commonDesc->bDescriptorType != USB_INTERFACE_DESCRIPTOR_TYPE) ||
  739. (((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceNumber == thisIfaceDesc->bInterfaceNumber)));
  740. }
  741. /*
  742. * This 'function' child's config descriptor contains
  743. * a subset of the parent's interface descriptors.
  744. * Update the child's configuration descriptor's size
  745. * to reflect the possibly-reduced number of interface descriptors.
  746. */
  747. functionConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR)scratch;
  748. functionConfigDesc->bNumInterfaces = (UCHAR)functionPdoExt->numInterfaces;
  749. functionConfigDesc->wTotalLength = (USHORT)totalLength;
  750. /*
  751. * Copy as much of the config descriptor as will fit in the caller's buffer.
  752. * Return success whether or not the caller's buffer was actually big enough (BUGBUG ? - this is what usbhub did).
  753. */
  754. *bytesReturned = MIN(bufferLength, totalLength);
  755. RtlCopyMemory(buffer, scratch, *bytesReturned);
  756. DBGDUMPBYTES("BuildFunctionConfigurationDescriptor built config desc for function", buffer, *bytesReturned);
  757. FREEPOOL(scratch);
  758. status = STATUS_SUCCESS;
  759. }
  760. else {
  761. ASSERT(scratch);
  762. status = STATUS_INSUFFICIENT_RESOURCES;
  763. *bytesReturned = 0;
  764. }
  765. return status;
  766. }
  767. #if DBG
  768. /*
  769. * FunctionIoctlCompletion
  770. *
  771. * Monitor URB completion status for DEBUG ONLY.
  772. */
  773. NTSTATUS FunctionIoctlCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context)
  774. {
  775. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
  776. ULONG ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
  777. NTSTATUS status = irp->IoStatus.Status;
  778. PURB urb;
  779. PUCHAR urbFuncName;
  780. switch (ioControlCode){
  781. case IOCTL_INTERNAL_USB_SUBMIT_URB:
  782. urb = irpSp->Parameters.Others.Argument1;
  783. urbFuncName = DbgGetUrbName(urb->UrbHeader.Function);
  784. if (!urbFuncName) urbFuncName = "???";
  785. if (((status != STATUS_SUCCESS) && (status != STATUS_CANCELLED)) ||
  786. !USBD_SUCCESS(urb->UrbHeader.Status)){
  787. DBGVERBOSE(("FunctionIoctlCompletion: %s (%xh) returned ntstatus %xh, urbstatus %xh.", urbFuncName, urb->UrbHeader.Function, status, urb->UrbHeader.Status));
  788. DBG_LOG_URB(urb);
  789. DBGVERBOSE(("<>"));
  790. }
  791. break;
  792. default:
  793. if (status != STATUS_SUCCESS){
  794. DBGWARN(("FunctionIoctlCompletion: ioctl %xh returned %xh.", ioControlCode, status));
  795. }
  796. break;
  797. }
  798. /*
  799. * Must propagate the pending bit if a lower driver returned pending.
  800. */
  801. if (irp->PendingReturned){
  802. IoMarkIrpPending(irp);
  803. }
  804. return status;
  805. }
  806. #endif
  807. VOID FunctionIdleNotificationCancelRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp)
  808. {
  809. PDEVEXT devExt;
  810. PFUNCTION_PDO_EXT functionPdoExt;
  811. PPARENT_FDO_EXT parentFdoExt;
  812. KIRQL oldIrql;
  813. PIRP parentIdleIrpToCancel = NULL;
  814. DBGVERBOSE(("Idle notification IRP %x cancelled", Irp));
  815. IoReleaseCancelSpinLock(Irp->CancelIrql);
  816. devExt = DeviceObject->DeviceExtension;
  817. functionPdoExt = &devExt->functionPdoExt;
  818. parentFdoExt = functionPdoExt->parentFdoExt;
  819. KeAcquireSpinLock(&parentFdoExt->parentFdoExtSpinLock, &oldIrql);
  820. ASSERT(Irp == functionPdoExt->idleNotificationIrp);
  821. functionPdoExt->idleNotificationIrp = NULL;
  822. /*
  823. * One of the functions on this composite device no longer wants
  824. * the device to be idled. So we can no longer allow the parent
  825. * device to be idle. Cancel it after we drop the spinlock.
  826. */
  827. if (parentFdoExt->pendingIdleIrp){
  828. parentIdleIrpToCancel = parentFdoExt->pendingIdleIrp;
  829. parentFdoExt->pendingIdleIrp = NULL;
  830. }
  831. KeReleaseSpinLock(&parentFdoExt->parentFdoExtSpinLock, oldIrql);
  832. if (parentIdleIrpToCancel){
  833. IoCancelIrp(parentIdleIrpToCancel);
  834. }
  835. // Also, power up the parent here before we complete this Idle IRP.
  836. //
  837. // (HID will start to send requests immediately upon its completion,
  838. // which may be before the parent's Idle IRP cancel routine is called
  839. // which powers up the parent.)
  840. if (parentFdoExt->state == STATE_SUSPENDED ||
  841. parentFdoExt->pendingIdleIrp) {
  842. ParentSetD0(parentFdoExt);
  843. }
  844. Irp->IoStatus.Status = STATUS_CANCELLED;
  845. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  846. }
  847. VOID CompleteFunctionIdleNotification(PFUNCTION_PDO_EXT functionPdoExt)
  848. {
  849. PPARENT_FDO_EXT parentFdoExt = functionPdoExt->parentFdoExt;
  850. NTSTATUS status;
  851. KIRQL oldIrql;
  852. PIRP irp;
  853. KeAcquireSpinLock(&parentFdoExt->parentFdoExtSpinLock, &oldIrql);
  854. if (functionPdoExt->idleNotificationIrp){
  855. PDRIVER_CANCEL oldCancelRoutine;
  856. irp = functionPdoExt->idleNotificationIrp;
  857. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  858. if (oldCancelRoutine){
  859. ASSERT(oldCancelRoutine == FunctionIdleNotificationCancelRoutine);
  860. functionPdoExt->idleNotificationIrp = NULL;
  861. }
  862. else {
  863. /*
  864. * The irp was cancelled AND the cancel routine was called.
  865. * The cancel routine will complete this irp, so don't complete
  866. * it here.
  867. */
  868. ASSERT(irp->Cancel);
  869. irp = NULL;
  870. }
  871. }
  872. else {
  873. irp = NULL;
  874. }
  875. KeReleaseSpinLock(&parentFdoExt->parentFdoExtSpinLock, oldIrql);
  876. if (irp) {
  877. DBGVERBOSE(("Completing idle request IRP %x", irp));
  878. irp->IoStatus.Status = STATUS_SUCCESS;
  879. IoCompleteRequest(irp, IO_NO_INCREMENT);
  880. }
  881. }
  882. /*
  883. * FunctionIdleNotificationRequest
  884. *
  885. *
  886. * This function handles a request by a USB client driver to tell us
  887. * that the device wants to idle (selective suspend).
  888. *
  889. *
  890. */
  891. NTSTATUS FunctionIdleNotificationRequest(PFUNCTION_PDO_EXT functionPdoExt, PIRP Irp)
  892. {
  893. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
  894. PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
  895. NTSTATUS ntStatus = STATUS_PENDING;
  896. DBGVERBOSE(("Idle request %x, IRP %x", functionPdoExt, Irp));
  897. idleCallbackInfo = (PUSB_IDLE_CALLBACK_INFO)
  898. irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
  899. if (idleCallbackInfo && idleCallbackInfo->IdleCallback){
  900. PPARENT_FDO_EXT parentFdoExt = functionPdoExt->parentFdoExt;
  901. BOOLEAN doCheckParentIdle = FALSE;
  902. KIRQL oldIrql;
  903. KeAcquireSpinLock(&parentFdoExt->parentFdoExtSpinLock, &oldIrql);
  904. if (functionPdoExt->idleNotificationIrp){
  905. DBGVERBOSE(("Idle request: already have idle IRP"));
  906. ntStatus = STATUS_DEVICE_BUSY;
  907. }
  908. else {
  909. PDRIVER_CANCEL oldCancelRoutine;
  910. functionPdoExt->idleNotificationIrp = Irp;
  911. /*
  912. * Must set cancel routine before checking Cancel flag
  913. */
  914. oldCancelRoutine = IoSetCancelRoutine(Irp, FunctionIdleNotificationCancelRoutine);
  915. ASSERT(!oldCancelRoutine);
  916. if (Irp->Cancel){
  917. /*
  918. * Irp was cancelled. Check whether cancel routine was called.
  919. */
  920. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
  921. if (oldCancelRoutine){
  922. /*
  923. * Cancel routine was NOT called. So complete the irp here.
  924. */
  925. functionPdoExt->idleNotificationIrp = NULL;
  926. ntStatus = STATUS_CANCELLED;
  927. }
  928. else {
  929. /*
  930. * Cancel routine was called, and it will complete the IRP
  931. * as soon as we drop the spinlock.
  932. * Return STATUS_PENDING so we don't touch the IRP.
  933. */
  934. IoMarkIrpPending(Irp);
  935. ntStatus = STATUS_PENDING;
  936. }
  937. }
  938. else {
  939. doCheckParentIdle = TRUE;
  940. }
  941. }
  942. KeReleaseSpinLock(&parentFdoExt->parentFdoExtSpinLock, oldIrql);
  943. if (doCheckParentIdle){
  944. /*
  945. * See if we are ready to idle out this hub (after dropping spinlock).
  946. */
  947. CheckParentIdle(parentFdoExt);
  948. }
  949. }
  950. else {
  951. DBGVERBOSE(("Idle request: No callback provided with idle IRP!"));
  952. ntStatus = STATUS_NO_CALLBACK_ACTIVE;
  953. }
  954. return ntStatus;
  955. }
  956. /*
  957. * FunctionInternalDeviceControl
  958. *
  959. *
  960. * Note: this function cannot be pageable because internal
  961. * ioctls may be sent at IRQL==DISPATCH_LEVEL.
  962. */
  963. NTSTATUS FunctionInternalDeviceControl(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp)
  964. {
  965. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
  966. ULONG ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
  967. PURB urb;
  968. USHORT urbFunc;
  969. NTSTATUS status = NO_STATUS;
  970. switch (ioControlCode){
  971. case IOCTL_INTERNAL_USB_SUBMIT_URB:
  972. urb = irpSp->Parameters.Others.Argument1;
  973. ASSERT(urb);
  974. DBG_LOG_URB(urb);
  975. urbFunc = urb->UrbHeader.Function;
  976. if (functionPdoExt->state != STATE_STARTED){
  977. DBGWARN(("FunctionInternalDeviceControl: failing urb (func %xh) because child pdo state is %xh.", urbFunc, functionPdoExt->state));
  978. status = STATUS_DEVICE_NOT_READY;
  979. }
  980. else if (functionPdoExt->parentFdoExt->state != STATE_STARTED){
  981. DBGERR(("FunctionInternalDeviceControl: BAD PNP state! - child is started while parent has state %xh.", functionPdoExt->parentFdoExt->state));
  982. status = STATUS_DEVICE_NOT_READY;
  983. }
  984. else {
  985. switch (urbFunc){
  986. case URB_FUNCTION_SELECT_CONFIGURATION:
  987. status = UrbFunctionSelectConfiguration(functionPdoExt, urb);
  988. irp->IoStatus.Information = 0;
  989. break;
  990. case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
  991. status = UrbFunctionGetDescriptorFromDevice(functionPdoExt, urb);
  992. break;
  993. case URB_FUNCTION_SELECT_INTERFACE:
  994. /*
  995. * Pass this URB down to the parent
  996. */
  997. ASSERT(urb->UrbSelectInterface.ConfigurationHandle);
  998. break;
  999. case URB_FUNCTION_ISOCH_TRANSFER:
  1000. /*
  1001. * Pass this URB down to the parent
  1002. */
  1003. DBGSHOWISOCHPROGRESS();
  1004. break;
  1005. case URB_FUNCTION_ABORT_PIPE:
  1006. case URB_FUNCTION_RESET_PIPE:
  1007. /*
  1008. * Pass ABORT and RESET URBs down to the parent.
  1009. */
  1010. DBGVERBOSE((DbgGetUrbName(urbFunc)));
  1011. break;
  1012. case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:
  1013. case URB_FUNCTION_CLASS_INTERFACE:
  1014. case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
  1015. default:
  1016. /*
  1017. * Pass unsupported URBs down to the parent
  1018. */
  1019. DBGVERBOSE(("URB function %xh not implemented - passing to parent", (ULONG)urbFunc));
  1020. break;
  1021. }
  1022. }
  1023. /*
  1024. * Set the URB status
  1025. */
  1026. switch (status){
  1027. case NO_STATUS: break;
  1028. case STATUS_PENDING: urb->UrbHeader.Status = USBD_STATUS_PENDING; break;
  1029. case STATUS_SUCCESS: urb->UrbHeader.Status = USBD_STATUS_SUCCESS; break;
  1030. default: urb->UrbHeader.Status = USBD_STATUS_ERROR; break;
  1031. }
  1032. break;
  1033. case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION:
  1034. status = FunctionIdleNotificationRequest(functionPdoExt, irp);
  1035. break;
  1036. case IOCTL_INTERNAL_USB_GET_BUS_INFO:
  1037. case IOCTL_INTERNAL_USB_GET_PORT_STATUS:
  1038. /*
  1039. * Leave the status as NO_STATUS so that these IRPs get passed down to the parent.
  1040. */
  1041. break;
  1042. case IOCTL_INTERNAL_USB_RESET_PORT:
  1043. case IOCTL_INTERNAL_USB_CYCLE_PORT:
  1044. /*
  1045. * Pass RESET and CYCLE IRPs down to the parent.
  1046. * ParentInternalDeviceControl will synchronize
  1047. * multiple abort/resets on the parent device.
  1048. */
  1049. DBGWARN(("RESET or CYCLE PORT -- pass down to parent"));
  1050. break;
  1051. case IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO:
  1052. TRAP("IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO - shouldn't get this");
  1053. status = STATUS_NOT_IMPLEMENTED;
  1054. break;
  1055. default:
  1056. /*
  1057. * This is not a pnp/power/syscntrl irp, so we fail unsupported irps
  1058. * with an actual error code (not with the default status).
  1059. */
  1060. status = STATUS_NOT_SUPPORTED;
  1061. break;
  1062. }
  1063. if (status == NO_STATUS){
  1064. /*
  1065. * We didn't handle this IRP, so send it down to our own parent FDO.
  1066. */
  1067. IoCopyCurrentIrpStackLocationToNext(irp);
  1068. #if DBG
  1069. IoSetCompletionRoutine(irp, FunctionIoctlCompletion, functionPdoExt, TRUE, TRUE, TRUE);
  1070. #endif
  1071. status = IoCallDriver(functionPdoExt->parentFdoExt->fdo, irp);
  1072. }
  1073. else if (status != STATUS_PENDING){
  1074. /*
  1075. * We serviced this IRP, so complete it.
  1076. */
  1077. irp->IoStatus.Status = status;
  1078. IoCompleteRequest(irp, IO_NO_INCREMENT);
  1079. }
  1080. return status;
  1081. }
  1082. VOID FreeFunctionPDOResources(PFUNCTION_PDO_EXT functionPdoExt)
  1083. {
  1084. PAGED_CODE();
  1085. DBGVERBOSE(("Removing function PDO %xh (#%d)", functionPdoExt->pdo, functionPdoExt->functionIndex));
  1086. /*
  1087. * functionInterfaceList points inside
  1088. * the parent's interface list, so don't free it here.
  1089. */
  1090. IoDeleteDevice(functionPdoExt->pdo);
  1091. }
  1092. PFUNCTION_PDO_EXT FindFunctionByIndex(PPARENT_FDO_EXT parentFdoExt, ULONG functionIndex)
  1093. {
  1094. PFUNCTION_PDO_EXT functionPdoExt = NULL;
  1095. KIRQL oldIrql;
  1096. ULONG i;
  1097. KeAcquireSpinLock(&parentFdoExt->parentFdoExtSpinLock, &oldIrql);
  1098. ASSERT(parentFdoExt->deviceRelations);
  1099. for (i = 0; i < parentFdoExt->deviceRelations->Count; i++){
  1100. PDEVICE_OBJECT devObj = parentFdoExt->deviceRelations->Objects[i];
  1101. PDEVEXT devExt;
  1102. PFUNCTION_PDO_EXT thisFuncPdoExt;
  1103. ASSERT(devObj);
  1104. devExt = devObj->DeviceExtension;
  1105. ASSERT(devExt);
  1106. ASSERT(devExt->signature == USBCCGP_TAG);
  1107. ASSERT(!devExt->isParentFdo);
  1108. thisFuncPdoExt = &devExt->functionPdoExt;
  1109. if (thisFuncPdoExt->functionIndex == functionIndex){
  1110. functionPdoExt = thisFuncPdoExt;
  1111. break;
  1112. }
  1113. }
  1114. KeReleaseSpinLock(&parentFdoExt->parentFdoExtSpinLock, oldIrql);
  1115. return functionPdoExt;
  1116. }
  1117. /*
  1118. ********************************************************************************
  1119. * EnqueueFunctionWaitWakeIrp
  1120. ********************************************************************************
  1121. *
  1122. * Enqueue the function's WaitWake IRP in the PARENT's queue.
  1123. * If no WaitWake IRP is pending on the parent, send one down.
  1124. */
  1125. NTSTATUS EnqueueFunctionWaitWakeIrp(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp)
  1126. {
  1127. PPARENT_FDO_EXT parentFdoExt = functionPdoExt->parentFdoExt;
  1128. PDRIVER_CANCEL oldCancelRoutine;
  1129. BOOLEAN submitParentWWirp = FALSE;
  1130. KIRQL oldIrql;
  1131. NTSTATUS status;
  1132. KeAcquireSpinLock(&parentFdoExt->parentFdoExtSpinLock, &oldIrql);
  1133. /*
  1134. * Must set a cancel routine before checking the Cancel flag
  1135. * (this makes the cancel code path for the IRP have to contend
  1136. * for our local spinlock).
  1137. */
  1138. oldCancelRoutine = IoSetCancelRoutine(irp, FunctionWaitWakeIrpCancelRoutine);
  1139. ASSERT(!oldCancelRoutine);
  1140. if (irp->Cancel){
  1141. /*
  1142. * This IRP has already been cancelled.
  1143. */
  1144. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  1145. if (oldCancelRoutine){
  1146. /*
  1147. * Cancel routine was NOT called, so complete the IRP here
  1148. * (caller will do this when we return error).
  1149. */
  1150. ASSERT(oldCancelRoutine == FunctionWaitWakeIrpCancelRoutine);
  1151. status = STATUS_CANCELLED;
  1152. }
  1153. else {
  1154. /*
  1155. * Cancel routine was called, and it will dequeue and complete the IRP
  1156. * as soon as we drop the spinlock.
  1157. * Initialize the IRP's listEntry so the dequeue doesn't corrupt the list.
  1158. * Then return STATUS_PENDING so we don't touch the IRP
  1159. */
  1160. InitializeListHead(&irp->Tail.Overlay.ListEntry);
  1161. IoMarkIrpPending(irp);
  1162. status = STATUS_PENDING;
  1163. }
  1164. }
  1165. else {
  1166. /*
  1167. * Enqueue this WW irp in the parent's queue.
  1168. */
  1169. InsertTailList(&parentFdoExt->functionWaitWakeIrpQueue, &irp->Tail.Overlay.ListEntry);
  1170. /*
  1171. * IoMarkIrpPending sets a bit in the current stack location
  1172. * to indicate that the Irp may complete on a different thread.
  1173. */
  1174. IoMarkIrpPending(irp);
  1175. /*
  1176. * If a WW irp is not pending on the parent,
  1177. * then submit one after we drop the spinlock.
  1178. */
  1179. if (!parentFdoExt->isWaitWakePending){
  1180. submitParentWWirp = TRUE;
  1181. parentFdoExt->isWaitWakePending = TRUE;
  1182. }
  1183. status = STATUS_PENDING;
  1184. }
  1185. KeReleaseSpinLock(&parentFdoExt->parentFdoExtSpinLock, oldIrql);
  1186. if (submitParentWWirp){
  1187. SubmitParentWaitWakeIrp(parentFdoExt);
  1188. }
  1189. return status;
  1190. }
  1191. /*
  1192. ********************************************************************************
  1193. * FunctionWaitWakeIrpCancelRoutine
  1194. ********************************************************************************
  1195. *
  1196. */
  1197. VOID FunctionWaitWakeIrpCancelRoutine(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
  1198. {
  1199. PDEVEXT devExt = (PDEVEXT)deviceObject->DeviceExtension;
  1200. PFUNCTION_PDO_EXT functionPdoExt;
  1201. PPARENT_FDO_EXT parentFdoExt;
  1202. PIRP parentWaitWakeIrpToCancel = NULL;
  1203. KIRQL oldIrql;
  1204. ASSERT(devExt->signature == USBCCGP_TAG);
  1205. ASSERT(!devExt->isParentFdo);
  1206. functionPdoExt = &devExt->functionPdoExt;
  1207. parentFdoExt = functionPdoExt->parentFdoExt;
  1208. KeAcquireSpinLock(&parentFdoExt->parentFdoExtSpinLock, &oldIrql);
  1209. /*
  1210. * Dequeue the client's WaitWake IRP.
  1211. */
  1212. RemoveEntryList(&irp->Tail.Overlay.ListEntry);
  1213. /*
  1214. * If the last function WaitWake IRP just got cancelled,
  1215. * cancel the parent's WaitWake IRP as well.
  1216. */
  1217. if (IsListEmpty(&parentFdoExt->functionWaitWakeIrpQueue) &&
  1218. parentFdoExt->isWaitWakePending){
  1219. ASSERT(parentFdoExt->parentWaitWakeIrp);
  1220. parentWaitWakeIrpToCancel = parentFdoExt->parentWaitWakeIrp;
  1221. }
  1222. KeReleaseSpinLock(&parentFdoExt->parentFdoExtSpinLock, oldIrql);
  1223. IoReleaseCancelSpinLock(irp->CancelIrql);
  1224. irp->IoStatus.Status = STATUS_CANCELLED;
  1225. IoCompleteRequest(irp, IO_NO_INCREMENT);
  1226. if (parentWaitWakeIrpToCancel){
  1227. // BUGBUG - slight race - what if parent WW irp completes just before this ?
  1228. // (we can't block in the completion routine, so there may not be a fix for this)
  1229. IoCancelIrp(parentWaitWakeIrpToCancel);
  1230. }
  1231. }
  1232. /*
  1233. * CompleteAllFunctionWaitWakeIrps
  1234. *
  1235. * Complete all WaitWake irps in the parent's queue
  1236. * with the given status.
  1237. */
  1238. VOID CompleteAllFunctionWaitWakeIrps(PPARENT_FDO_EXT parentFdoExt, NTSTATUS status)
  1239. {
  1240. LIST_ENTRY irpsToComplete;
  1241. KIRQL oldIrql;
  1242. PLIST_ENTRY listEntry;
  1243. PIRP irp;
  1244. /*
  1245. * Complete all the irps in the parent's WW irp list.
  1246. * The irps can get resubmitted on the same thread that we
  1247. * complete them on; so in order to avoid an infinite loop,
  1248. * empty the list into a private queue first, then complete
  1249. * the irps out of the private queue.
  1250. */
  1251. InitializeListHead(&irpsToComplete);
  1252. KeAcquireSpinLock(&parentFdoExt->parentFdoExtSpinLock, &oldIrql);
  1253. while (!IsListEmpty(&parentFdoExt->functionWaitWakeIrpQueue)){
  1254. PDRIVER_CANCEL oldCancelRoutine;
  1255. listEntry = RemoveHeadList(&parentFdoExt->functionWaitWakeIrpQueue);
  1256. InitializeListHead(listEntry); // in case cancel routine tries to dequeue again
  1257. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  1258. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  1259. if (oldCancelRoutine){
  1260. ASSERT(oldCancelRoutine == FunctionWaitWakeIrpCancelRoutine);
  1261. /*
  1262. * We can't complete an IRP while holding a spinlock.
  1263. * Also, we don't want to complete a WaitWake IRP while
  1264. * still processing queue because a driver
  1265. * may resend an IRP on the same thread, causing us to loop forever.
  1266. * So just move the IRPs to a private queue and we'll complete them later.
  1267. */
  1268. InsertTailList(&irpsToComplete, listEntry);
  1269. }
  1270. else {
  1271. /*
  1272. * This IRP was cancelled and the cancel routine WAS called.
  1273. * The cancel routine will complete the IRP as soon as we drop the spinlock.
  1274. * So don't touch the IRP.
  1275. */
  1276. ASSERT(irp->Cancel);
  1277. }
  1278. }
  1279. KeReleaseSpinLock(&parentFdoExt->parentFdoExtSpinLock, oldIrql);
  1280. while (!IsListEmpty(&irpsToComplete)){
  1281. listEntry = RemoveHeadList(&irpsToComplete);
  1282. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  1283. irp->IoStatus.Status = status;
  1284. IoCompleteRequest(irp, IO_NO_INCREMENT);
  1285. }
  1286. }
  1287. /*
  1288. * CompleteAllFunctionIdleIrps
  1289. *
  1290. * Complete all child function PDO Idle IRPs for the given parent
  1291. * with the given status.
  1292. */
  1293. VOID CompleteAllFunctionIdleIrps(PPARENT_FDO_EXT parentFdoExt, NTSTATUS status)
  1294. {
  1295. LIST_ENTRY irpsToComplete;
  1296. KIRQL oldIrql;
  1297. PIRP irp;
  1298. ULONG i;
  1299. DBGVERBOSE(("Complete all child Idle IRPs for parent %x", parentFdoExt));
  1300. InitializeListHead(&irpsToComplete);
  1301. KeAcquireSpinLock(&parentFdoExt->parentFdoExtSpinLock, &oldIrql);
  1302. ASSERT(parentFdoExt->deviceRelations);
  1303. for (i = 0; i < parentFdoExt->deviceRelations->Count; i++) {
  1304. PDEVICE_OBJECT devObj = parentFdoExt->deviceRelations->Objects[i];
  1305. PDEVEXT devExt;
  1306. PFUNCTION_PDO_EXT thisFuncPdoExt;
  1307. ASSERT(devObj);
  1308. devExt = devObj->DeviceExtension;
  1309. ASSERT(devExt);
  1310. ASSERT(devExt->signature == USBCCGP_TAG);
  1311. ASSERT(!devExt->isParentFdo);
  1312. thisFuncPdoExt = &devExt->functionPdoExt;
  1313. irp = thisFuncPdoExt->idleNotificationIrp;
  1314. // complete the Idle IRP if we have one.
  1315. if (irp){
  1316. PDRIVER_CANCEL oldCancelRoutine;
  1317. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  1318. if (oldCancelRoutine){
  1319. ASSERT(oldCancelRoutine == FunctionIdleNotificationCancelRoutine);
  1320. InsertTailList(&irpsToComplete, &irp->Tail.Overlay.ListEntry);
  1321. thisFuncPdoExt->idleNotificationIrp = NULL;
  1322. }
  1323. else {
  1324. /*
  1325. * The IRP was cancelled and the cancel routine was called.
  1326. * The cancel routine will dequeue and complete the irp,
  1327. * so don't do it here.
  1328. */
  1329. ASSERT(irp->Cancel);
  1330. }
  1331. }
  1332. }
  1333. KeReleaseSpinLock(&parentFdoExt->parentFdoExtSpinLock, oldIrql);
  1334. while (!IsListEmpty(&irpsToComplete)){
  1335. PLIST_ENTRY listEntry;
  1336. listEntry = RemoveHeadList(&irpsToComplete);
  1337. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  1338. irp->IoStatus.Status = status;
  1339. IoCompleteRequest(irp, IO_NO_INCREMENT);
  1340. }
  1341. }
  1342. /*
  1343. ********************************************************************************
  1344. * InstallExtPropDesc
  1345. ********************************************************************************
  1346. *
  1347. *
  1348. */
  1349. VOID
  1350. InstallExtPropDesc (
  1351. IN PFUNCTION_PDO_EXT FunctionPdoExt
  1352. )
  1353. /*++
  1354. Routine Description:
  1355. This routines queries a device for an Extended Properties Descriptor, but
  1356. only once the very first time for a given instance of a device.
  1357. If the Extended Properties Descriptor and all of the Custom Property
  1358. Sections appear valid then each Custom Property section <ValueName,
  1359. ValueData> pair is installed in the device instance specific registry key
  1360. for the PDO.
  1361. The registry value entries would be found under this registry key:
  1362. HKLM\System\CCS\Enum\<DeviceID>\<InstanceID>\Device Parameters
  1363. Arguments:
  1364. FunctionPdoExt - The Function PDO Device Extension
  1365. Return Value:
  1366. None
  1367. --*/
  1368. {
  1369. PDEVICE_OBJECT deviceObject;
  1370. static WCHAR DidExtPropDescKey[] = L"ExtPropDescSemaphore";
  1371. ULONG didExtPropDesc;
  1372. MS_EXT_PROP_DESC_HEADER msExtPropDescHeader;
  1373. PMS_EXT_PROP_DESC pMsExtPropDesc;
  1374. ULONG bytesReturned;
  1375. NTSTATUS ntStatus;
  1376. PAGED_CODE();
  1377. deviceObject = FunctionPdoExt->pdo;
  1378. // Check if the semaphore value is already set in the registry. We only
  1379. // care whether or not it already exists, not what data it has.
  1380. //
  1381. ntStatus = GetPdoRegistryParameter(deviceObject,
  1382. DidExtPropDescKey,
  1383. NULL,
  1384. 0,
  1385. NULL,
  1386. NULL);
  1387. if (NT_SUCCESS(ntStatus))
  1388. {
  1389. // Already did this once for this device instance. Don't do it again.
  1390. //
  1391. return;
  1392. }
  1393. // Set the semaphore key in the registry so that we only run the following
  1394. // code once per device.
  1395. didExtPropDesc = 1;
  1396. SetPdoRegistryParameter(deviceObject,
  1397. DidExtPropDescKey,
  1398. &didExtPropDesc,
  1399. sizeof(didExtPropDesc),
  1400. REG_DWORD,
  1401. PLUGPLAY_REGKEY_DEVICE);
  1402. RtlZeroMemory(&msExtPropDescHeader, sizeof(MS_EXT_PROP_DESC_HEADER));
  1403. // Request just the header of the MS Extended Property Descriptor
  1404. //
  1405. ntStatus = GetMsOsFeatureDescriptor(
  1406. FunctionPdoExt->parentFdoExt,
  1407. 1, // Recipient Interface
  1408. (UCHAR)FunctionPdoExt->baseInterfaceNumber,
  1409. MS_EXT_PROP_DESCRIPTOR_INDEX,
  1410. &msExtPropDescHeader,
  1411. sizeof(MS_EXT_PROP_DESC_HEADER),
  1412. &bytesReturned);
  1413. // Make sure the MS Extended Property Descriptor header looks ok
  1414. //
  1415. if (NT_SUCCESS(ntStatus) &&
  1416. bytesReturned == sizeof(MS_EXT_PROP_DESC_HEADER) &&
  1417. msExtPropDescHeader.dwLength >= sizeof(MS_EXT_PROP_DESC_HEADER) &&
  1418. msExtPropDescHeader.bcdVersion == MS_EXT_PROP_DESC_VER &&
  1419. msExtPropDescHeader.wIndex == MS_EXT_PROP_DESCRIPTOR_INDEX &&
  1420. msExtPropDescHeader.wCount > 0)
  1421. {
  1422. // Allocate a buffer large enough for the entire descriptor
  1423. //
  1424. pMsExtPropDesc = ALLOCPOOL(NonPagedPool,
  1425. msExtPropDescHeader.dwLength);
  1426. if (pMsExtPropDesc)
  1427. {
  1428. RtlZeroMemory(pMsExtPropDesc, msExtPropDescHeader.dwLength);
  1429. // Request the entire MS Extended Property Descriptor
  1430. //
  1431. ntStatus = GetMsOsFeatureDescriptor(
  1432. FunctionPdoExt->parentFdoExt,
  1433. 1, // Recipient Interface
  1434. (UCHAR)FunctionPdoExt->baseInterfaceNumber,
  1435. MS_EXT_PROP_DESCRIPTOR_INDEX,
  1436. pMsExtPropDesc,
  1437. msExtPropDescHeader.dwLength,
  1438. &bytesReturned);
  1439. if (NT_SUCCESS(ntStatus) &&
  1440. bytesReturned == msExtPropDescHeader.dwLength &&
  1441. RtlCompareMemory(&msExtPropDescHeader,
  1442. pMsExtPropDesc,
  1443. sizeof(MS_EXT_PROP_DESC_HEADER)) ==
  1444. sizeof(MS_EXT_PROP_DESC_HEADER))
  1445. {
  1446. // MS Extended Property Descriptor retrieved ok, parse and
  1447. // install each Custom Property Section it contains.
  1448. //
  1449. InstallExtPropDescSections(deviceObject,
  1450. pMsExtPropDesc);
  1451. }
  1452. // Done with the MS Extended Property Descriptor buffer, free it
  1453. //
  1454. FREEPOOL(pMsExtPropDesc);
  1455. }
  1456. }
  1457. }
  1458. /*
  1459. ********************************************************************************
  1460. * InstallExtPropDescSections
  1461. ********************************************************************************
  1462. *
  1463. *
  1464. */
  1465. VOID
  1466. InstallExtPropDescSections (
  1467. PDEVICE_OBJECT DeviceObject,
  1468. PMS_EXT_PROP_DESC pMsExtPropDesc
  1469. )
  1470. /*++
  1471. Routine Description:
  1472. This routines parses an Extended Properties Descriptor and validates each
  1473. Custom Property Section contained in the Extended Properties Descriptor.
  1474. If all of the Custom Property Sections appear valid then each Custom
  1475. Property section <ValueName, ValueData> pair is installed in the device
  1476. instance specific registry key for the PDO.
  1477. The registry value entries would be found under this registry key:
  1478. HKLM\System\CCS\Enum\<DeviceID>\<InstanceID>\Device Parameters
  1479. Arguments:
  1480. DeviceObject - The PDO
  1481. pMsExtPropDesc - Pointer to an Extended Properties Descriptor buffer.
  1482. It is assumed that the header of this descriptor has
  1483. already been validated.
  1484. Return Value:
  1485. None
  1486. --*/
  1487. {
  1488. PUCHAR p;
  1489. PUCHAR end;
  1490. ULONG pass;
  1491. ULONG i;
  1492. ULONG dwSize;
  1493. ULONG dwPropertyDataType;
  1494. USHORT wPropertyNameLength;
  1495. PWCHAR bPropertyName;
  1496. ULONG dwPropertyDataLength;
  1497. PVOID bPropertyData;
  1498. NTSTATUS ntStatus;
  1499. PAGED_CODE();
  1500. // Get a pointer to the end of the entire Extended Properties Descriptor
  1501. //
  1502. end = (PUCHAR)pMsExtPropDesc + pMsExtPropDesc->Header.dwLength;
  1503. // First pass: Validate each Custom Property Section
  1504. // Second pass: Install each Custom Property Section (if first pass ok)
  1505. //
  1506. for (pass = 0; pass < 2; pass++)
  1507. {
  1508. // Get a pointer to the first Custom Property Section
  1509. //
  1510. p = (PUCHAR)&pMsExtPropDesc->CustomSection[0];
  1511. // Iterate over all of the Custom Property Sections
  1512. //
  1513. for (i = 0; i < pMsExtPropDesc->Header.wCount; i++)
  1514. {
  1515. ULONG offset;
  1516. // Make sure the dwSize field is in bounds
  1517. //
  1518. if (p + sizeof(ULONG) > end)
  1519. {
  1520. break;
  1521. }
  1522. // Extract the dwSize field and advance running offset
  1523. //
  1524. dwSize = *((PULONG)p);
  1525. offset = sizeof(ULONG);
  1526. // Make sure the entire structure is in bounds
  1527. //
  1528. if (p + dwSize > end)
  1529. {
  1530. break;
  1531. }
  1532. // Make sure the dwPropertyDataType field is in bounds
  1533. if (dwSize < offset + sizeof(ULONG))
  1534. {
  1535. break;
  1536. }
  1537. // Extract the dwPropertyDataType field and advance running offset
  1538. //
  1539. dwPropertyDataType = *((PULONG)(p + offset));
  1540. offset += sizeof(ULONG);
  1541. // Make sure the wPropertyNameLength field is in bounds
  1542. //
  1543. if (dwSize < offset + sizeof(USHORT))
  1544. {
  1545. break;
  1546. }
  1547. // Extract the wPropertyNameLength field and advance running offset
  1548. //
  1549. wPropertyNameLength = *((PUSHORT)(p + offset));
  1550. offset += sizeof(USHORT);
  1551. // Make sure the bPropertyName field is in bounds
  1552. //
  1553. if (dwSize < offset + wPropertyNameLength)
  1554. {
  1555. break;
  1556. }
  1557. // Set the bPropertyName pointer and advance running offset
  1558. //
  1559. bPropertyName = (PWCHAR)(p + offset);
  1560. offset += wPropertyNameLength;
  1561. // Make sure the dwPropertyDataLength field is in bounds
  1562. if (dwSize < offset + sizeof(ULONG))
  1563. {
  1564. break;
  1565. }
  1566. // Extract the dwPropertyDataLength field and advance running offset
  1567. //
  1568. dwPropertyDataLength = *((ULONG UNALIGNED*)(p + offset));
  1569. offset += sizeof(ULONG);
  1570. // Make sure the bPropertyData field is in bounds
  1571. //
  1572. if (dwSize < offset + dwPropertyDataLength)
  1573. {
  1574. break;
  1575. }
  1576. // Set the bPropertyData pointer and advance running offset
  1577. //
  1578. bPropertyData = p + offset;
  1579. offset += wPropertyNameLength;
  1580. // Make sure the dwPropertyDataType is valid
  1581. //
  1582. if (dwPropertyDataType < REG_SZ ||
  1583. dwPropertyDataType > REG_MULTI_SZ)
  1584. {
  1585. break;
  1586. }
  1587. // Make sure the wPropertyNameLength is valid
  1588. //
  1589. if (wPropertyNameLength == 0 ||
  1590. (wPropertyNameLength % sizeof(WCHAR)) != 0)
  1591. {
  1592. break;
  1593. }
  1594. // Make sure bPropertyName is NULL terminated
  1595. //
  1596. if (bPropertyName[(wPropertyNameLength / sizeof(WCHAR)) - 1] !=
  1597. UNICODE_NULL)
  1598. {
  1599. break;
  1600. }
  1601. // Everything looks ok,
  1602. //
  1603. if (pass > 0)
  1604. {
  1605. ntStatus = SetPdoRegistryParameter(
  1606. DeviceObject,
  1607. bPropertyName,
  1608. bPropertyData,
  1609. dwPropertyDataLength,
  1610. dwPropertyDataType,
  1611. PLUGPLAY_REGKEY_DEVICE);
  1612. }
  1613. }
  1614. // Skip the second pass if we bailed out of the first pass
  1615. //
  1616. if (i < pMsExtPropDesc->Header.wCount)
  1617. {
  1618. break;
  1619. }
  1620. }
  1621. }