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.

2052 lines
65 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Module: kmxltop.c
  4. //
  5. // Description:
  6. // Topology parsing routines for the kernel mixer line driver
  7. //
  8. //
  9. //@@BEGIN_MSINTERNAL
  10. // Development Team:
  11. // D. Baumberger
  12. //
  13. // History: Date Author Comment
  14. //
  15. //@@END_MSINTERNAL
  16. //
  17. //---------------------------------------------------------------------------
  18. //
  19. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  20. // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  21. // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  22. // PURPOSE.
  23. //
  24. // Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
  25. //
  26. //---------------------------------------------------------------------------
  27. ///////////////////////////////////////////////////////////////////////
  28. ///////////////////////////////////////////////////////////////////////
  29. // //
  30. // I N C L U D E S //
  31. // //
  32. ///////////////////////////////////////////////////////////////////////
  33. ///////////////////////////////////////////////////////////////////////
  34. #include "WDMSYS.H"
  35. #include "kmxluser.h"
  36. ///////////////////////////////////////////////////////////////////////
  37. //
  38. // kmxlQueryTopology
  39. //
  40. // Queries the topology from the device and stores all the information
  41. // in pTopology.
  42. //
  43. //
  44. NTSTATUS
  45. kmxlQueryTopology(
  46. IN PFILE_OBJECT pfoInstance, // The handle to query the topology for
  47. OUT PKSTOPOLOGY pTopology // The topology structure to fill in
  48. )
  49. {
  50. NTSTATUS Status;
  51. PKSMULTIPLE_ITEM pCategories = NULL;
  52. PKSMULTIPLE_ITEM pNodes = NULL;
  53. PKSMULTIPLE_ITEM pConnections = NULL;
  54. ASSERT( pfoInstance );
  55. ASSERT( pTopology );
  56. PAGED_CODE();
  57. //
  58. // Get device's topology categories
  59. //
  60. Status = kmxlGetProperty(
  61. pfoInstance,
  62. &KSPROPSETID_Topology,
  63. KSPROPERTY_TOPOLOGY_CATEGORIES,
  64. 0, // 0 extra input bytes
  65. NULL, // No input data
  66. 0, // Flags
  67. &pCategories
  68. );
  69. if( !NT_SUCCESS( Status ) ) {
  70. RETURN( Status );
  71. }
  72. //
  73. // Get the list of nodes types in the topology
  74. //
  75. Status = kmxlGetProperty(
  76. pfoInstance,
  77. &KSPROPSETID_Topology,
  78. KSPROPERTY_TOPOLOGY_NODES,
  79. 0, // 0 extra input bytes
  80. NULL, // No input data
  81. 0, // Flags
  82. &pNodes
  83. );
  84. if( !NT_SUCCESS( Status ) ) {
  85. AudioFreeMemory_Unknown( &pCategories );
  86. RETURN( Status );
  87. }
  88. //
  89. // Get the list of connections in the meta-topology
  90. //
  91. Status = kmxlGetProperty(
  92. pfoInstance,
  93. &KSPROPSETID_Topology,
  94. KSPROPERTY_TOPOLOGY_CONNECTIONS,
  95. 0, // 0 extra input butes
  96. NULL, // No input data
  97. 0, // Flags
  98. &pConnections
  99. );
  100. if( !NT_SUCCESS( Status ) ) {
  101. AudioFreeMemory_Unknown( &pCategories );
  102. AudioFreeMemory_Unknown( &pNodes );
  103. RETURN( Status );
  104. }
  105. //
  106. // Fill in the topology structure so this information is available
  107. // later. For the Categories and TopologyNodes, the pointers are
  108. // pointers to a KSMULTIPLE_ITEM structure. The definition of this
  109. // is that the data will follow immediately after the structure.
  110. //
  111. pTopology->CategoriesCount = pCategories->Count;
  112. pTopology->Categories = ( GUID* )( pCategories + 1 );
  113. pTopology->TopologyNodesCount = pNodes->Count;
  114. pTopology->TopologyNodes = ( GUID* )( pNodes + 1 );
  115. pTopology->TopologyConnectionsCount = pConnections->Count;
  116. pTopology->TopologyConnections =
  117. (PKSTOPOLOGY_CONNECTION) ( pConnections + 1 );
  118. return( STATUS_SUCCESS );
  119. }
  120. ///////////////////////////////////////////////////////////////////////
  121. //
  122. // kmxlParseTopology
  123. //
  124. // Loops through all the pins building up lists of sources and
  125. // destinations. For each source, a child graph is the built.
  126. //
  127. //
  128. NTSTATUS
  129. kmxlParseTopology(
  130. IN PMIXEROBJECT pmxobj,
  131. OUT NODELIST* plistSources, // Pointer to the sources list to build
  132. OUT NODELIST* plistDests // Pointer to the dests list to build
  133. )
  134. {
  135. NTSTATUS Status;
  136. ULONG cPins,
  137. PinID;
  138. PMXLNODE pTemp;
  139. NODELIST listSources = NULL;
  140. NODELIST listDests = NULL;
  141. ASSERT( pmxobj );
  142. ASSERT( plistSources );
  143. ASSERT( plistDests );
  144. PAGED_CODE();
  145. //
  146. // Query the number of pins
  147. //
  148. DPF(DL_TRACE|FA_MIXER,("Parsing Topology for: %ls",pmxobj->pMixerDevice->DeviceInterface) );
  149. Status = GetPinProperty(
  150. pmxobj->pfo,
  151. KSPROPERTY_PIN_CTYPES,
  152. 0,
  153. sizeof( cPins ),
  154. &cPins );
  155. if( !NT_SUCCESS( Status ) ) {
  156. DPF(DL_WARNING|FA_USER,("GetPinProperty CTYPES Failed Status=%X",Status) );
  157. RETURN( Status );
  158. }
  159. DPF(DL_TRACE|FA_MIXER,("Number of Pins %u",cPins));
  160. //
  161. // Now scan through each of the pins identifying those that are
  162. // sources and destinations.
  163. //
  164. for( PinID = 0; PinID < cPins; PinID++ ) {
  165. KSPIN_DATAFLOW DataFlow;
  166. //
  167. // Read the direction of dataflow of this pin.
  168. //
  169. Status = GetPinProperty(
  170. pmxobj->pfo,
  171. KSPROPERTY_PIN_DATAFLOW,
  172. PinID,
  173. sizeof( KSPIN_DATAFLOW ),
  174. &DataFlow
  175. );
  176. if( !NT_SUCCESS( Status ) ) {
  177. DPF(DL_WARNING|FA_USER,("GetPinProperty DATAFLOW Failed Status=%X",Status) );
  178. continue;
  179. }
  180. //
  181. // Based on the DataFlow, identify if the pin is a source,
  182. // a destination, or neither.
  183. //
  184. switch( DataFlow ) {
  185. ///////////////////////////////////////////////////////////
  186. case KSPIN_DATAFLOW_IN:
  187. ///////////////////////////////////////////////////////////
  188. // DATAFLOW_IN pins are sources. //
  189. ///////////////////////////////////////////////////////////
  190. //
  191. // Create a new mixer node structure for this source
  192. // and fill in the known information about it.
  193. //
  194. pTemp = kmxlAllocateNode( TAG_AudN_NODE );
  195. if( !pTemp ) {
  196. Status=STATUS_INSUFFICIENT_RESOURCES;
  197. goto exit;
  198. }
  199. pTemp->Type = SOURCE;
  200. pTemp->Id = PinID;
  201. //
  202. // Retrieve the category of this pin and store it away.
  203. // The return does not need to be checked because the
  204. // GUID will remain at GUID_NULL and be categorized
  205. // properly.
  206. //
  207. GetPinProperty(
  208. pmxobj->pfo,
  209. KSPROPERTY_PIN_CATEGORY,
  210. PinID,
  211. sizeof( pTemp->NodeType ),
  212. &pTemp->NodeType
  213. );
  214. DPF(DL_TRACE|FA_MIXER,( "Identified SOURCE Pin %d: %s", PinID,
  215. PinCategoryToString( &pTemp->NodeType ) ) );
  216. //
  217. // Retrieve the commmunication of this pin and store it away so
  218. // we can tell if this is a wave out or wave in source
  219. //
  220. Status = GetPinProperty(
  221. pmxobj->pfo,
  222. KSPROPERTY_PIN_COMMUNICATION,
  223. PinID,
  224. sizeof( pTemp->Communication ),
  225. &pTemp->Communication
  226. );
  227. if (!NT_SUCCESS(Status)) {
  228. pTemp->Communication = KSPIN_COMMUNICATION_NONE;
  229. }
  230. //
  231. // Add this new source node to the list of source
  232. // nodes.
  233. //
  234. kmxlAddToList( listSources, pTemp );
  235. break;
  236. ///////////////////////////////////////////////////////////
  237. case KSPIN_DATAFLOW_OUT:
  238. ///////////////////////////////////////////////////////////
  239. // DATAFLOW_OUT pins are destinations //
  240. ///////////////////////////////////////////////////////////
  241. //
  242. // Create a new mixer node structure for this dest
  243. // and fill in the known information about it.
  244. //
  245. pTemp = kmxlAllocateNode( TAG_AudN_NODE );
  246. if( !pTemp ) {
  247. Status=STATUS_INSUFFICIENT_RESOURCES;
  248. goto exit;
  249. }
  250. pTemp->Type = DESTINATION;
  251. pTemp->Id = PinID;
  252. //
  253. // Retrieve the category of this pin and store it away.
  254. // The return does not need to be checked because the
  255. // GUID will remain at GUID_NULL and be categorized
  256. // properly.
  257. //
  258. GetPinProperty(
  259. pmxobj->pfo,
  260. KSPROPERTY_PIN_CATEGORY,
  261. PinID,
  262. sizeof( pTemp->NodeType ),
  263. &pTemp->NodeType
  264. );
  265. DPF(DL_TRACE|FA_MIXER,( "Identified DESTINATION Pin %d: %s", PinID,
  266. PinCategoryToString( &pTemp->NodeType ) ) );
  267. //
  268. // Retrieve the commmunication of this pin and store it away so
  269. // we can tell if this is a wave out or wave in destination
  270. //
  271. Status = GetPinProperty(
  272. pmxobj->pfo,
  273. KSPROPERTY_PIN_COMMUNICATION,
  274. PinID,
  275. sizeof( pTemp->Communication ),
  276. &pTemp->Communication
  277. );
  278. if (!NT_SUCCESS(Status)) {
  279. pTemp->Communication = KSPIN_COMMUNICATION_NONE;
  280. }
  281. //
  282. // Add this new destination node to the list of destination
  283. // nodes.
  284. //
  285. kmxlAddToList( listDests, pTemp );
  286. break;
  287. ///////////////////////////////////////////////////////////
  288. default:
  289. ///////////////////////////////////////////////////////////
  290. // DATAFLOW_BOTH and others are currently not supported. //
  291. ///////////////////////////////////////////////////////////
  292. DPF(DL_WARNING|FA_USER,("Invalid DataFlow value =%X",DataFlow) );
  293. }
  294. }
  295. DPF(DL_TRACE|FA_MIXER,("DataFlow done. PIN_COMMUNICATION read.") );
  296. //
  297. // For each source found, build the graphs of their children. This
  298. // will recurse builing the graph of the children's children, etc.
  299. //
  300. pTemp = kmxlFirstInList( listSources );
  301. while( pTemp ) {
  302. Status=kmxlBuildChildGraph(
  303. pmxobj, // The mixer object
  304. listDests, // The list of all the destinations
  305. pTemp, // The source node to build the graph for
  306. KSFILTER_NODE, // Sources are always KSFILTER_NODEs
  307. pTemp->Id // The Pin id of the source
  308. );
  309. if (!NT_SUCCESS(Status)) {
  310. DPF(DL_WARNING|FA_USER,("kmxlBuildChildGraph failed Status=%X",Status) );
  311. goto exit;
  312. }
  313. pTemp = kmxlNextNode( pTemp );
  314. }
  315. exit:
  316. //
  317. // Finally fill in the client pointers
  318. //
  319. *plistSources = listSources;
  320. *plistDests = listDests;
  321. //We must have a destination and a source
  322. if (listSources == NULL || listDests == NULL)
  323. {
  324. Status = STATUS_INVALID_DEVICE_REQUEST;
  325. }
  326. return Status;
  327. }
  328. ///////////////////////////////////////////////////////////////////////
  329. //
  330. // BuildChildGraph
  331. //
  332. // Builds the graph of the child of the given node. For each child
  333. // of the node, it recurses to find their child, etc.
  334. //
  335. //
  336. NTSTATUS
  337. kmxlBuildChildGraph(
  338. IN PMIXEROBJECT pmxobj,
  339. IN NODELIST listDests, // The list of destinations
  340. IN PMXLNODE pNode, // The node to build the graph for
  341. IN ULONG FromNode, // The node's ID
  342. IN ULONG FromNodePin // The Pin connection to look for
  343. )
  344. {
  345. ULONG Index = 0;
  346. PMXLNODE pNewNode = NULL;
  347. PMXLNODE pTemp = NULL;
  348. BOOL bEndOfTheLine = FALSE;
  349. PEERNODE* pPeerNode = NULL;
  350. NTSTATUS Status=STATUS_SUCCESS;
  351. PAGED_CODE();
  352. //
  353. // Find the index of the requested connection. A return of -1
  354. // indicates that the connection was not found. Searches start
  355. // at Index, which starts with 0 and is > 0 if the last was a match.
  356. //
  357. while ( (Index = kmxlFindTopologyConnection(pmxobj, Index, FromNode, FromNodePin))
  358. != (ULONG) -1) {
  359. //
  360. // Check to see if this connection is a KSFILTER_NODE. That will
  361. // indicate that it's connected to a destination and not another node.
  362. //
  363. if( pmxobj->pTopology->TopologyConnections[ Index ].ToNode == KSFILTER_NODE ) {
  364. //
  365. // Find the destination node so that the parent field can be
  366. // updated to include this node. bEndOfTheLine is set to TRUE
  367. // since there can be no other connections after the destination.
  368. //
  369. pNewNode = kmxlFindDestination(
  370. listDests,
  371. pmxobj->pTopology->TopologyConnections[ Index ].ToNodePin
  372. );
  373. bEndOfTheLine = TRUE;
  374. //
  375. // We better find a destination; if not, something's really wrong.
  376. //
  377. if (pNewNode==NULL) {
  378. RETURN( STATUS_UNSUCCESSFUL );
  379. }
  380. } else {
  381. //
  382. // Using the identifier stored in the ToNode of the topology
  383. // connections, index into the node table and retrieve the
  384. // mixer node associated with that id.
  385. //
  386. pNewNode = &pmxobj->pNodeTable[
  387. pmxobj->pTopology->TopologyConnections[ Index ].ToNode
  388. ];
  389. //
  390. // Fill in a couple of missing details. Note that these details
  391. // may already be filled in but it doesn't hurt to overwrite
  392. // them with the same values.
  393. //
  394. pNewNode->Type = NODE;
  395. pNewNode->Id = pmxobj->pTopology->TopologyConnections[ Index ].ToNode;
  396. }
  397. //
  398. // Insert the new node into the childlist of the current node only
  399. // if it isn't already there. It only wastes memory to add it more
  400. // than once and prevents the proper updating of the child and parent
  401. // lists.
  402. //
  403. if( !kmxlInChildList( pNode, pNewNode ) ) {
  404. pPeerNode = kmxlAllocatePeerNode( pNewNode, TAG_Audn_PEERNODE );
  405. if( !pPeerNode ) {
  406. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  407. }
  408. DPF(DL_TRACE|FA_MIXER,( "Added %s(%d-0x%08x) to child list of %s(%d-0x%08x).",
  409. pPeerNode->pNode->Type == SOURCE ? "SOURCE" :
  410. pPeerNode->pNode->Type == DESTINATION ? "DEST" :
  411. pPeerNode->pNode->Type == NODE ? "NODE" :
  412. "Huh?",
  413. pPeerNode->pNode->Id,
  414. pPeerNode,
  415. pNode->Type == SOURCE ? "SOURCE" :
  416. pNode->Type == DESTINATION ? "DEST" :
  417. pNode->Type == NODE ? "NODE" :
  418. "Huh?",
  419. pNode->Id,
  420. pNode ) );
  421. kmxlAddToChildList( pNode, pPeerNode );
  422. }
  423. //
  424. // Insert the new node into the parentlist of the new node only
  425. // if it isn't already there. It only wastes memory to add it more
  426. // than once and prevents the proper updating the child and parent
  427. // lists.
  428. //
  429. if( !kmxlInParentList( pNewNode, pNode ) ) {
  430. pPeerNode = kmxlAllocatePeerNode( pNode, TAG_Audn_PEERNODE );
  431. if( !pPeerNode ) {
  432. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  433. }
  434. DPF(DL_TRACE|FA_MIXER,("Added %s(%d-0x%08x) to parent list of %s(%d-0x%08x).",
  435. pPeerNode->pNode->Type == SOURCE ? "SOURCE" :
  436. pPeerNode->pNode->Type == DESTINATION ? "DEST" :
  437. pPeerNode->pNode->Type == NODE ? "NODE" :
  438. "Huh?",
  439. pPeerNode->pNode->Id,
  440. pPeerNode,
  441. pNewNode->Type == SOURCE ? "SOURCE" :
  442. pNewNode->Type == DESTINATION ? "DEST" :
  443. pNewNode->Type == NODE ? "NODE" :
  444. "Huh?",
  445. pNewNode->Id,
  446. pNewNode ) );
  447. kmxlAddToParentList( pNewNode, pPeerNode );
  448. }
  449. //
  450. // Skip past the connection we just processed.
  451. //
  452. ++Index;
  453. } // Loop until FindConnection fails.
  454. //
  455. // The last connection found connects to a destination node. Do not
  456. // try to enumerate the children, since there are none.
  457. //
  458. if( bEndOfTheLine ) {
  459. RETURN( Status );
  460. }
  461. //
  462. // For each of the children of this node, recurse to build up the lists
  463. // of the child's nodes.
  464. //
  465. pPeerNode = kmxlFirstChildNode( pNode );
  466. while( pPeerNode ) {
  467. Status = kmxlBuildChildGraph(
  468. pmxobj,
  469. listDests, // The list of destination nodes
  470. pPeerNode->pNode, // The parent node
  471. pPeerNode->pNode->Id, // The Id of the parent
  472. PINID_WILDCARD // Look for any connection by this node
  473. );
  474. if (!NT_SUCCESS(Status)) {
  475. break;
  476. }
  477. pPeerNode = kmxlNextPeerNode( pPeerNode );
  478. }
  479. RETURN( Status );
  480. }
  481. ///////////////////////////////////////////////////////////////////////
  482. //
  483. // BuildNodeTable
  484. //
  485. // Allocates enough memory to hold TopologyNodeCount MXLNODE structures.
  486. // The GUIDs from the Topology are copied over into the MXLNODE structures.
  487. //
  488. //
  489. PMXLNODE
  490. kmxlBuildNodeTable(
  491. IN PKSTOPOLOGY pTopology // The topology structure
  492. )
  493. {
  494. PMXLNODE pTable = NULL;
  495. ULONG i;
  496. ASSERT( pTopology );
  497. PAGED_CODE();
  498. //
  499. // If we don't have any node count, we don't want to allocate a zero byte buffer.
  500. // simply return the error case.
  501. //
  502. if( 0 == pTopology->TopologyNodesCount )
  503. {
  504. return NULL;
  505. }
  506. //
  507. // Allocate an array of nodes the same size as the Topology Node
  508. // table.
  509. //
  510. if( !NT_SUCCESS( AudioAllocateMemory_Paged(pTopology->TopologyNodesCount * sizeof( MXLNODE ),
  511. TAG_AudN_NODE,
  512. ZERO_FILL_MEMORY,
  513. &pTable) ) )
  514. {
  515. return( NULL );
  516. }
  517. //
  518. // Initialize the nodes. All the can be filled in here is the GUIDs,
  519. // copied from the node table.
  520. //
  521. for( i = 0; i < pTopology->TopologyNodesCount; i++ ) {
  522. pTable[ i ].NodeType = pTopology->TopologyNodes[ i ];
  523. }
  524. return( pTable );
  525. }
  526. ///////////////////////////////////////////////////////////////////////
  527. //
  528. // FindTopologyConnection
  529. //
  530. // Scans through the connection table looking for a connection that
  531. // matches the FromNode/FromNodePin criteria.
  532. //
  533. //
  534. ULONG
  535. kmxlFindTopologyConnection(
  536. IN PMIXEROBJECT pmxobj,
  537. IN ULONG StartIndex, // Index to start search
  538. IN ULONG FromNode, // The Node ID to look for
  539. IN ULONG FromNodePin // The Pin ID to look for
  540. )
  541. {
  542. ULONG i;
  543. PAGED_CODE();
  544. for( i = StartIndex; i < pmxobj->pTopology->TopologyConnectionsCount; i++ ) {
  545. if( ( ( pmxobj->pTopology->TopologyConnections[ i ].FromNode == FromNode )||
  546. ( FromNode == PINID_WILDCARD ) ) &&
  547. ( ( pmxobj->pTopology->TopologyConnections[ i ].FromNodePin == FromNodePin ) ||
  548. ( FromNodePin == PINID_WILDCARD ) ) ) {
  549. //#ifdef PARSE_TRACE
  550. //TRACE( "WDMAUD: Found connection from (%d,%d) -> %d.\n",
  551. // FromNode, FromNodePin, i );
  552. //#endif
  553. return( i );
  554. }
  555. }
  556. return( (ULONG) -1 );
  557. }
  558. ///////////////////////////////////////////////////////////////////////
  559. //
  560. // kmxlGetProperty
  561. //
  562. // Queries a property by first determining the correct number of
  563. // output bytes, allocating that much memory, and quering the
  564. // actual data.
  565. //
  566. //
  567. NTSTATUS
  568. kmxlGetProperty(
  569. PFILE_OBJECT pFileObject, // The instance of the filter
  570. CONST GUID *pguidPropertySet, // The requested property set
  571. ULONG ulPropertyId, // The ID of the specific property
  572. ULONG cbInput, // The number of extra input bytes
  573. PVOID pInputData, // Pointer to the extra input bytes
  574. ULONG Flags, // Additional flags
  575. PVOID *ppPropertyOutput // Pointer to a pointer of the output
  576. )
  577. {
  578. ULONG BytesReturned;
  579. ULONG cbPropertyInput = sizeof(KSPROPERTY);
  580. PKSPROPERTY pPropertyInput = NULL;
  581. NTSTATUS Status;
  582. PAGED_CODE();
  583. ASSERT( pFileObject );
  584. //
  585. // Allocate enough memory for the KSPROPERTY structure and any additional
  586. // input the callers wants to include.
  587. //
  588. cbPropertyInput += cbInput;
  589. Status = AudioAllocateMemory_Paged(cbPropertyInput,
  590. TAG_AudV_PROPERTY,
  591. ZERO_FILL_MEMORY,
  592. &pPropertyInput );
  593. if(!NT_SUCCESS(Status)) {
  594. goto exit;
  595. }
  596. //
  597. // Set up the field of the KSPROPERTY structure
  598. //
  599. pPropertyInput->Set = *pguidPropertySet;
  600. pPropertyInput->Id = ulPropertyId;
  601. pPropertyInput->Flags = KSPROPERTY_TYPE_GET | Flags;
  602. //
  603. // Copy the additional input from the caller.
  604. //
  605. if(pInputData != NULL) {
  606. RtlCopyMemory(pPropertyInput + 1, pInputData, cbInput);
  607. }
  608. //
  609. // This first call will query the number of bytes the output needs.
  610. //
  611. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY pPropertyInput=%X",pPropertyInput) );
  612. Status = KsSynchronousIoControlDevice(
  613. pFileObject,
  614. KernelMode,
  615. IOCTL_KS_PROPERTY,
  616. pPropertyInput,
  617. cbPropertyInput,
  618. NULL,
  619. 0,
  620. &BytesReturned
  621. );
  622. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Status=%X",Status) );
  623. ASSERT(!NT_SUCCESS(Status));
  624. if(Status != STATUS_BUFFER_OVERFLOW) {
  625. goto exit;
  626. }
  627. if(BytesReturned == 0) {
  628. *ppPropertyOutput = NULL;
  629. Status = STATUS_SUCCESS;
  630. goto exit;
  631. }
  632. //
  633. // Allocate enough memory to hold all of the output.
  634. //
  635. Status = AudioAllocateMemory_Paged(BytesReturned,
  636. TAG_Audv_PROPERTY,
  637. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  638. ppPropertyOutput );
  639. if(!NT_SUCCESS(Status)) {
  640. goto exit;
  641. }
  642. //
  643. // Now actually get the output data.
  644. //
  645. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY pPropertyInput=%X",pPropertyInput) );
  646. Status = KsSynchronousIoControlDevice(
  647. pFileObject,
  648. KernelMode,
  649. IOCTL_KS_PROPERTY,
  650. pPropertyInput,
  651. cbPropertyInput,
  652. *ppPropertyOutput,
  653. BytesReturned,
  654. &BytesReturned
  655. );
  656. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Status=%X",Status) );
  657. if(!NT_SUCCESS(Status)) {
  658. AudioFreeMemory_Unknown(ppPropertyOutput);
  659. goto exit;
  660. }
  661. exit:
  662. AudioFreeMemory_Unknown(&pPropertyInput);
  663. if(!NT_SUCCESS(Status)) {
  664. *ppPropertyOutput = NULL;
  665. DPF(DL_WARNING|FA_USER,("Failed to get Property Status=%X",Status) );
  666. }
  667. RETURN(Status);
  668. }
  669. ///////////////////////////////////////////////////////////////////////
  670. //
  671. // kmxlNodeProperty
  672. //
  673. // Creates a KSNODEPROPERTY structure with additional input data
  674. // after it and uses KsSychronousIoControlDevice() to query or set the
  675. // property. Only memory for the input is allocated here.
  676. //
  677. //
  678. NTSTATUS
  679. kmxlNodeProperty(
  680. IN PFILE_OBJECT pFileObject, // Instance of the filter owning node
  681. IN CONST GUID* pguidPropertySet, // The GUID of the property set
  682. IN ULONG ulPropertyId, // The specific property in the set
  683. IN ULONG ulNodeId, // The virtual node id
  684. IN ULONG cbInput, // # of extra input bytes
  685. IN PVOID pInputData, // Pointer to the extra input bytes
  686. OUT PVOID pPropertyOutput, // Pointer to the output data
  687. IN ULONG cbPropertyOutput, // Size of the output data buffer
  688. IN ULONG Flags // KSPROPERTY_TYPE_GET or SET
  689. )
  690. {
  691. NTSTATUS Status;
  692. KSNODEPROPERTY NodeProperty;
  693. ULONG cbPropertyIn = sizeof( KSNODEPROPERTY );
  694. PKSNODEPROPERTY pInData = NULL;
  695. ULONG BytesReturned;
  696. PAGED_CODE();
  697. ASSERT( pFileObject );
  698. ASSERT( pguidPropertySet );
  699. if( cbInput > 0 ) {
  700. //
  701. // If the caller passed in some extra input, add that size
  702. // to the size of the required KSNODEPROPERTY and allocate
  703. // a chunk of memory.
  704. //
  705. cbPropertyIn += cbInput;
  706. Status = AudioAllocateMemory_Paged(cbPropertyIn,
  707. TAG_AudU_PROPERTY,
  708. ZERO_FILL_MEMORY,
  709. &pInData );
  710. if( !NT_SUCCESS( Status ) ) {
  711. goto exit;
  712. }
  713. RtlCopyMemory( pInData + 1, pInputData, cbInput );
  714. } else {
  715. pInData = &NodeProperty;
  716. }
  717. //
  718. // Fill in the property and node information.
  719. //
  720. pInData->Property.Set = *pguidPropertySet;
  721. pInData->Property.Id = ulPropertyId;
  722. pInData->Property.Flags = Flags |
  723. KSPROPERTY_TYPE_TOPOLOGY;
  724. pInData->NodeId = ulNodeId;
  725. pInData->Reserved = 0;
  726. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY pInData=%X",pInData) );
  727. Status = KsSynchronousIoControlDevice(
  728. pFileObject, // The FILE_OBJECT for SysAudio
  729. KernelMode, // Call originates in Kernel mode
  730. IOCTL_KS_PROPERTY, // KS PROPERTY IOCTL
  731. pInData, // Pointer to the KSNODEPROPERTY struct
  732. cbPropertyIn, // Number or bytes input
  733. pPropertyOutput, // Pointer to the buffer to store output
  734. cbPropertyOutput, // Size of the output buffer
  735. &BytesReturned // Number of bytes returned from the call
  736. );
  737. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Status=%X",Status) );
  738. if(!NT_SUCCESS(Status)) {
  739. goto exit;
  740. }
  741. exit:
  742. //
  743. // If the user passed in extra byte, we allocated memory to hold them.
  744. // Now the memory must be deallocated.
  745. //
  746. if( cbInput > 0 ) {
  747. AudioFreeMemory_Unknown( &pInData );
  748. }
  749. RETURN( Status );
  750. }
  751. ///////////////////////////////////////////////////////////////////////
  752. //
  753. // kmxlAudioNodeProperty
  754. //
  755. // Similar to kmxlNodeProperty except for the property set is assumed
  756. // to be KSPROPSETID_Audio and a KSNODEPROPERTY_AUDIO_CHANNEL structure
  757. // is used instead of KSNODEPROPERTY to allow channel selection.
  758. //
  759. //
  760. NTSTATUS
  761. kmxlAudioNodeProperty(
  762. IN PFILE_OBJECT pfo, // Instance of the filter owning node
  763. IN ULONG ulPropertyId, // The audio property to get
  764. IN ULONG ulNodeId, // The virtual node id
  765. IN LONG lChannel, // The channel number
  766. IN PVOID pInData, // Pointer to extra input bytes
  767. IN ULONG cbInData, // Number of extra input bytes
  768. OUT PVOID pOutData, // Pointer to output buffer
  769. IN LONG cbOutData, // Size of the output buffer
  770. IN ULONG Flags // KSPROPERTY_TYPE_GET or SET
  771. )
  772. {
  773. NTSTATUS Status;
  774. KSNODEPROPERTY_AUDIO_CHANNEL Channel;
  775. PKSNODEPROPERTY_AUDIO_CHANNEL pInput = NULL;
  776. ULONG cbInput;
  777. ULONG BytesReturned;
  778. PAGED_CODE();
  779. ASSERT( pfo );
  780. //
  781. // Determine the minimum number of input bytes
  782. //
  783. cbInput = sizeof( KSNODEPROPERTY_AUDIO_CHANNEL );
  784. //
  785. // If the caller passed in additional data, allocate enough memory
  786. // to hold the KSNODEPROPERTY_AUDIO_CHANNEL plus the input bytes
  787. // and copy the input bytes into the new memory immediately after
  788. // the KSNODEPROPERTY_AUDIO_CHANNEL structure.
  789. //
  790. if( cbInData > 0 ) {
  791. cbInput += cbInData;
  792. Status = AudioAllocateMemory_Paged(cbInput,
  793. TAG_Audu_PROPERTY,
  794. ZERO_FILL_MEMORY,
  795. &pInput );
  796. if( !NT_SUCCESS( Status ) ) {
  797. goto exit;
  798. }
  799. RtlCopyMemory( pInput + 1, pInData, cbInData );
  800. } else {
  801. //
  802. // Memory saving hack... if the user didn't give any additional
  803. // bytes, just point to memory on the stack.
  804. //
  805. pInput = &Channel;
  806. }
  807. //
  808. // Fill in the property fields.
  809. //
  810. pInput->NodeProperty.Property.Set = KSPROPSETID_Audio;
  811. pInput->NodeProperty.Property.Id = ulPropertyId;
  812. pInput->NodeProperty.Property.Flags = Flags |
  813. KSPROPERTY_TYPE_TOPOLOGY;
  814. //
  815. // Fill in the node details.
  816. //
  817. pInput->NodeProperty.NodeId = ulNodeId;
  818. pInput->NodeProperty.Reserved = 0;
  819. //
  820. // Fill in the channel details.
  821. //
  822. pInput->Channel = lChannel;
  823. pInput->Reserved = 0;
  824. //
  825. // And execute the property.
  826. //
  827. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY pInput=%X",pInput) );
  828. Status = KsSynchronousIoControlDevice(
  829. pfo, // The FILE_OBJECT for SysAudio
  830. KernelMode, // Call originates in Kernel mode
  831. IOCTL_KS_PROPERTY, // KS PROPERTY IOCTL
  832. pInput, // Pointer to the KSNODEPROPERTY struct
  833. cbInput, // Number or bytes input
  834. pOutData, // Pointer to the buffer to store output
  835. cbOutData, // Size of the output buffer
  836. &BytesReturned // Number of bytes returned from the call
  837. );
  838. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  839. if(!NT_SUCCESS(Status)) {
  840. goto exit;
  841. }
  842. exit:
  843. //
  844. // If the user passed in extra bytes, we allocated memory to hold them.
  845. // Now the memory must be deallocated.
  846. //
  847. if( cbInData > 0 ) {
  848. AudioFreeMemory_Unknown( &pInData );
  849. }
  850. RETURN( Status );
  851. }
  852. ///////////////////////////////////////////////////////////////////////
  853. //
  854. // kmxlGetPinName
  855. //
  856. // Calls GetPinPropertyEx to guery and allocate memory for the pin
  857. // name. If that call fails, a default name is copy based on the
  858. // pin type.
  859. //
  860. // The short name is made identical to the long name, but using only
  861. // the first sizeof( szShortName ) / sizeof( WCHAR ) characters.
  862. //
  863. //
  864. VOID
  865. kmxlGetPinName(
  866. IN PFILE_OBJECT pfo, // Instance of the owning filter
  867. IN ULONG PinId, // Id of the pin
  868. IN PMXLLINE pLine // The line to store the name into
  869. )
  870. {
  871. WCHAR* szName = NULL;
  872. NTSTATUS Status;
  873. KSP_PIN Pin;
  874. ULONG BytesReturned = 0;
  875. ULONG BytesReturned2 = 0;
  876. PAGED_CODE();
  877. Pin.Property.Set = KSPROPSETID_Pin;
  878. Pin.Property.Id = KSPROPERTY_PIN_NAME;
  879. Pin.Property.Flags = KSPROPERTY_TYPE_GET;
  880. Pin.PinId = PinId;
  881. Pin.Reserved = 0;
  882. //
  883. // Query to see how many bytes of storage we need to allocate.
  884. // Note that the pointer and number of bytes must both be zero
  885. // or this will fail!
  886. //
  887. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Pin=%X",&Pin) );
  888. Status = KsSynchronousIoControlDevice(
  889. pfo,
  890. KernelMode,
  891. IOCTL_KS_PROPERTY,
  892. &Pin,
  893. sizeof(KSP_PIN),
  894. NULL,
  895. 0,
  896. &BytesReturned
  897. );
  898. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  899. ASSERT(!NT_SUCCESS(Status));
  900. if( Status != STATUS_BUFFER_OVERFLOW ) {
  901. goto exit;
  902. }
  903. //
  904. // Allocate what was returned.
  905. //
  906. Status = AudioAllocateMemory_Paged(BytesReturned,
  907. TAG_Audp_NAME,
  908. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  909. &szName );
  910. if( !NT_SUCCESS( Status ) ) {
  911. DPF(DL_WARNING|FA_USER,("Setting Default szName") );
  912. goto exit;
  913. }
  914. //
  915. // Call again to get the pin name.
  916. //
  917. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Pin=%X",&Pin) );
  918. BytesReturned2=BytesReturned;
  919. Status = KsSynchronousIoControlDevice(
  920. pfo,
  921. KernelMode,
  922. IOCTL_KS_PROPERTY,
  923. &Pin,
  924. sizeof(KSP_PIN),
  925. szName,
  926. BytesReturned2,
  927. &BytesReturned2
  928. );
  929. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  930. //
  931. // If successful, copy as much of the name that will fit into the
  932. // short name and name fields of the line.
  933. //
  934. if( NT_SUCCESS( Status ) && szName ) {
  935. #ifdef DEBUG
  936. //
  937. // There is no good reason that I can think of for a driver to return
  938. // a different return value the second time it's called. That would just
  939. // be stupid.
  940. //
  941. if( BytesReturned != BytesReturned2 )
  942. {
  943. DPF(DL_WARNING|FA_SYSAUDIO,("Unequal returns! BR=%08x,BR2=%08x",BytesReturned,BytesReturned2));
  944. }
  945. //
  946. // Let's explicitly look for the case that made this driver fault. The
  947. // BytesReturned value was 8 and it contained MUX\0 in the buffer. The problem
  948. // was that wcsncpy walked MIXER_SHORT_NAME_CHARS number of characters.
  949. // Thus it walked off the end of the source buffer.
  950. //
  951. if( (BytesReturned/sizeof(WCHAR) < MIXER_SHORT_NAME_CHARS) &&
  952. (szName[BytesReturned/sizeof(WCHAR)-1] != (WCHAR)NULL) )
  953. {
  954. DPF(DL_ERROR|FA_SYSAUDIO,("Hit short name assert! BR=%08x",BytesReturned));
  955. }
  956. #endif
  957. wcsncpy(
  958. pLine->Line.szShortName,
  959. szName,
  960. min(BytesReturned/sizeof(WCHAR),MIXER_SHORT_NAME_CHARS)
  961. );
  962. pLine->Line.szShortName[ min(BytesReturned/sizeof(WCHAR),MIXER_SHORT_NAME_CHARS) - 1 ] = (WCHAR)NULL;
  963. wcsncpy(
  964. pLine->Line.szName,
  965. szName,
  966. min(BytesReturned/sizeof(WCHAR),MIXER_LONG_NAME_CHARS) );
  967. pLine->Line.szName[ min(BytesReturned/sizeof(WCHAR),MIXER_LONG_NAME_CHARS) - 1 ] = (WCHAR)NULL;
  968. AudioFreeMemory_Unknown( &szName );
  969. return;
  970. }
  971. AudioFreeMemory_Unknown( &szName );
  972. exit:
  973. //
  974. // The pin doesn't support the property. Copy in a good default.
  975. //
  976. CopyAnsiStringtoUnicodeString(
  977. pLine->Line.szName,
  978. PinCategoryToString( &pLine->Type ),
  979. min(MIXER_LONG_NAME_CHARS, strlen(PinCategoryToString(&pLine->Type)) + 1)
  980. );
  981. wcsncpy(
  982. pLine->Line.szShortName,
  983. pLine->Line.szName,
  984. MIXER_SHORT_NAME_CHARS
  985. );
  986. pLine->Line.szShortName[ MIXER_SHORT_NAME_CHARS - 1 ] = 0x00;
  987. }
  988. ///////////////////////////////////////////////////////////////////////
  989. //
  990. // kmxlGetNodeName
  991. //
  992. // Retrieves the name of a node (control).
  993. //
  994. //
  995. VOID
  996. kmxlGetNodeName(
  997. IN PFILE_OBJECT pfo, // Instance of the owning filter
  998. IN ULONG NodeId, // The node id
  999. IN PMXLCONTROL pControl // The control to store the name
  1000. )
  1001. {
  1002. NTSTATUS Status;
  1003. LONG cbName=0;
  1004. WCHAR* szName = NULL;
  1005. KSNODEPROPERTY NodeProperty;
  1006. PAGED_CODE();
  1007. ASSERT( pfo );
  1008. ASSERT( pControl );
  1009. //
  1010. // Query the number of bytes the node name is
  1011. //
  1012. NodeProperty.Property.Set = KSPROPSETID_Topology;
  1013. NodeProperty.Property.Id = KSPROPERTY_TOPOLOGY_NAME;
  1014. NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET |
  1015. KSPROPERTY_TYPE_TOPOLOGY;
  1016. NodeProperty.NodeId = NodeId;
  1017. NodeProperty.Reserved = 0;
  1018. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Node=%X",&NodeProperty) );
  1019. Status = KsSynchronousIoControlDevice(
  1020. pfo,
  1021. KernelMode,
  1022. IOCTL_KS_PROPERTY,
  1023. &NodeProperty,
  1024. sizeof( NodeProperty ),
  1025. NULL,
  1026. 0,
  1027. &cbName
  1028. );
  1029. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  1030. if( ( Status == STATUS_BUFFER_OVERFLOW ) ||
  1031. ( Status == STATUS_BUFFER_TOO_SMALL ) ) {
  1032. //
  1033. // Allocate enough space to hold the entire name
  1034. //
  1035. if( !NT_SUCCESS( AudioAllocateMemory_Paged(cbName,
  1036. TAG_Audp_NAME,
  1037. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  1038. &szName ) ) )
  1039. {
  1040. goto exit;
  1041. }
  1042. ASSERT( szName );
  1043. //
  1044. // Requery for the name with the previously allocated buffer.
  1045. //
  1046. Status = kmxlNodeProperty(
  1047. pfo,
  1048. &KSPROPSETID_Topology,
  1049. KSPROPERTY_TOPOLOGY_NAME,
  1050. NodeId,
  1051. 0,
  1052. NULL,
  1053. szName,
  1054. cbName,
  1055. KSPROPERTY_TYPE_GET
  1056. );
  1057. if( NT_SUCCESS( Status ) && szName ) {
  1058. //
  1059. // Copy the names retrieved into the szShortName and Name
  1060. // fields of the control. The short name is just a shortened
  1061. // version of the full name.
  1062. //
  1063. //
  1064. // Note: cbName is a byte value and wcsncpy takes a count of characters,
  1065. // We are dealing with wide characters, thus we must adjust the
  1066. // memory size to characters! Note that the driver could have
  1067. // returned a source buffer less then MIXER_SHORT_NAME_CHARS in length!
  1068. //
  1069. #ifdef DEBUG
  1070. if( (cbName/sizeof(WCHAR) < MIXER_SHORT_NAME_CHARS) &&
  1071. (szName[cbName/sizeof(WCHAR)-1] != (WCHAR)NULL) )
  1072. {
  1073. DPF(DL_ERROR|FA_SYSAUDIO,("Hit short name assert! cbName=%08x",cbName));
  1074. }
  1075. #endif
  1076. wcsncpy(
  1077. pControl->Control.szShortName,
  1078. szName,
  1079. min(cbName/sizeof(WCHAR),MIXER_SHORT_NAME_CHARS)
  1080. );
  1081. pControl->Control.szShortName[ min(cbName/sizeof(WCHAR),MIXER_SHORT_NAME_CHARS) - 1 ] = (WCHAR)NULL;
  1082. wcsncpy(
  1083. pControl->Control.szName,
  1084. szName,
  1085. min(cbName/sizeof(WCHAR),MIXER_LONG_NAME_CHARS) );
  1086. pControl->Control.szName[ min(cbName/sizeof(WCHAR),MIXER_LONG_NAME_CHARS) - 1 ] = (WCHAR)NULL;
  1087. AudioFreeMemory_Unknown( &szName );
  1088. return;
  1089. }
  1090. }
  1091. //
  1092. // Looks like we might leak memory on the error condition. See
  1093. // kmxlGetPinName above!
  1094. //
  1095. AudioFreeMemory_Unknown( &szName );
  1096. exit:
  1097. //
  1098. // The node doesn't support the property. Copy in a good default.
  1099. //
  1100. CopyAnsiStringtoUnicodeString(
  1101. pControl->Control.szName,
  1102. NodeTypeToString( pControl->NodeType ),
  1103. min(MIXER_LONG_NAME_CHARS, strlen(NodeTypeToString(pControl->NodeType)) + 1)
  1104. );
  1105. wcsncpy(
  1106. pControl->Control.szShortName,
  1107. pControl->Control.szName,
  1108. MIXER_SHORT_NAME_CHARS
  1109. );
  1110. pControl->Control.szShortName[ MIXER_SHORT_NAME_CHARS - 1 ] = 0x00;
  1111. }
  1112. ///////////////////////////////////////////////////////////////////////
  1113. //
  1114. // kmxlGetSuperMixCaps
  1115. //
  1116. //
  1117. NTSTATUS
  1118. kmxlGetSuperMixCaps(
  1119. IN PFILE_OBJECT pfo,
  1120. IN ULONG ulNodeId,
  1121. OUT PKSAUDIO_MIXCAP_TABLE* paMixCaps
  1122. )
  1123. {
  1124. NTSTATUS Status;
  1125. ULONG Size;
  1126. struct {
  1127. ULONG InputChannels;
  1128. ULONG OutputChannels;
  1129. } SuperMixSize;
  1130. PKSAUDIO_MIXCAP_TABLE pMixCaps = NULL;
  1131. PAGED_CODE();
  1132. ASSERT( pfo );
  1133. ASSERT( paMixCaps );
  1134. *paMixCaps = NULL;
  1135. //
  1136. // Query the node with just the first 2 DWORDs of the MIXCAP table.
  1137. // This will return the dimensions of the supermixer.
  1138. //
  1139. Status = kmxlNodeProperty(
  1140. pfo,
  1141. &KSPROPSETID_Audio,
  1142. KSPROPERTY_AUDIO_MIX_LEVEL_CAPS,
  1143. ulNodeId,
  1144. 0,
  1145. NULL,
  1146. &SuperMixSize,
  1147. sizeof( SuperMixSize ),
  1148. KSPROPERTY_TYPE_GET
  1149. );
  1150. if( !NT_SUCCESS( Status ) ) {
  1151. DPF(DL_WARNING|FA_MIXER,( "kmxlNodeProperty failed with %X!", Status ) );
  1152. RETURN( Status );
  1153. }
  1154. //
  1155. // Allocate a MIXCAPS table big enough to hold all the entires.
  1156. // The size needs to include the first 2 DWORDs in the MIXCAP
  1157. // table besides the array ( InputCh * OutputCh ) of MIXCAPs
  1158. //
  1159. Size = sizeof( SuperMixSize ) +
  1160. SuperMixSize.InputChannels * SuperMixSize.OutputChannels *
  1161. sizeof( KSAUDIO_MIX_CAPS );
  1162. Status = AudioAllocateMemory_Paged(Size,
  1163. TAG_AudS_SUPERMIX,
  1164. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  1165. &pMixCaps );
  1166. if( !NT_SUCCESS( Status ) ) {
  1167. DPF(DL_WARNING|FA_MIXER,( "failed to allocate caps memory!" ) );
  1168. RETURN( Status );
  1169. }
  1170. //
  1171. // Query the node once again to fill in the MIXCAPS structures.
  1172. //
  1173. Status = kmxlNodeProperty(
  1174. pfo,
  1175. &KSPROPSETID_Audio,
  1176. KSPROPERTY_AUDIO_MIX_LEVEL_CAPS,
  1177. ulNodeId,
  1178. 0,
  1179. NULL,
  1180. pMixCaps,
  1181. Size,
  1182. KSPROPERTY_TYPE_GET
  1183. );
  1184. if( !NT_SUCCESS( Status ) ) {
  1185. DPF(DL_WARNING|FA_PROPERTY ,( "kmxlNodeProperty failed with %X!", Status ) );
  1186. AudioFreeMemory( Size,&pMixCaps );
  1187. RETURN( Status );
  1188. }
  1189. *paMixCaps = pMixCaps;
  1190. RETURN( Status );
  1191. }
  1192. ///////////////////////////////////////////////////////////////////////
  1193. //
  1194. // kmxlQueryPropertyRange
  1195. //
  1196. //
  1197. NTSTATUS
  1198. kmxlQueryPropertyRange(
  1199. IN PFILE_OBJECT pfo,
  1200. IN CONST GUID* pguidPropSet,
  1201. IN ULONG ulPropertyId,
  1202. IN ULONG ulNodeId,
  1203. OUT PKSPROPERTY_DESCRIPTION* ppPropDesc
  1204. )
  1205. {
  1206. NTSTATUS Status;
  1207. KSNODEPROPERTY NodeProperty;
  1208. KSPROPERTY_DESCRIPTION PropertyDescription;
  1209. PKSPROPERTY_DESCRIPTION pPropDesc = NULL;
  1210. ULONG BytesReturned;
  1211. PAGED_CODE();
  1212. //
  1213. // We don't want to allocate some arbitrary memory size if the driver
  1214. // does not set this value.
  1215. //
  1216. PropertyDescription.DescriptionSize=0;
  1217. NodeProperty.Property.Set = *pguidPropSet;
  1218. NodeProperty.Property.Id = ulPropertyId;
  1219. NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT |
  1220. KSPROPERTY_TYPE_TOPOLOGY;
  1221. NodeProperty.NodeId = ulNodeId;
  1222. NodeProperty.Reserved = 0;
  1223. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Query Node=%X",&NodeProperty) );
  1224. Status = KsSynchronousIoControlDevice(
  1225. pfo,
  1226. KernelMode,
  1227. IOCTL_KS_PROPERTY,
  1228. &NodeProperty,
  1229. sizeof( NodeProperty ),
  1230. &PropertyDescription,
  1231. sizeof( PropertyDescription ),
  1232. &BytesReturned
  1233. );
  1234. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  1235. if( !NT_SUCCESS( Status ) ) {
  1236. RETURN( Status );
  1237. }
  1238. //
  1239. // Never use a buffer that is smaller then we think it should be!
  1240. //
  1241. if( PropertyDescription.DescriptionSize < sizeof(KSPROPERTY_DESCRIPTION) )
  1242. {
  1243. #ifdef DEBUG
  1244. DPF(DL_ERROR|FA_ALL,("KSPROPERTY_DESCRIPTION.DescriptionSize!>=sizeof(KSPROPERTY_DESCRIPTION)") );
  1245. #endif
  1246. RETURN(STATUS_INVALID_PARAMETER);
  1247. }
  1248. Status = AudioAllocateMemory_Paged(PropertyDescription.DescriptionSize,
  1249. TAG_Auda_PROPERTY,
  1250. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  1251. &pPropDesc );
  1252. if( !NT_SUCCESS( Status ) ) {
  1253. RETURN( Status );
  1254. }
  1255. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Get Node=%X",&NodeProperty) );
  1256. Status = KsSynchronousIoControlDevice(
  1257. pfo,
  1258. KernelMode,
  1259. IOCTL_KS_PROPERTY,
  1260. &NodeProperty,
  1261. sizeof( NodeProperty ),
  1262. pPropDesc,
  1263. PropertyDescription.DescriptionSize,
  1264. &BytesReturned
  1265. );
  1266. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  1267. if( !NT_SUCCESS( Status ) ) {
  1268. AudioFreeMemory( PropertyDescription.DescriptionSize,&pPropDesc );
  1269. RETURN( Status );
  1270. }
  1271. *ppPropDesc = pPropDesc;
  1272. return( STATUS_SUCCESS );
  1273. }
  1274. ///////////////////////////////////////////////////////////////////////
  1275. //
  1276. // kmxlGetControlChannels
  1277. //
  1278. //
  1279. NTSTATUS
  1280. kmxlGetControlChannels(
  1281. IN PFILE_OBJECT pfo,
  1282. IN PMXLCONTROL pControl
  1283. )
  1284. {
  1285. NTSTATUS Status;
  1286. PKSPROPERTY_DESCRIPTION pPropDesc = NULL;
  1287. PKSPROPERTY_MEMBERSHEADER pMemberHeader;
  1288. PCHANNEL_STEPPING pChannelStepping;
  1289. ULONG i;
  1290. PAGED_CODE();
  1291. Status = kmxlQueryPropertyRange(
  1292. pfo,
  1293. &KSPROPSETID_Audio,
  1294. pControl->PropertyId,
  1295. pControl->Id,
  1296. &pPropDesc
  1297. );
  1298. //
  1299. // Do some checking on the returned value. Look for things that we
  1300. // support.
  1301. //
  1302. if ( NT_SUCCESS(Status) ) {
  1303. ASSERT(pPropDesc);
  1304. pMemberHeader = (PKSPROPERTY_MEMBERSHEADER) ( pPropDesc + 1 );
  1305. #ifdef DEBUG
  1306. //
  1307. // If the MembersListCount is greater then zero and the GUID's are equal
  1308. // then we will reference the pMemberHeader value that we create here.
  1309. // If we do, then we must make sure that the memory that we allocated
  1310. // is large enough to handle it!
  1311. //
  1312. if( ( pPropDesc->MembersListCount > 0 ) &&
  1313. (IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General )) )
  1314. {
  1315. //
  1316. // if this is the case, we will touch the pMemberHeader->MembersCount
  1317. // field.
  1318. //
  1319. if (pPropDesc->DescriptionSize < (sizeof(KSPROPERTY_DESCRIPTION) +
  1320. sizeof(KSPROPERTY_MEMBERSHEADER)) )
  1321. {
  1322. DPF(DL_ERROR|FA_ALL,("Incorrectly reported DescriptionSize in KSPROPERTY_DESCRIPTION structure") );
  1323. RETURN(STATUS_INVALID_PARAMETER);
  1324. }
  1325. }
  1326. #endif
  1327. }
  1328. if( ( NT_SUCCESS( Status ) ) &&
  1329. ( pPropDesc->MembersListCount > 0 ) &&
  1330. ( IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General )) &&
  1331. ( pMemberHeader->MembersCount > 0 ) &&
  1332. ( pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL ) )
  1333. {
  1334. //
  1335. // Volume controls may either be of MIXERTYPE_CONTROLF_UNIFORM
  1336. // or not. Uniform controls adjust all channels (or are mono
  1337. // in the first place) with one control. Those that have the
  1338. // fdwControl field set to 0 can set all channels of the volume
  1339. // independently. This information will have to come from the
  1340. // node itself, by checking to see if the node uniform control.
  1341. //
  1342. pControl->NumChannels = pMemberHeader->MembersCount;
  1343. if( (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM) ||
  1344. (pMemberHeader->MembersCount == 1) ) {
  1345. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  1346. }
  1347. }
  1348. else {
  1349. // Fall through to using the old method which checks if volume is supported on
  1350. // each channel one at a time
  1351. Status = kmxlSupportsMultiChannelControl(pfo,
  1352. pControl->Id,
  1353. pControl->PropertyId);
  1354. if (NT_SUCCESS(Status)) {
  1355. pControl->NumChannels = 2; // we have stereo
  1356. pControl->Control.fdwControl = 0;
  1357. } else {
  1358. pControl->NumChannels = 1; // we have mono or master channel
  1359. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  1360. }
  1361. }
  1362. // Done with the pPropDesc
  1363. AudioFreeMemory_Unknown( &pPropDesc );
  1364. ASSERT(pControl->NumChannels > 0);
  1365. ASSERT(pControl->pChannelStepping == NULL);
  1366. Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ),
  1367. TAG_AuDB_CHANNEL,
  1368. ZERO_FILL_MEMORY,
  1369. &pControl->pChannelStepping );
  1370. if( !NT_SUCCESS( Status ) ) {
  1371. pControl->NumChannels = 0;
  1372. return( Status );
  1373. }
  1374. // For a failure, set the default range.
  1375. pChannelStepping = pControl->pChannelStepping;
  1376. for (i = 0; i < pControl->NumChannels; i++, pChannelStepping++) {
  1377. pChannelStepping->MinValue = DEFAULT_RANGE_MIN;
  1378. pChannelStepping->MaxValue = DEFAULT_RANGE_MAX;
  1379. pChannelStepping->Steps = DEFAULT_RANGE_STEPS;
  1380. }
  1381. return( STATUS_SUCCESS );
  1382. }
  1383. ///////////////////////////////////////////////////////////////////////
  1384. //
  1385. // kmxlGetControlRange
  1386. //
  1387. //
  1388. NTSTATUS
  1389. kmxlGetControlRange(
  1390. IN PFILE_OBJECT pfo,
  1391. IN PMXLCONTROL pControl
  1392. )
  1393. {
  1394. NTSTATUS Status;
  1395. PKSPROPERTY_DESCRIPTION pPropDesc;
  1396. PKSPROPERTY_MEMBERSHEADER pMemberHeader;
  1397. PKSPROPERTY_STEPPING_LONG pSteppingLong;
  1398. PCHANNEL_STEPPING pChannelStepping;
  1399. ULONG i;
  1400. PAGED_CODE();
  1401. //
  1402. // Query the range for this control and initialize pControl in case of failure
  1403. //
  1404. ASSERT( pControl->pChannelStepping == NULL );
  1405. pControl->pChannelStepping = NULL;
  1406. Status = kmxlQueryPropertyRange(
  1407. pfo,
  1408. &KSPROPSETID_Audio,
  1409. pControl->PropertyId,
  1410. pControl->Id,
  1411. &pPropDesc
  1412. );
  1413. if( !NT_SUCCESS( Status ) ) {
  1414. DPF(DL_WARNING|FA_MIXER,( "Failed to get BASICSUPPORT on control %x!", pControl ) );
  1415. // If BASICSUPPORT fails, kmxlGetControlChannels to handle the default behavior
  1416. Status = kmxlGetControlChannels( pfo, pControl );
  1417. RETURN( Status );
  1418. }
  1419. //
  1420. // Do some checking on the returned value. Look for things that we
  1421. // support.
  1422. //
  1423. if( ( pPropDesc->MembersListCount == 0 ) ||
  1424. ( !IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General ) ) ||
  1425. ( pPropDesc->PropTypeSet.Id != VT_I4 ) )
  1426. {
  1427. AudioFreeMemory_Unknown( &pPropDesc );
  1428. RETURN( STATUS_NOT_SUPPORTED );
  1429. }
  1430. pMemberHeader = (PKSPROPERTY_MEMBERSHEADER) ( pPropDesc + 1 );
  1431. #ifdef DEBUG
  1432. //
  1433. // If the MembersListCount is greater then zero and the GUID's are equal
  1434. // then we will reference the pMemberHeader value that we create here.
  1435. // If we do, then we must make sure that the memory that we allocated
  1436. // is large enough to handle it!
  1437. //
  1438. if (pPropDesc->DescriptionSize < (sizeof(KSPROPERTY_DESCRIPTION) +
  1439. sizeof(KSPROPERTY_MEMBERSHEADER)) )
  1440. {
  1441. DPF(DL_ERROR|FA_ALL,("Incorrectly reported DescriptionSize in KSPROPERTY_DESCRIPTION structure") );
  1442. RETURN(STATUS_INVALID_PARAMETER);
  1443. }
  1444. #endif
  1445. //
  1446. // Do some more checking on the returned value.
  1447. //
  1448. if ( (pMemberHeader->MembersCount == 0) ||
  1449. (pMemberHeader->MembersSize != sizeof(KSPROPERTY_STEPPING_LONG)) ||
  1450. (!(pMemberHeader->MembersFlags & KSPROPERTY_MEMBER_STEPPEDRANGES)) )
  1451. {
  1452. AudioFreeMemory_Unknown( &pPropDesc );
  1453. RETURN( STATUS_NOT_SUPPORTED );
  1454. }
  1455. //
  1456. // Volume controls may either be of MIXERTYPE_CONTROLF_UNIFORM
  1457. // or not. Uniform controls adjust all channels (or are mono
  1458. // in the first place) with one control. Those that have the
  1459. // fdwControl field set to 0 can set all channels of the volume
  1460. // independently. This information will have to come from the
  1461. // node itself, by checking to see if the node uniform control.
  1462. //
  1463. if (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL) {
  1464. pControl->NumChannels = pMemberHeader->MembersCount;
  1465. if( (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM) ||
  1466. (pMemberHeader->MembersCount == 1) ) {
  1467. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  1468. }
  1469. } else {
  1470. // Use the old method which checks if volume is supported on
  1471. // each channel one at a time
  1472. Status = kmxlSupportsMultiChannelControl(pfo,
  1473. pControl->Id,
  1474. pControl->PropertyId);
  1475. if (NT_SUCCESS(Status)) {
  1476. pControl->NumChannels = 2; // we have stereo
  1477. pControl->Control.fdwControl = 0;
  1478. } else {
  1479. pControl->NumChannels = 1; // we have mono or master channel
  1480. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  1481. }
  1482. }
  1483. DPF(DL_TRACE|FA_MIXER,(
  1484. "KMXL: Found %d channel ranges on control %x",
  1485. pControl->NumChannels,
  1486. pControl
  1487. ) );
  1488. ASSERT(pControl->NumChannels > 0);
  1489. ASSERT(pControl->pChannelStepping == NULL);
  1490. Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ),
  1491. TAG_AuDA_CHANNEL,
  1492. ZERO_FILL_MEMORY,
  1493. &pControl->pChannelStepping );
  1494. if( !NT_SUCCESS( Status ) ) {
  1495. pControl->NumChannels = 0;
  1496. AudioFreeMemory_Unknown( &pPropDesc );
  1497. RETURN( Status );
  1498. }
  1499. pSteppingLong = (PKSPROPERTY_STEPPING_LONG) ( pMemberHeader + 1 );
  1500. pChannelStepping = pControl->pChannelStepping;
  1501. // Assuming that MemberSize is sizeof(KSPROPERTY_STEPPING_LONG) for now
  1502. for (i = 0; i < pControl->NumChannels; pChannelStepping++) {
  1503. if ( pSteppingLong->Bounds.SignedMaximum == pSteppingLong->Bounds.SignedMinimum ) {
  1504. DPF(DL_WARNING|FA_MIXER,( "Channel %d has pSteppingLong->Bounds.SignedMaximum == pSteppingLong->Bounds.SignedMinimum", i ) );
  1505. AudioFreeMemory_Unknown( &pPropDesc );
  1506. RETURN( STATUS_NOT_SUPPORTED );
  1507. }
  1508. pChannelStepping->MinValue = pSteppingLong->Bounds.SignedMinimum;
  1509. pChannelStepping->MaxValue = pSteppingLong->Bounds.SignedMaximum;
  1510. if( pSteppingLong->SteppingDelta == 0 ) {
  1511. DPF(DL_WARNING|FA_MIXER,( "Channel %d has pSteppingLong->SteppingDelta == 0", i ) );
  1512. AudioFreeMemory_Unknown( &pPropDesc );
  1513. RETURN( STATUS_NOT_SUPPORTED );
  1514. }
  1515. pChannelStepping->Steps = (LONG) ( ( (LONGLONG) pSteppingLong->Bounds.SignedMaximum -
  1516. (LONGLONG) pSteppingLong->Bounds.SignedMinimum ) /
  1517. (LONGLONG) pSteppingLong->SteppingDelta );
  1518. if( pChannelStepping->Steps == 0 ) {
  1519. DPF(DL_WARNING|FA_MIXER, ( "Channel %d has pChannelStepping->Steps == 0", i ) );
  1520. AudioFreeMemory_Unknown( &pPropDesc );
  1521. RETURN( STATUS_NOT_SUPPORTED );
  1522. }
  1523. //
  1524. // Need to correct any out of bounds min, max and stepping values. This code use to be
  1525. // in persist.c.
  1526. //
  1527. /*
  1528. ASSERT ( pChannelStepping->MinValue >= -150*65536 && pChannelStepping->MinValue <= 150*65536 );
  1529. ASSERT ( pChannelStepping->MaxValue >= -150*65536 && pChannelStepping->MaxValue <= 150*65536 );
  1530. ASSERT ( pChannelStepping->Steps >= 0 && pChannelStepping->Steps <= 65535 );
  1531. */
  1532. if (!(pChannelStepping->MinValue >= -150*65536 && pChannelStepping->MinValue <= 150*65536)) {
  1533. DPF(DL_WARNING|FA_MIXER,
  1534. ("MinValue %X of Control %X of type %X on Channel %X is out of range! Correcting",
  1535. pChannelStepping->MinValue,
  1536. pControl->Control.dwControlID,
  1537. pControl->Control.dwControlType,
  1538. i) );
  1539. pChannelStepping->MinValue = DEFAULT_RANGE_MIN;
  1540. }
  1541. if (!(pChannelStepping->MaxValue >= -150*65536 && pChannelStepping->MaxValue <= 150*65536)) {
  1542. DPF(DL_WARNING|FA_MIXER,
  1543. ("MaxValue %X of Control %X of type %X on Channel %X is out of range! Correcting",
  1544. pChannelStepping->MaxValue,
  1545. pControl->Control.dwControlID,
  1546. pControl->Control.dwControlType,
  1547. i) );
  1548. pChannelStepping->MaxValue = DEFAULT_RANGE_MAX;
  1549. }
  1550. if (!(pChannelStepping->Steps >= 0 && pChannelStepping->Steps <= 65535)) {
  1551. DPF(DL_WARNING|FA_MIXER,
  1552. ("Steps %X of Control %X of type %X on Channel %X is out of range! Correcting",
  1553. pChannelStepping->Steps,
  1554. pControl->Control.dwControlID,
  1555. pControl->Control.dwControlType,
  1556. i) );
  1557. pChannelStepping->Steps = DEFAULT_RANGE_STEPS;
  1558. pControl->Control.Metrics.cSteps = DEFAULT_RANGE_STEPS;
  1559. }
  1560. DPF(DL_TRACE|FA_MIXER,( "Channel %d ranges from %08x to %08x by %08x steps",
  1561. i,
  1562. pChannelStepping->MinValue,
  1563. pChannelStepping->MaxValue,
  1564. pChannelStepping->Steps ) );
  1565. // Use the next Stepping structure, if there is one.
  1566. if (++i < pMemberHeader->MembersCount) {
  1567. pSteppingLong++;
  1568. }
  1569. }
  1570. AudioFreeMemory_Unknown( &pPropDesc );
  1571. return( STATUS_SUCCESS );
  1572. }
  1573. ///////////////////////////////////////////////////////////////////////
  1574. //
  1575. // FindTopologyConnectionTo
  1576. //
  1577. // Scans through the connection table looking for a connection that
  1578. // matches the ToNode/ToNodePin criteria.
  1579. //
  1580. //
  1581. ULONG
  1582. kmxlFindTopologyConnectionTo(
  1583. IN CONST KSTOPOLOGY_CONNECTION* pConnections, // The connection table
  1584. IN ULONG cConnections, // The # of connections
  1585. IN ULONG StartIndex, // Index to start search
  1586. IN ULONG ToNode, // The Node ID to look for
  1587. IN ULONG ToNodePin // The Pin ID to look for
  1588. )
  1589. {
  1590. ULONG i;
  1591. PAGED_CODE();
  1592. for( i = StartIndex; i < cConnections; i++ ) {
  1593. if( ( ( pConnections[ i ].ToNode == ToNode ) ||
  1594. ( ToNode == PINID_WILDCARD ) ) &&
  1595. ( ( pConnections[ i ].ToNodePin == ToNodePin ) ||
  1596. ( ToNodePin == PINID_WILDCARD ) ) ) {
  1597. return( i );
  1598. }
  1599. }
  1600. return( (ULONG) -1 );
  1601. }
  1602. ///////////////////////////////////////////////////////////////////////
  1603. //
  1604. // kmxlGetNumMuxLines
  1605. //
  1606. //
  1607. DWORD
  1608. kmxlGetNumMuxLines(
  1609. IN PKSTOPOLOGY pTopology,
  1610. IN ULONG NodeId
  1611. )
  1612. {
  1613. ULONG Index = 0,
  1614. Count = 0;
  1615. PAGED_CODE();
  1616. do {
  1617. Index = kmxlFindTopologyConnectionTo(
  1618. pTopology->TopologyConnections,
  1619. pTopology->TopologyConnectionsCount,
  1620. Index,
  1621. NodeId,
  1622. PINID_WILDCARD
  1623. );
  1624. if( Index == (ULONG) -1 ) {
  1625. break;
  1626. }
  1627. ++Count;
  1628. ++Index;
  1629. } while( 1 );
  1630. return( Count );
  1631. }
  1632. ///////////////////////////////////////////////////////////////////////
  1633. //
  1634. // kmxlGetMuxLineNames
  1635. //
  1636. //
  1637. VOID
  1638. kmxlGetMuxLineNames(
  1639. IN PMIXEROBJECT pmxobj,
  1640. IN PMXLCONTROL pControl
  1641. )
  1642. {
  1643. PMXLNODE pNode;
  1644. ULONG i, Index = 0, NodeId;
  1645. ASSERT( pmxobj );
  1646. ASSERT( pControl );
  1647. PAGED_CODE();
  1648. if( !NT_SUCCESS( AudioAllocateMemory_Paged(pControl->Control.cMultipleItems * sizeof( MIXERCONTROLDETAILS_LISTTEXT ),
  1649. TAG_AudG_GETMUXLINE,
  1650. ZERO_FILL_MEMORY,
  1651. &pControl->Parameters.lpmcd_lt ) ) )
  1652. {
  1653. DPF(DL_WARNING|FA_USER,("Failing non failable routine!") );
  1654. return;
  1655. }
  1656. if( !NT_SUCCESS( AudioAllocateMemory_Paged(pControl->Control.cMultipleItems * sizeof( ULONG ),
  1657. TAG_AudG_GETMUXLINE,
  1658. ZERO_FILL_MEMORY,
  1659. &pControl->Parameters.pPins ) ) )
  1660. {
  1661. AudioFreeMemory( pControl->Control.cMultipleItems * sizeof( MIXERCONTROLDETAILS_LISTTEXT ),
  1662. &pControl->Parameters.lpmcd_lt );
  1663. pControl->Parameters.Count = 0;
  1664. DPF(DL_WARNING|FA_USER,("Failing non failable routine!") );
  1665. return;
  1666. }
  1667. ASSERT( pControl->Parameters.lpmcd_lt );
  1668. ASSERT( pControl->Parameters.pPins );
  1669. pControl->Parameters.Count = pControl->Control.cMultipleItems;
  1670. for( i = 0; i < pControl->Control.cMultipleItems; i++ ) {
  1671. Index = kmxlFindTopologyConnectionTo(
  1672. pmxobj->pTopology->TopologyConnections,
  1673. pmxobj->pTopology->TopologyConnectionsCount,
  1674. Index,
  1675. pControl->Id,
  1676. PINID_WILDCARD
  1677. );
  1678. if( Index != (ULONG) -1 ) {
  1679. NodeId = pmxobj->pTopology->TopologyConnections[ Index ].FromNode;
  1680. if( NodeId == KSFILTER_NODE ) {
  1681. pControl->Parameters.lpmcd_lt[ i ].dwParam1 = pmxobj->pTopology->TopologyConnections[ Index ].FromNodePin;
  1682. pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1;
  1683. pControl->Parameters.pPins[ i ]
  1684. = pmxobj->pTopology->TopologyConnections[ Index ].ToNodePin;
  1685. ++Index;
  1686. continue;
  1687. } else {
  1688. pNode = &pmxobj->pNodeTable[ NodeId ];
  1689. }
  1690. ++Index;
  1691. while( pNode ) {
  1692. if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) ||
  1693. IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ||
  1694. ( kmxlParentListLength( pNode ) > 1 ) )
  1695. {
  1696. pControl->Parameters.lpmcd_lt[ i ].dwParam1 = 0x8000 + pNode->Id;
  1697. pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1;
  1698. pControl->Parameters.pPins[ i ]
  1699. = pmxobj->pTopology->TopologyConnections[ Index - 1 ].ToNodePin;
  1700. break;
  1701. }
  1702. if( pNode->Type == SOURCE ) {
  1703. pControl->Parameters.lpmcd_lt[ i ].dwParam1 = pNode->Id;
  1704. pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1;
  1705. pControl->Parameters.pPins[ i ]
  1706. = pmxobj->pTopology->TopologyConnections[ Index - 1 ].ToNodePin;
  1707. break;
  1708. } // if
  1709. if( kmxlFirstParentNode( pNode ) ) {
  1710. pNode = (kmxlFirstParentNode( pNode ))->pNode;
  1711. } else {
  1712. pNode = NULL;
  1713. }
  1714. } // while
  1715. } // if
  1716. } // for
  1717. } // kmxlGetMuxLineNames