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

1564 lines
38 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. enum.c
  5. Abstract:
  6. This module provides the functions related to device enumeration.
  7. Author:
  8. Andy Thornton (andrewth) 20-Oct-97
  9. Revision History:
  10. --*/
  11. #include "mfp.h"
  12. #pragma hdrstop
  13. #include <initguid.h>
  14. #include <mf.h>
  15. #include <wdmguid.h>
  16. NTSTATUS
  17. MfBuildChildRequirements(
  18. IN PMF_CHILD_EXTENSION Child,
  19. OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList
  20. );
  21. NTSTATUS
  22. MfBuildDeviceID(
  23. IN PMF_PARENT_EXTENSION Parent,
  24. OUT PWSTR *DeviceID
  25. );
  26. NTSTATUS
  27. MfBuildInstanceID(
  28. IN PMF_CHILD_EXTENSION Child,
  29. OUT PWSTR *InstanceID
  30. );
  31. NTSTATUS
  32. MfBuildResourceMap(
  33. IN PUCHAR Data,
  34. IN ULONG Length,
  35. OUT PMF_RESOURCE_MAP *ResourceMap
  36. );
  37. NTSTATUS
  38. MfBuildVaryingResourceMap(
  39. IN PMF_REGISTRY_VARYING_RESOURCE_MAP RegistryMap,
  40. IN ULONG Length,
  41. OUT PMF_VARYING_RESOURCE_MAP *ResourceMap
  42. );
  43. NTSTATUS
  44. MfEnumRegistryChild(
  45. IN HANDLE ParentHandle,
  46. IN ULONG Index,
  47. IN OUT PMF_DEVICE_INFO Info
  48. );
  49. NTSTATUS
  50. MfEnumerate(
  51. IN PMF_PARENT_EXTENSION Parent
  52. );
  53. NTSTATUS
  54. MfEnumerateFromInterface(
  55. IN PMF_PARENT_EXTENSION Parent
  56. );
  57. NTSTATUS
  58. MfEnumerateFromRegistry(
  59. IN PMF_PARENT_EXTENSION Parent
  60. );
  61. BOOLEAN
  62. MfIsChildEnumeratedAlready(
  63. PMF_PARENT_EXTENSION Parent,
  64. PUNICODE_STRING childName
  65. );
  66. BOOLEAN
  67. MfIsResourceShared(
  68. IN PMF_PARENT_EXTENSION Parent,
  69. IN UCHAR Index,
  70. IN ULONG Offset,
  71. IN ULONG Size
  72. );
  73. NTSTATUS
  74. MfParentResourceToChildRequirement(
  75. IN PMF_PARENT_EXTENSION Parent,
  76. IN PMF_CHILD_EXTENSION Child,
  77. IN UCHAR Index,
  78. IN ULONG Offset OPTIONAL,
  79. IN ULONG Size OPTIONAL,
  80. OUT PIO_RESOURCE_DESCRIPTOR Requirement
  81. );
  82. NTSTATUS
  83. MfValidateDeviceInfo(
  84. IN PMF_PARENT_EXTENSION Parent,
  85. IN PMF_DEVICE_INFO DeviceInfo
  86. );
  87. #ifdef ALLOC_PRAGMA
  88. #pragma alloc_text(PAGE, MfBuildChildRequirements)
  89. #pragma alloc_text(PAGE, MfBuildDeviceID)
  90. #pragma alloc_text(PAGE, MfBuildInstanceID)
  91. #pragma alloc_text(PAGE, MfBuildResourceMap)
  92. #pragma alloc_text(PAGE, MfBuildVaryingResourceMap)
  93. #pragma alloc_text(PAGE, MfEnumRegistryChild)
  94. #pragma alloc_text(PAGE, MfEnumerate)
  95. #pragma alloc_text(PAGE, MfEnumerateFromInterface)
  96. #pragma alloc_text(PAGE, MfEnumerateFromRegistry)
  97. #pragma alloc_text(PAGE, MfIsResourceShared)
  98. #pragma alloc_text(PAGE, MfParentResourceToChildRequirement)
  99. #endif
  100. NTSTATUS
  101. MfBuildResourceMap(
  102. IN PUCHAR Data,
  103. IN ULONG Length,
  104. OUT PMF_RESOURCE_MAP *ResourceMap
  105. )
  106. /*++
  107. Routine Description:
  108. Constructs an MF_RESOURCE_MAP from information returned from the registry
  109. Arguments:
  110. Data - The raw REG_BINARY data from the registry
  111. Length - Length in bytes of Data
  112. ResourceMap - On success a pointer to the resource map. Memory should be
  113. freed using ExFreePool when no longer required
  114. Return Value:
  115. Status code indicating the success or otherwise of the operation.
  116. --*/
  117. {
  118. PMF_RESOURCE_MAP resourceMap;
  119. //
  120. // Allocate the resource map structure, add space for a count
  121. //
  122. resourceMap = ExAllocatePoolWithTag(PagedPool,
  123. sizeof(MF_RESOURCE_MAP) + Length - 1,
  124. MF_RESOURCE_MAP_TAG
  125. );
  126. if (!resourceMap) {
  127. return STATUS_INSUFFICIENT_RESOURCES;
  128. }
  129. //
  130. // Fill it in
  131. //
  132. resourceMap->Count = Length;
  133. RtlCopyMemory(&resourceMap->Resources, Data, Length);
  134. //
  135. // Hand it back to the caller
  136. //
  137. *ResourceMap = resourceMap;
  138. return STATUS_SUCCESS;
  139. }
  140. NTSTATUS
  141. MfBuildVaryingResourceMap(
  142. IN PMF_REGISTRY_VARYING_RESOURCE_MAP RegistryMap,
  143. IN ULONG Length,
  144. OUT PMF_VARYING_RESOURCE_MAP *ResourceMap
  145. )
  146. /*++
  147. Routine Description:
  148. Constructs an MF_VARYING_RESOURCE_MAP from information returned from the registry
  149. Arguments:
  150. RegistryMap - The raw REG_BINARY data from the registry
  151. Length - Length in bytes of RegistryMap
  152. ResourceMap - On success a pointer to the resource map. Memory should be
  153. freed using ExFreePool when no longer required
  154. Return Value:
  155. Status code indicating the success or otherwise of the operation.
  156. --*/
  157. {
  158. PMF_VARYING_RESOURCE_MAP resourceMap;
  159. PMF_VARYING_RESOURCE_ENTRY current;
  160. PMF_REGISTRY_VARYING_RESOURCE_MAP currentRegistry;
  161. ULONG count;
  162. if (Length % sizeof(MF_REGISTRY_VARYING_RESOURCE_MAP) != 0) {
  163. return STATUS_INVALID_PARAMETER;
  164. }
  165. count = Length / sizeof(MF_REGISTRY_VARYING_RESOURCE_MAP);
  166. //
  167. // Allocate the resource map structure
  168. //
  169. resourceMap = ExAllocatePoolWithTag(PagedPool,
  170. sizeof(MF_VARYING_RESOURCE_MAP) +
  171. (count-1) * sizeof(MF_VARYING_RESOURCE_ENTRY),
  172. MF_VARYING_MAP_TAG
  173. );
  174. if (!resourceMap) {
  175. return STATUS_INSUFFICIENT_RESOURCES;
  176. }
  177. //
  178. // Fill it in
  179. //
  180. resourceMap->Count = count;
  181. //
  182. // Translate the registry data into an aligned internal format
  183. //
  184. current = resourceMap->Resources;
  185. currentRegistry = RegistryMap;
  186. while (count--) {
  187. current->ResourceIndex = currentRegistry->ResourceIndex;
  188. current->Offset = currentRegistry->Offset;
  189. current->Size = currentRegistry->Size;
  190. currentRegistry++;
  191. current++;
  192. }
  193. //
  194. // Hand it back to the caller
  195. //
  196. *ResourceMap = resourceMap;
  197. return STATUS_SUCCESS;
  198. }
  199. NTSTATUS
  200. MfEnumRegistryChild(
  201. IN HANDLE ParentHandle,
  202. IN ULONG Index,
  203. IN OUT PMF_DEVICE_INFO Info
  204. )
  205. /*++
  206. Routine Description:
  207. Initialized an MF_DEVICE_INFO from information stored in the registry.
  208. Arguments:
  209. ParentHandle - Handle to the registry key under which the data is stored
  210. Index - Index of the subkey to use
  211. Info - Pointer to the device info that should be filled in
  212. Return Value:
  213. Status code indicating the success or otherwise of the operation.
  214. --*/
  215. {
  216. NTSTATUS status;
  217. PMF_REGISTRY_VARYING_RESOURCE_MAP varyingMap = NULL;
  218. PUCHAR resourceMap = NULL;
  219. ULONG varyingMapSize = 0, resourceMapSize = 0, stringSize = 0;
  220. BOOLEAN gotId = FALSE;
  221. ASSERT(ParentHandle && Info);
  222. //
  223. // Retrieve the data - we must have a HardwareID and/or CompatibleID
  224. //
  225. status = MfGetRegistryValue(ParentHandle,
  226. L"HardwareID",
  227. REG_MULTI_SZ,
  228. MF_GETREG_SZ_TO_MULTI_SZ,
  229. &stringSize,
  230. &Info->HardwareID.Buffer
  231. );
  232. if (NT_SUCCESS(status)) {
  233. gotId = TRUE;
  234. } else if (status != STATUS_OBJECT_NAME_NOT_FOUND) {
  235. goto cleanup;
  236. }
  237. ASSERT(stringSize <= MAXUSHORT);
  238. if (stringSize <= MAXUSHORT) {
  239. Info->HardwareID.Length = (USHORT)stringSize;
  240. Info->HardwareID.MaximumLength = Info->HardwareID.Length;
  241. } else {
  242. status = STATUS_INVALID_PARAMETER;
  243. goto cleanup;
  244. }
  245. //
  246. // ... CompatibleID ...
  247. //
  248. stringSize = 0;
  249. status = MfGetRegistryValue(ParentHandle,
  250. L"CompatibleID",
  251. REG_MULTI_SZ,
  252. MF_GETREG_SZ_TO_MULTI_SZ,
  253. &stringSize,
  254. &Info->CompatibleID.Buffer
  255. );
  256. if (NT_SUCCESS(status)) {
  257. gotId = TRUE;
  258. } else if (status != STATUS_OBJECT_NAME_NOT_FOUND) {
  259. goto cleanup;
  260. }
  261. ASSERT(stringSize <= MAXUSHORT);
  262. if (stringSize <= MAXUSHORT) {
  263. Info->CompatibleID.Length = (USHORT)stringSize;
  264. Info->CompatibleID.MaximumLength = Info->CompatibleID.Length;
  265. } else {
  266. status = STATUS_INVALID_PARAMETER;
  267. goto cleanup;
  268. }
  269. //
  270. // Now check that we have got an ID - if we don't then fail
  271. //
  272. if (!gotId) {
  273. status = STATUS_UNSUCCESSFUL;
  274. goto cleanup;
  275. }
  276. //
  277. // ...ResourceMap...
  278. //
  279. status = MfGetRegistryValue(ParentHandle,
  280. L"ResourceMap",
  281. REG_BINARY,
  282. 0, // flags
  283. &resourceMapSize,
  284. &resourceMap
  285. );
  286. if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND) {
  287. goto cleanup;
  288. }
  289. //
  290. // If we have a resource map the store it in our device info
  291. //
  292. if (resourceMap) {
  293. status = MfBuildResourceMap(resourceMap,
  294. resourceMapSize,
  295. &Info->ResourceMap
  296. );
  297. ExFreePool(resourceMap);
  298. resourceMap = NULL;
  299. if (!NT_SUCCESS(status)) {
  300. goto cleanup;
  301. }
  302. }
  303. //
  304. // ...VaryingResourceMap...
  305. //
  306. status = MfGetRegistryValue(ParentHandle,
  307. L"VaryingResourceMap",
  308. REG_BINARY,
  309. 0, // flags
  310. &varyingMapSize,
  311. &varyingMap
  312. );
  313. if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND) {
  314. goto cleanup;
  315. }
  316. if (varyingMap) {
  317. status = MfBuildVaryingResourceMap(varyingMap,
  318. varyingMapSize,
  319. &Info->VaryingResourceMap
  320. );
  321. ExFreePool(varyingMap);
  322. varyingMap = NULL;
  323. if (!NT_SUCCESS(status)) {
  324. goto cleanup;
  325. }
  326. }
  327. //
  328. // ...MfFlags
  329. //
  330. status = MfGetRegistryValue(ParentHandle,
  331. L"MFFlags",
  332. REG_DWORD,
  333. 0, // flags
  334. NULL,
  335. (PVOID) &Info->MfFlags
  336. );
  337. if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND) {
  338. goto cleanup;
  339. }
  340. return STATUS_SUCCESS;
  341. cleanup:
  342. MfFreeDeviceInfo(Info);
  343. //
  344. // If any of the values were of the wrong type then this is an invalid
  345. // MF entry.
  346. //
  347. if (status == STATUS_OBJECT_TYPE_MISMATCH) {
  348. status = STATUS_INVALID_PARAMETER;
  349. }
  350. return status;
  351. }
  352. NTSTATUS
  353. MfEnumerate(
  354. IN PMF_PARENT_EXTENSION Parent
  355. )
  356. /*++
  357. Routine Description:
  358. Allocates and initialies the Children list of PDOs for this MF device.
  359. First from the registry and then by querying an MF_ENUMERATION_INTERFACE from
  360. its PDO.
  361. Arguments:
  362. Parent - The MF device that should be enumerated
  363. Return Value:
  364. Status code indicating the success or otherwise of the operation.
  365. --*/
  366. {
  367. NTSTATUS status;
  368. PMF_CHILD_EXTENSION current, next;
  369. //
  370. // Try to get our children from the registry
  371. //
  372. status = MfEnumerateFromRegistry(Parent);
  373. if (!NT_SUCCESS(status)) {
  374. //
  375. // STATUS_UNSUCCESSFUL indicates that there wasn't any MF information
  376. // in the registry
  377. //
  378. if (status == STATUS_UNSUCCESSFUL) {
  379. //
  380. // See if our parent has an MF_ENUMERATION_INTERFACE for us...
  381. //
  382. status = MfEnumerateFromInterface(Parent);
  383. }
  384. }
  385. return status;
  386. }
  387. NTSTATUS
  388. MfEnumerateFromRegistry(
  389. IN PMF_PARENT_EXTENSION Parent
  390. )
  391. /*++
  392. Routine Description:
  393. Allocates and initialies the Children list of PDOs for this MF device by
  394. looking in the registry
  395. Arguments:
  396. Parent - The MF device that should be enumerated
  397. Return Value:
  398. Status code indicating the success or otherwise of the operation.
  399. STATUS_UNSUCCESSFUL indicates that no MF information was found in the
  400. registry.
  401. --*/
  402. {
  403. NTSTATUS status;
  404. HANDLE parentHandle = NULL, childHandle = NULL;
  405. ULONG index = 0;
  406. UNICODE_STRING childName;
  407. PDEVICE_OBJECT pdo;
  408. PMF_CHILD_EXTENSION child;
  409. MF_DEVICE_INFO info;
  410. ASSERT(!(Parent->Common.DeviceState & MF_DEVICE_ENUMERATED));
  411. //
  412. // Open the "Device Parameters" key for our PDO and see what the INF file
  413. // put there.
  414. //
  415. status = IoOpenDeviceRegistryKey(Parent->PhysicalDeviceObject,
  416. PLUGPLAY_REGKEY_DEVICE,
  417. KEY_READ,
  418. &parentHandle
  419. );
  420. if (!NT_SUCCESS(status)) {
  421. goto cleanup;
  422. }
  423. ASSERT(parentHandle);
  424. //
  425. // Iterate over keys
  426. //
  427. for (;;) {
  428. //
  429. // Open the child key for this info
  430. //
  431. status = MfGetSubkeyByIndex(parentHandle,
  432. index,
  433. KEY_READ,
  434. &childHandle,
  435. &childName
  436. );
  437. if (status == STATUS_NO_MORE_ENTRIES) {
  438. if (IsListEmpty(&Parent->Children)) {
  439. //
  440. // There wern't any children - fail
  441. //
  442. status = STATUS_UNSUCCESSFUL;
  443. goto cleanup;
  444. }
  445. //
  446. // We've found all the children
  447. //
  448. break;
  449. }
  450. if (!NT_SUCCESS(status)) {
  451. goto cleanup;
  452. }
  453. RtlZeroMemory(&info, sizeof(info));
  454. if (!MfIsChildEnumeratedAlready(Parent, &childName)) {
  455. info.Name = childName;
  456. //
  457. // Query the registry for the info
  458. //
  459. status = MfEnumRegistryChild(childHandle, index, &info);
  460. if (!NT_SUCCESS(status)) {
  461. goto cleanup;
  462. }
  463. status = MfValidateDeviceInfo(Parent,&info);
  464. if (!NT_SUCCESS(status)) {
  465. ASSERT(FALSE);
  466. goto cleanup;
  467. }
  468. status = MfCreatePdo(Parent, &pdo);
  469. if (NT_SUCCESS(status)) {
  470. child = (PMF_CHILD_EXTENSION) pdo->DeviceExtension;
  471. child->Info = info;
  472. child->Common.DeviceState |= MF_DEVICE_ENUMERATED;
  473. } else {
  474. MfFreeDeviceInfo(&info);
  475. }
  476. } else {
  477. RtlFreeUnicodeString(&childName);
  478. }
  479. ZwClose(childHandle);
  480. index++;
  481. }
  482. ZwClose(parentHandle);
  483. return STATUS_SUCCESS;
  484. cleanup:
  485. if (parentHandle) {
  486. ZwClose(parentHandle);
  487. }
  488. if (childHandle) {
  489. ZwClose(childHandle);
  490. }
  491. return status;
  492. }
  493. NTSTATUS
  494. MfEnumerateFromInterface(
  495. IN PMF_PARENT_EXTENSION Parent
  496. )
  497. /*++
  498. Routine Description:
  499. Allocates and initialies the Children list of PDOs for this MF device by
  500. querying its pdo for an interface
  501. Arguments:
  502. Parent - The MF device that should be enumerated
  503. Return Value:
  504. Status code indicating the success or otherwise of the operation.
  505. --*/
  506. {
  507. NTSTATUS status;
  508. IO_STACK_LOCATION request;
  509. MF_ENUMERATION_INTERFACE interface;
  510. PDEVICE_OBJECT pdo;
  511. PMF_CHILD_EXTENSION child;
  512. MF_DEVICE_INFO info;
  513. ULONG index = 0;
  514. //
  515. // Send a query interface IRP to our parent's PDO
  516. //
  517. RtlZeroMemory(&request, sizeof(IO_STACK_LOCATION));
  518. RtlZeroMemory(&interface, sizeof(MF_ENUMERATION_INTERFACE));
  519. request.MajorFunction = IRP_MJ_PNP;
  520. request.MinorFunction = IRP_MN_QUERY_INTERFACE;
  521. request.Parameters.QueryInterface.InterfaceType = &GUID_MF_ENUMERATION_INTERFACE;
  522. request.Parameters.QueryInterface.Size = sizeof(MF_ENUMERATION_INTERFACE);
  523. request.Parameters.QueryInterface.Version = 1;
  524. request.Parameters.QueryInterface.Interface = (PINTERFACE) &interface;
  525. status = MfSendPnpIrp(Parent->PhysicalDeviceObject, &request, NULL);
  526. if (!NT_SUCCESS(status)) {
  527. goto cleanup;
  528. }
  529. FOR_ALL_IN_LIST(MF_CHILD_EXTENSION, &Parent->Children, child) {
  530. child->Common.DeviceState &= ~MF_DEVICE_ENUMERATED;
  531. }
  532. for (;;) {
  533. RtlZeroMemory(&info, sizeof(info));
  534. //
  535. // Query the interface for the info
  536. //
  537. status = interface.EnumerateChild(interface.Context,
  538. index,
  539. &info
  540. );
  541. if (!NT_SUCCESS(status)) {
  542. if (status == STATUS_NO_MORE_ENTRIES) {
  543. if (IsListEmpty(&Parent->Children)) {
  544. //
  545. // There wern't any children - fail
  546. //
  547. status = STATUS_UNSUCCESSFUL;
  548. goto cleanup;
  549. }
  550. status = STATUS_SUCCESS;
  551. break;
  552. } else {
  553. goto cleanup;
  554. }
  555. }
  556. status = MfValidateDeviceInfo(Parent,&info);
  557. if (!NT_SUCCESS(status)) {
  558. goto cleanup;
  559. }
  560. if (!MfIsChildEnumeratedAlready(Parent, &info.Name)) {
  561. //
  562. // Create a device object
  563. //
  564. status = MfCreatePdo(Parent, &pdo);
  565. if (NT_SUCCESS(status)) {
  566. child = (PMF_CHILD_EXTENSION) pdo->DeviceExtension;
  567. child->Info = info;
  568. child->Common.DeviceState |= MF_DEVICE_ENUMERATED;
  569. } else {
  570. MfFreeDeviceInfo(&info);
  571. }
  572. } else {
  573. child->Common.DeviceState |= MF_DEVICE_ENUMERATED;
  574. MfFreeDeviceInfo(&info);
  575. }
  576. index++;
  577. }
  578. interface.InterfaceDereference(interface.Context);
  579. return STATUS_SUCCESS;
  580. cleanup:
  581. return status;
  582. }
  583. NTSTATUS
  584. MfBuildDeviceID(
  585. IN PMF_PARENT_EXTENSION Parent,
  586. OUT PWSTR *DeviceID
  587. )
  588. /*++
  589. Routine Description:
  590. Constructs a device ID for the parent device
  591. Arguments:
  592. Parent - Parent the device ID should be constructed for
  593. DeviceID - On success the device id
  594. Return Value:
  595. Status code indicating the success or otherwise of the operation.
  596. --*/
  597. {
  598. #define MF_ENUMERATOR_STRING L"MF\\"
  599. NTSTATUS status;
  600. PWSTR source, destination, id = NULL;
  601. ULONG idSize;
  602. PWCHAR deviceID;
  603. SIZE_T dummy;
  604. idSize = sizeof(MF_ENUMERATOR_STRING) + Parent->DeviceID.Length;
  605. id = ExAllocatePoolWithTag(PagedPool,
  606. idSize,
  607. MF_DEVICE_ID_TAG
  608. );
  609. if (!id) {
  610. status = STATUS_INSUFFICIENT_RESOURCES;
  611. goto cleanup;
  612. }
  613. //
  614. // First copy the enumerator prefix
  615. // the deviceID parameter points to the end of
  616. // this string.
  617. //
  618. if (FAILED(StringCbCopyEx(id, // Destination
  619. idSize, // Destination buffer size
  620. MF_ENUMERATOR_STRING, // Source
  621. &deviceID, // Ptr to end of the copy
  622. &dummy, // bytes remaining in destination
  623. 0 // Flags
  624. ))) {
  625. ASSERT(FALSE);
  626. status = STATUS_INVALID_PARAMETER;
  627. goto cleanup;
  628. }
  629. //
  630. // Now concatenate the device ID of the parent
  631. // to this enumerator prefix. This means that
  632. // the deviceID variable points to the beginning of
  633. // this portion of the string. StringCbCatN guarantees
  634. // NULL termination of the string.
  635. //
  636. if (FAILED(StringCbCatN(id,
  637. idSize,
  638. Parent->DeviceID.Buffer,
  639. Parent->DeviceID.Length
  640. ))) {
  641. ASSERT(FALSE);
  642. status = STATUS_INVALID_PARAMETER;
  643. goto cleanup;
  644. }
  645. //
  646. // replace each occurence of '\' in the device ID
  647. // portion with '#'
  648. //
  649. while (*deviceID != UNICODE_NULL) {
  650. ASSERT(*deviceID != L' ');
  651. if (*deviceID == L'\\') {
  652. *deviceID = L'#';
  653. }
  654. deviceID++;
  655. }
  656. *DeviceID = id;
  657. return STATUS_SUCCESS;
  658. cleanup:
  659. if (id) {
  660. ExFreePool(id);
  661. }
  662. *DeviceID = NULL;
  663. return status;
  664. }
  665. NTSTATUS
  666. MfBuildInstanceID(
  667. IN PMF_CHILD_EXTENSION Child,
  668. OUT PWSTR *InstanceID
  669. )
  670. /*++
  671. Routine Description:
  672. Constructs a instance ID for this child
  673. Arguments:
  674. Child - Child the ID should be constructed for
  675. DeviceID - On success the device id
  676. Return Value:
  677. Status code indicating the success or otherwise of the operation.
  678. --*/
  679. {
  680. NTSTATUS status;
  681. PWSTR current, id = NULL;
  682. ULONG idSize;
  683. PWCHAR instancePtr;
  684. idSize = Child->Parent->InstanceID.Length + sizeof(L'#')
  685. + Child->Info.Name.Length + sizeof(UNICODE_NULL);
  686. id = ExAllocatePoolWithTag(PagedPool,
  687. idSize,
  688. MF_INSTANCE_ID_TAG
  689. );
  690. if (!id) {
  691. status = STATUS_INSUFFICIENT_RESOURCES;
  692. goto cleanup;
  693. }
  694. //
  695. // Copy the parents instance ID...
  696. //
  697. if (FAILED(StringCbCopyN(id,
  698. idSize,
  699. Child->Parent->InstanceID.Buffer,
  700. Child->Parent->InstanceID.Length
  701. ))) {
  702. ASSERT(FALSE);
  703. status = STATUS_INVALID_PARAMETER;
  704. goto cleanup;
  705. }
  706. //
  707. // ...then the '#'...
  708. //
  709. if (FAILED(StringCbCat(id, idSize, L"#"))) {
  710. ASSERT(FALSE);
  711. status = STATUS_INVALID_PARAMETER;
  712. goto cleanup;
  713. }
  714. //
  715. // ...the child name...
  716. //
  717. if (FAILED(StringCbCatN(id,
  718. idSize,
  719. Child->Info.Name.Buffer,
  720. Child->Info.Name.Length
  721. ))) {
  722. ASSERT(FALSE);
  723. status = STATUS_INVALID_PARAMETER;
  724. goto cleanup;
  725. }
  726. *InstanceID = id;
  727. return STATUS_SUCCESS;
  728. cleanup:
  729. if (id) {
  730. ExFreePool(id);
  731. }
  732. *InstanceID = NULL;
  733. return status;
  734. }
  735. BOOLEAN
  736. MfIsResourceShared(
  737. IN PMF_PARENT_EXTENSION Parent,
  738. IN UCHAR Index,
  739. IN ULONG Offset,
  740. IN ULONG Size
  741. )
  742. /*++
  743. Routine Description:
  744. Determines if the Parent resource of Index has been requested by more than
  745. one child, in which case the children wanting that resource should claim it
  746. shared.
  747. Arguments:
  748. Parent - The parent device of the MF subtree.
  749. Index - The index of the parent resources we are interested in.
  750. Return Value:
  751. TRUE if the resource is shared, FALSE otherwise
  752. --*/
  753. {
  754. PMF_CHILD_EXTENSION current;
  755. PUCHAR resource;
  756. PMF_VARYING_RESOURCE_ENTRY varyingResource;
  757. PLIST_ENTRY currentEntry;
  758. BOOLEAN result = FALSE;
  759. ULONG refCount = 0;
  760. //
  761. // Iterate through the list of children in the parent
  762. //
  763. MfAcquireChildrenLock(Parent);
  764. for (currentEntry = Parent->Children.Flink;
  765. currentEntry != &Parent->Children;
  766. currentEntry = currentEntry->Flink) {
  767. current = CONTAINING_RECORD(currentEntry,
  768. MF_CHILD_EXTENSION,
  769. ListEntry);
  770. //
  771. // Iterate through the array of descriptors
  772. //
  773. if (current->Info.ResourceMap) {
  774. FOR_ALL_IN_ARRAY(current->Info.ResourceMap->Resources,
  775. current->Info.ResourceMap->Count,
  776. resource) {
  777. if (*resource == Index) {
  778. refCount++;
  779. if (refCount > 1) {
  780. result = TRUE;
  781. goto out;
  782. }
  783. }
  784. }
  785. }
  786. if (current->Info.VaryingResourceMap) {
  787. FOR_ALL_IN_ARRAY(current->Info.VaryingResourceMap->Resources,
  788. current->Info.VaryingResourceMap->Count,
  789. varyingResource) {
  790. //
  791. // If indexes are the same and ranges overlap, we have a reference
  792. //
  793. if ((varyingResource->ResourceIndex == Index) &&
  794. ( ( Size == 0) ||
  795. ( varyingResource->Offset >= Offset &&
  796. varyingResource->Offset < Offset + Size) ||
  797. ( Offset >= varyingResource->Offset &&
  798. Offset < varyingResource->Offset + varyingResource->Size))) {
  799. refCount++;
  800. if (refCount > 1) {
  801. result = TRUE;
  802. goto out;
  803. }
  804. }
  805. }
  806. }
  807. }
  808. out:
  809. MfReleaseChildrenLock(Parent);
  810. return result;
  811. }
  812. NTSTATUS
  813. MfBuildChildRequirements(
  814. IN PMF_CHILD_EXTENSION Child,
  815. OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList
  816. )
  817. /*++
  818. Routine Description:
  819. Constructs a requirements list for Child based on the resources allocated
  820. to the childs parent
  821. Arguments:
  822. Child - Child the requirements list is to be built for
  823. RequirementsList - On success a pointer to the list
  824. Return Value:
  825. Status code indicating the success or otherwise of the operation.
  826. --*/
  827. {
  828. NTSTATUS status;
  829. ULONG size, count = 0;
  830. PIO_RESOURCE_REQUIREMENTS_LIST requirements = NULL;
  831. PIO_RESOURCE_DESCRIPTOR descriptor;
  832. PCHAR resource;
  833. PMF_VARYING_RESOURCE_ENTRY varyingResource;
  834. //
  835. // Check if we have a resource list. If not, then MF has been
  836. // loaded on device that doesn't consume resources. As a result,
  837. // the children can't consume resources either.
  838. //
  839. if (Child->Parent->ResourceList == NULL) {
  840. *RequirementsList = NULL;
  841. return STATUS_SUCCESS;
  842. }
  843. //
  844. // Calculate the size of the resource list
  845. //
  846. if (Child->Info.VaryingResourceMap) {
  847. count += Child->Info.VaryingResourceMap->Count;
  848. }
  849. if (Child->Info.ResourceMap) {
  850. count += Child->Info.ResourceMap->Count;
  851. }
  852. //
  853. // Allocate the buffer
  854. //
  855. size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) +
  856. (count-1) * sizeof(IO_RESOURCE_DESCRIPTOR);
  857. requirements = ExAllocatePoolWithTag(PagedPool,
  858. size,
  859. MF_CHILD_REQUIREMENTS_TAG
  860. );
  861. if (!requirements) {
  862. status = STATUS_INSUFFICIENT_RESOURCES;
  863. goto cleanup;
  864. }
  865. //
  866. // Build the list
  867. //
  868. RtlZeroMemory(requirements, size);
  869. requirements->ListSize = size;
  870. requirements->InterfaceType = Child->Parent->ResourceList->List[0].InterfaceType;
  871. requirements->BusNumber = Child->Parent->ResourceList->List[0].BusNumber;
  872. requirements->AlternativeLists = 1;
  873. requirements->List[0].Version = MF_CM_RESOURCE_VERSION;
  874. requirements->List[0].Revision = MF_CM_RESOURCE_REVISION;
  875. requirements->List[0].Count = count;
  876. descriptor = requirements->List[0].Descriptors;
  877. if (Child->Info.ResourceMap) {
  878. FOR_ALL_IN_ARRAY(Child->Info.ResourceMap->Resources,
  879. Child->Info.ResourceMap->Count,
  880. resource) {
  881. status = MfParentResourceToChildRequirement(Child->Parent,
  882. Child,
  883. *resource,
  884. 0,
  885. 0,
  886. descriptor
  887. );
  888. if (!NT_SUCCESS(status)) {
  889. goto cleanup;
  890. }
  891. descriptor++;
  892. }
  893. }
  894. if (Child->Info.VaryingResourceMap) {
  895. FOR_ALL_IN_ARRAY(Child->Info.VaryingResourceMap->Resources,
  896. Child->Info.VaryingResourceMap->Count,
  897. varyingResource) {
  898. status = MfParentResourceToChildRequirement(Child->Parent,
  899. Child,
  900. varyingResource->ResourceIndex,
  901. varyingResource->Offset,
  902. varyingResource->Size,
  903. descriptor
  904. );
  905. if (!NT_SUCCESS(status)) {
  906. goto cleanup;
  907. }
  908. descriptor++;
  909. }
  910. }
  911. *RequirementsList = requirements;
  912. return STATUS_SUCCESS;
  913. cleanup:
  914. *RequirementsList = NULL;
  915. if (requirements) {
  916. ExFreePool(requirements);
  917. }
  918. return status;
  919. }
  920. NTSTATUS
  921. MfParentResourceToChildRequirement(
  922. IN PMF_PARENT_EXTENSION Parent,
  923. IN PMF_CHILD_EXTENSION Child,
  924. IN UCHAR Index,
  925. IN ULONG Offset OPTIONAL,
  926. IN ULONG Size OPTIONAL,
  927. OUT PIO_RESOURCE_DESCRIPTOR Requirement
  928. )
  929. /*++
  930. Routine Description:
  931. This function build an requirements descriptor for a resource the parent is
  932. started with.
  933. Arguments:
  934. Parent - The parent device of the MF subtree.
  935. Index - The index of the parent resources we are interested in.
  936. Offset - The offset within the parent resource of the requirement.
  937. This is actually used as an index into a table stored in the parent
  938. resource list describing the mapping from this given offset to the
  939. real offset to be used. This allows for varying resource maps to
  940. access the same offset within the same resource and get a different
  941. requirement. If Size == 0, this is ignored.
  942. Size - The length of the requirement. If set to 0, it is assumed to be
  943. the length of the parent resource.
  944. Requirement - Pointer to a descriptor that should be filled in
  945. Return Value:
  946. Success or otherwise of the operation
  947. --*/
  948. {
  949. NTSTATUS status;
  950. CM_PARTIAL_RESOURCE_DESCRIPTOR resource;
  951. PMF_RESOURCE_TYPE resType;
  952. ULONG effectiveOffset;
  953. ULONGLONG resourceStart;
  954. ULONG dummyLength;
  955. ASSERT(Parent->ResourceList->Count == 1);
  956. //
  957. // Bounds check the index
  958. //
  959. if (Index > Parent->ResourceList->List[0].PartialResourceList.Count) {
  960. if (Child->Info.MfFlags & MF_FLAGS_FILL_IN_UNKNOWN_RESOURCE) {
  961. //
  962. // Fill in a null resource list
  963. //
  964. RtlZeroMemory(Requirement, sizeof(IO_RESOURCE_DESCRIPTOR));
  965. Requirement->Type = CmResourceTypeNull;
  966. return STATUS_SUCCESS;
  967. }
  968. return STATUS_INVALID_PARAMETER;
  969. }
  970. RtlCopyMemory(&resource,
  971. &Parent->ResourceList->List[0].PartialResourceList.PartialDescriptors[Index],
  972. sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
  973. //
  974. // Find the appropriate resource type for the resource -> requirement
  975. // function if this is an arbitrated resource
  976. //
  977. if (!(resource.Type & CmResourceTypeNonArbitrated)) {
  978. resType = MfFindResourceType(resource.Type);
  979. if (!resType) {
  980. DEBUG_MSG(1,
  981. ("Unknown resource type %i at parent index 0x%x\n",
  982. resource.Type,
  983. Index
  984. ));
  985. return STATUS_INVALID_PARAMETER;
  986. }
  987. //
  988. // update the resource with the correct offset and length
  989. // if size == 0 we assume it is optional and don't do the update
  990. //
  991. if (Size) {
  992. status = resType->UnpackResource(&resource,
  993. &resourceStart,
  994. &dummyLength);
  995. if (!NT_SUCCESS(status)) {
  996. return status;
  997. }
  998. status = resType->UpdateResource(&resource,
  999. resourceStart+Offset,
  1000. Size
  1001. );
  1002. if (!NT_SUCCESS(status)) {
  1003. return status;
  1004. }
  1005. }
  1006. //
  1007. // Convert the resource to a requirement
  1008. //
  1009. status = resType->RequirementFromResource(&resource, Requirement);
  1010. if (!NT_SUCCESS(status)) {
  1011. return status;
  1012. }
  1013. //
  1014. // Update the share disposition if necessary
  1015. //
  1016. if (MfIsResourceShared(Parent, Index, Offset, Size)) {
  1017. Requirement->ShareDisposition = CmResourceShareShared;
  1018. }
  1019. } else {
  1020. //
  1021. // This is a non-arbitrated resource so it is modled after a device
  1022. // private, just copy the data
  1023. //
  1024. Requirement->Type = resource.Type;
  1025. Requirement->ShareDisposition = resource.ShareDisposition;
  1026. Requirement->Flags = resource.Flags;
  1027. Requirement->u.DevicePrivate.Data[0] = resource.u.DevicePrivate.Data[0];
  1028. Requirement->u.DevicePrivate.Data[1] = resource.u.DevicePrivate.Data[1];
  1029. Requirement->u.DevicePrivate.Data[2] = resource.u.DevicePrivate.Data[2];
  1030. }
  1031. return STATUS_SUCCESS;
  1032. }
  1033. BOOLEAN
  1034. MfIsChildEnumeratedAlready(
  1035. PMF_PARENT_EXTENSION Parent,
  1036. PUNICODE_STRING ChildName
  1037. )
  1038. /*++
  1039. Routine Description:
  1040. This function checks whether a child with this name has already
  1041. been enumerated.
  1042. Arguments:
  1043. Parent - The parent device of the MF subtree.
  1044. ChildName - unicode string to compare to existing child names
  1045. Return Value:
  1046. TRUE or FALSE
  1047. --*/
  1048. {
  1049. PMF_CHILD_EXTENSION currentChild;
  1050. PLIST_ENTRY currentEntry;
  1051. BOOLEAN result = FALSE;
  1052. for (currentEntry = Parent->Children.Flink;
  1053. currentEntry != &Parent->Children;
  1054. currentEntry = currentEntry->Flink) {
  1055. currentChild = CONTAINING_RECORD(currentEntry,
  1056. MF_CHILD_EXTENSION,
  1057. ListEntry);
  1058. //
  1059. // Comparison is case-sensitive because there is no reason it
  1060. // shouldn't be.
  1061. //
  1062. if (RtlEqualUnicodeString(&currentChild->Info.Name,
  1063. ChildName,
  1064. FALSE)) {
  1065. result = TRUE;
  1066. break;
  1067. }
  1068. }
  1069. return result;
  1070. }
  1071. NTSTATUS
  1072. MfValidateDeviceInfo(
  1073. IN PMF_PARENT_EXTENSION Parent,
  1074. IN PMF_DEVICE_INFO DeviceInfo
  1075. )
  1076. /*++
  1077. Routine Description:
  1078. This routine validates that an MF_DEVICE_INFO structure read either from an interface
  1079. or the registry is valid.
  1080. Arguments:
  1081. Parent - The parent device for which the MF_DEVICE_INFO structure represents a child.
  1082. DeviceInfo - The structure to validate.
  1083. Return Value:
  1084. STATUS_SUCCESS if validation was successful.
  1085. STATUS_UNSUCCESSFUL otherwise.
  1086. --*/
  1087. {
  1088. ULONG i, parentResCount, parentResLength;
  1089. PCM_PARTIAL_RESOURCE_DESCRIPTOR parentResources;
  1090. PMF_VARYING_RESOURCE_ENTRY varyingResource;
  1091. if (Parent->ResourceList) {
  1092. parentResCount = Parent->ResourceList->List[0].PartialResourceList.Count;
  1093. parentResources = Parent->ResourceList->List[0].PartialResourceList.PartialDescriptors;
  1094. } else {
  1095. //
  1096. // The parent has no resources, so the device better not have
  1097. // any resource maps.
  1098. //
  1099. if (DeviceInfo->ResourceMap || DeviceInfo->VaryingResourceMap) {
  1100. return STATUS_UNSUCCESSFUL;
  1101. }
  1102. }
  1103. //
  1104. // Make sure that each entry in the resource map points to a valid resource
  1105. // of the parent device.
  1106. //
  1107. if (DeviceInfo->ResourceMap) {
  1108. for (i=0; i<DeviceInfo->ResourceMap->Count; i++) {
  1109. if (DeviceInfo->ResourceMap->Resources[i] >= parentResCount) {
  1110. return STATUS_UNSUCCESSFUL;
  1111. }
  1112. }
  1113. }
  1114. //
  1115. // Make sure that each entry in the varying resource map points to a valid
  1116. // resource of the parent device, and additionally, make sure that the
  1117. // offset/length in the varying resource map entry are a subset of the
  1118. // corresponding resource in the parent device.
  1119. //
  1120. if (DeviceInfo->VaryingResourceMap) {
  1121. for (i=0; i<DeviceInfo->VaryingResourceMap->Count; i++) {
  1122. varyingResource = &DeviceInfo->VaryingResourceMap->Resources[i];
  1123. if (varyingResource->ResourceIndex >= parentResCount) {
  1124. return STATUS_UNSUCCESSFUL;
  1125. }
  1126. parentResLength = parentResources[varyingResource->ResourceIndex].u.Generic.Length;
  1127. if ((varyingResource->Offset >= parentResLength) ||
  1128. (varyingResource->Size > parentResLength) ||
  1129. ((varyingResource->Offset + varyingResource->Size) > parentResLength)) {
  1130. return STATUS_UNSUCCESSFUL;
  1131. }
  1132. }
  1133. }
  1134. return STATUS_SUCCESS;
  1135. }