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

2013 lines
62 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;
  875. PAGED_CODE();
  876. Pin.Property.Set = KSPROPSETID_Pin;
  877. Pin.Property.Id = KSPROPERTY_PIN_NAME;
  878. Pin.Property.Flags = KSPROPERTY_TYPE_GET;
  879. Pin.PinId = PinId;
  880. Pin.Reserved = 0;
  881. //
  882. // Query to see how many bytes of storage we need to allocate.
  883. // Note that the pointer and number of bytes must both be zero
  884. // or this will fail!
  885. //
  886. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Pin=%X",&Pin) );
  887. Status = KsSynchronousIoControlDevice(
  888. pfo,
  889. KernelMode,
  890. IOCTL_KS_PROPERTY,
  891. &Pin,
  892. sizeof(KSP_PIN),
  893. NULL,
  894. 0,
  895. &BytesReturned
  896. );
  897. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  898. ASSERT(!NT_SUCCESS(Status));
  899. if( Status != STATUS_BUFFER_OVERFLOW ) {
  900. goto exit;
  901. }
  902. //
  903. // Allocate what was returned.
  904. //
  905. Status = AudioAllocateMemory_Paged(BytesReturned,
  906. TAG_Audp_NAME,
  907. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  908. &szName );
  909. if( !NT_SUCCESS( Status ) ) {
  910. DPF(DL_WARNING|FA_USER,("Setting Default szName") );
  911. goto exit;
  912. }
  913. //
  914. // Call again to get the pin name.
  915. //
  916. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Pin=%X",&Pin) );
  917. Status = KsSynchronousIoControlDevice(
  918. pfo,
  919. KernelMode,
  920. IOCTL_KS_PROPERTY,
  921. &Pin,
  922. sizeof(KSP_PIN),
  923. szName,
  924. BytesReturned,
  925. &BytesReturned
  926. );
  927. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  928. //
  929. // If successful, copy as much of the name that will fit into the
  930. // short name and name fields of the line.
  931. //
  932. if( NT_SUCCESS( Status ) && szName ) {
  933. wcsncpy(
  934. pLine->Line.szShortName,
  935. szName,
  936. MIXER_SHORT_NAME_CHARS
  937. );
  938. pLine->Line.szShortName[ MIXER_SHORT_NAME_CHARS - 1 ] = 0x00;
  939. wcsncpy(
  940. pLine->Line.szName,
  941. szName,
  942. MIXER_LONG_NAME_CHARS );
  943. pLine->Line.szName[ MIXER_LONG_NAME_CHARS - 1 ] = 0x00;
  944. AudioFreeMemory_Unknown( &szName );
  945. return;
  946. }
  947. AudioFreeMemory_Unknown( &szName );
  948. exit:
  949. //
  950. // The pin doesn't support the property. Copy in a good default.
  951. //
  952. CopyAnsiStringtoUnicodeString(
  953. pLine->Line.szName,
  954. PinCategoryToString( &pLine->Type ),
  955. min(MIXER_LONG_NAME_CHARS, strlen(PinCategoryToString(&pLine->Type)) + 1)
  956. );
  957. wcsncpy(
  958. pLine->Line.szShortName,
  959. pLine->Line.szName,
  960. MIXER_SHORT_NAME_CHARS
  961. );
  962. pLine->Line.szShortName[ MIXER_SHORT_NAME_CHARS - 1 ] = 0x00;
  963. }
  964. ///////////////////////////////////////////////////////////////////////
  965. //
  966. // kmxlGetNodeName
  967. //
  968. // Retrieves the name of a node (control).
  969. //
  970. //
  971. VOID
  972. kmxlGetNodeName(
  973. IN PFILE_OBJECT pfo, // Instance of the owning filter
  974. IN ULONG NodeId, // The node id
  975. IN PMXLCONTROL pControl // The control to store the name
  976. )
  977. {
  978. NTSTATUS Status;
  979. LONG cbName;
  980. WCHAR* szName = NULL;
  981. KSNODEPROPERTY NodeProperty;
  982. PAGED_CODE();
  983. ASSERT( pfo );
  984. ASSERT( pControl );
  985. //
  986. // Query the number of bytes the node name is
  987. //
  988. NodeProperty.Property.Set = KSPROPSETID_Topology;
  989. NodeProperty.Property.Id = KSPROPERTY_TOPOLOGY_NAME;
  990. NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET |
  991. KSPROPERTY_TYPE_TOPOLOGY;
  992. NodeProperty.NodeId = NodeId;
  993. NodeProperty.Reserved = 0;
  994. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Node=%X",&NodeProperty) );
  995. Status = KsSynchronousIoControlDevice(
  996. pfo,
  997. KernelMode,
  998. IOCTL_KS_PROPERTY,
  999. &NodeProperty,
  1000. sizeof( NodeProperty ),
  1001. NULL,
  1002. 0,
  1003. &cbName
  1004. );
  1005. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  1006. if( ( Status == STATUS_BUFFER_OVERFLOW ) ||
  1007. ( Status == STATUS_BUFFER_TOO_SMALL ) ) {
  1008. //
  1009. // Allocate enough space to hold the entire name
  1010. //
  1011. if( !NT_SUCCESS( AudioAllocateMemory_Paged(cbName,
  1012. TAG_Audp_NAME,
  1013. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  1014. &szName ) ) )
  1015. {
  1016. goto exit;
  1017. }
  1018. ASSERT( szName );
  1019. //
  1020. // Requery for the name with the previously allocated buffer.
  1021. //
  1022. Status = kmxlNodeProperty(
  1023. pfo,
  1024. &KSPROPSETID_Topology,
  1025. KSPROPERTY_TOPOLOGY_NAME,
  1026. NodeId,
  1027. 0,
  1028. NULL,
  1029. szName,
  1030. cbName,
  1031. KSPROPERTY_TYPE_GET
  1032. );
  1033. if( NT_SUCCESS( Status ) && szName ) {
  1034. //
  1035. // Copy the names retrieved into the szShortName and Name
  1036. // fields of the control. The short name is just a shortened
  1037. // version of the full name.
  1038. //
  1039. wcsncpy(
  1040. pControl->Control.szShortName,
  1041. szName,
  1042. MIXER_SHORT_NAME_CHARS
  1043. );
  1044. pControl->Control.szShortName[ MIXER_SHORT_NAME_CHARS - 1 ] = 0x00;
  1045. wcsncpy(
  1046. pControl->Control.szName,
  1047. szName,
  1048. MIXER_LONG_NAME_CHARS );
  1049. pControl->Control.szName[ MIXER_LONG_NAME_CHARS - 1 ] = 0x00;
  1050. AudioFreeMemory_Unknown( &szName );
  1051. return;
  1052. }
  1053. }
  1054. //
  1055. // Looks like we might leak memory on the error condition. See
  1056. // kmxlGetPinName above!
  1057. //
  1058. AudioFreeMemory_Unknown( &szName );
  1059. exit:
  1060. //
  1061. // The node doesn't support the property. Copy in a good default.
  1062. //
  1063. CopyAnsiStringtoUnicodeString(
  1064. pControl->Control.szName,
  1065. NodeTypeToString( pControl->NodeType ),
  1066. min(MIXER_LONG_NAME_CHARS, strlen(NodeTypeToString(pControl->NodeType)) + 1)
  1067. );
  1068. wcsncpy(
  1069. pControl->Control.szShortName,
  1070. pControl->Control.szName,
  1071. MIXER_SHORT_NAME_CHARS
  1072. );
  1073. pControl->Control.szShortName[ MIXER_SHORT_NAME_CHARS - 1 ] = 0x00;
  1074. }
  1075. ///////////////////////////////////////////////////////////////////////
  1076. //
  1077. // kmxlGetSuperMixCaps
  1078. //
  1079. //
  1080. NTSTATUS
  1081. kmxlGetSuperMixCaps(
  1082. IN PFILE_OBJECT pfo,
  1083. IN ULONG ulNodeId,
  1084. OUT PKSAUDIO_MIXCAP_TABLE* paMixCaps
  1085. )
  1086. {
  1087. NTSTATUS Status;
  1088. ULONG Size;
  1089. struct {
  1090. ULONG InputChannels;
  1091. ULONG OutputChannels;
  1092. } SuperMixSize;
  1093. PKSAUDIO_MIXCAP_TABLE pMixCaps = NULL;
  1094. PAGED_CODE();
  1095. ASSERT( pfo );
  1096. ASSERT( paMixCaps );
  1097. *paMixCaps = NULL;
  1098. //
  1099. // Query the node with just the first 2 DWORDs of the MIXCAP table.
  1100. // This will return the dimensions of the supermixer.
  1101. //
  1102. Status = kmxlNodeProperty(
  1103. pfo,
  1104. &KSPROPSETID_Audio,
  1105. KSPROPERTY_AUDIO_MIX_LEVEL_CAPS,
  1106. ulNodeId,
  1107. 0,
  1108. NULL,
  1109. &SuperMixSize,
  1110. sizeof( SuperMixSize ),
  1111. KSPROPERTY_TYPE_GET
  1112. );
  1113. if( !NT_SUCCESS( Status ) ) {
  1114. DPF(DL_WARNING|FA_MIXER,( "kmxlNodeProperty failed with %X!", Status ) );
  1115. RETURN( Status );
  1116. }
  1117. //
  1118. // Allocate a MIXCAPS table big enough to hold all the entires.
  1119. // The size needs to include the first 2 DWORDs in the MIXCAP
  1120. // table besides the array ( InputCh * OutputCh ) of MIXCAPs
  1121. //
  1122. Size = sizeof( SuperMixSize ) +
  1123. SuperMixSize.InputChannels * SuperMixSize.OutputChannels *
  1124. sizeof( KSAUDIO_MIX_CAPS );
  1125. Status = AudioAllocateMemory_Paged(Size,
  1126. TAG_AudS_SUPERMIX,
  1127. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  1128. &pMixCaps );
  1129. if( !NT_SUCCESS( Status ) ) {
  1130. DPF(DL_WARNING|FA_MIXER,( "failed to allocate caps memory!" ) );
  1131. RETURN( Status );
  1132. }
  1133. //
  1134. // Query the node once again to fill in the MIXCAPS structures.
  1135. //
  1136. Status = kmxlNodeProperty(
  1137. pfo,
  1138. &KSPROPSETID_Audio,
  1139. KSPROPERTY_AUDIO_MIX_LEVEL_CAPS,
  1140. ulNodeId,
  1141. 0,
  1142. NULL,
  1143. pMixCaps,
  1144. Size,
  1145. KSPROPERTY_TYPE_GET
  1146. );
  1147. if( !NT_SUCCESS( Status ) ) {
  1148. DPF(DL_WARNING|FA_PROPERTY ,( "kmxlNodeProperty failed with %X!", Status ) );
  1149. AudioFreeMemory( Size,&pMixCaps );
  1150. RETURN( Status );
  1151. }
  1152. *paMixCaps = pMixCaps;
  1153. RETURN( Status );
  1154. }
  1155. ///////////////////////////////////////////////////////////////////////
  1156. //
  1157. // kmxlQueryPropertyRange
  1158. //
  1159. //
  1160. NTSTATUS
  1161. kmxlQueryPropertyRange(
  1162. IN PFILE_OBJECT pfo,
  1163. IN CONST GUID* pguidPropSet,
  1164. IN ULONG ulPropertyId,
  1165. IN ULONG ulNodeId,
  1166. OUT PKSPROPERTY_DESCRIPTION* ppPropDesc
  1167. )
  1168. {
  1169. NTSTATUS Status;
  1170. KSNODEPROPERTY NodeProperty;
  1171. KSPROPERTY_DESCRIPTION PropertyDescription;
  1172. PKSPROPERTY_DESCRIPTION pPropDesc = NULL;
  1173. ULONG BytesReturned;
  1174. PAGED_CODE();
  1175. //
  1176. // We don't want to allocate some arbitrary memory size if the driver
  1177. // does not set this value.
  1178. //
  1179. PropertyDescription.DescriptionSize=0;
  1180. NodeProperty.Property.Set = *pguidPropSet;
  1181. NodeProperty.Property.Id = ulPropertyId;
  1182. NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT |
  1183. KSPROPERTY_TYPE_TOPOLOGY;
  1184. NodeProperty.NodeId = ulNodeId;
  1185. NodeProperty.Reserved = 0;
  1186. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Query Node=%X",&NodeProperty) );
  1187. Status = KsSynchronousIoControlDevice(
  1188. pfo,
  1189. KernelMode,
  1190. IOCTL_KS_PROPERTY,
  1191. &NodeProperty,
  1192. sizeof( NodeProperty ),
  1193. &PropertyDescription,
  1194. sizeof( PropertyDescription ),
  1195. &BytesReturned
  1196. );
  1197. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  1198. if( !NT_SUCCESS( Status ) ) {
  1199. RETURN( Status );
  1200. }
  1201. //
  1202. // Never use a buffer that is smaller then we think it should be!
  1203. //
  1204. if( PropertyDescription.DescriptionSize < sizeof(KSPROPERTY_DESCRIPTION) )
  1205. {
  1206. #ifdef DEBUG
  1207. DPF(DL_ERROR|FA_ALL,("KSPROPERTY_DESCRIPTION.DescriptionSize!>=sizeof(KSPROPERTY_DESCRIPTION)") );
  1208. #endif
  1209. RETURN(STATUS_INVALID_PARAMETER);
  1210. }
  1211. Status = AudioAllocateMemory_Paged(PropertyDescription.DescriptionSize,
  1212. TAG_Auda_PROPERTY,
  1213. ZERO_FILL_MEMORY | LIMIT_MEMORY,
  1214. &pPropDesc );
  1215. if( !NT_SUCCESS( Status ) ) {
  1216. RETURN( Status );
  1217. }
  1218. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Get Node=%X",&NodeProperty) );
  1219. Status = KsSynchronousIoControlDevice(
  1220. pfo,
  1221. KernelMode,
  1222. IOCTL_KS_PROPERTY,
  1223. &NodeProperty,
  1224. sizeof( NodeProperty ),
  1225. pPropDesc,
  1226. PropertyDescription.DescriptionSize,
  1227. &BytesReturned
  1228. );
  1229. DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
  1230. if( !NT_SUCCESS( Status ) ) {
  1231. AudioFreeMemory( PropertyDescription.DescriptionSize,&pPropDesc );
  1232. RETURN( Status );
  1233. }
  1234. *ppPropDesc = pPropDesc;
  1235. return( STATUS_SUCCESS );
  1236. }
  1237. ///////////////////////////////////////////////////////////////////////
  1238. //
  1239. // kmxlGetControlChannels
  1240. //
  1241. //
  1242. NTSTATUS
  1243. kmxlGetControlChannels(
  1244. IN PFILE_OBJECT pfo,
  1245. IN PMXLCONTROL pControl
  1246. )
  1247. {
  1248. NTSTATUS Status;
  1249. PKSPROPERTY_DESCRIPTION pPropDesc = NULL;
  1250. PKSPROPERTY_MEMBERSHEADER pMemberHeader;
  1251. PCHANNEL_STEPPING pChannelStepping;
  1252. ULONG i;
  1253. PAGED_CODE();
  1254. Status = kmxlQueryPropertyRange(
  1255. pfo,
  1256. &KSPROPSETID_Audio,
  1257. pControl->PropertyId,
  1258. pControl->Id,
  1259. &pPropDesc
  1260. );
  1261. //
  1262. // Do some checking on the returned value. Look for things that we
  1263. // support.
  1264. //
  1265. if ( NT_SUCCESS(Status) ) {
  1266. ASSERT(pPropDesc);
  1267. pMemberHeader = (PKSPROPERTY_MEMBERSHEADER) ( pPropDesc + 1 );
  1268. #ifdef DEBUG
  1269. //
  1270. // If the MembersListCount is greater then zero and the GUID's are equal
  1271. // then we will reference the pMemberHeader value that we create here.
  1272. // If we do, then we must make sure that the memory that we allocated
  1273. // is large enough to handle it!
  1274. //
  1275. if( ( pPropDesc->MembersListCount > 0 ) &&
  1276. (IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General )) )
  1277. {
  1278. //
  1279. // if this is the case, we will touch the pMemberHeader->MembersCount
  1280. // field.
  1281. //
  1282. if (pPropDesc->DescriptionSize < (sizeof(KSPROPERTY_DESCRIPTION) +
  1283. sizeof(KSPROPERTY_MEMBERSHEADER)) )
  1284. {
  1285. DPF(DL_ERROR|FA_ALL,("Incorrectly reported DescriptionSize in KSPROPERTY_DESCRIPTION structure") );
  1286. RETURN(STATUS_INVALID_PARAMETER);
  1287. }
  1288. }
  1289. #endif
  1290. }
  1291. if( ( NT_SUCCESS( Status ) ) &&
  1292. ( pPropDesc->MembersListCount > 0 ) &&
  1293. ( IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General )) &&
  1294. ( pMemberHeader->MembersCount > 0 ) &&
  1295. ( pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL ) )
  1296. {
  1297. //
  1298. // Volume controls may either be of MIXERTYPE_CONTROLF_UNIFORM
  1299. // or not. Uniform controls adjust all channels (or are mono
  1300. // in the first place) with one control. Those that have the
  1301. // fdwControl field set to 0 can set all channels of the volume
  1302. // independently. This information will have to come from the
  1303. // node itself, by checking to see if the node uniform control.
  1304. //
  1305. pControl->NumChannels = pMemberHeader->MembersCount;
  1306. if( (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM) ||
  1307. (pMemberHeader->MembersCount == 1) ) {
  1308. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  1309. }
  1310. }
  1311. else {
  1312. // Fall through to using the old method which checks if volume is supported on
  1313. // each channel one at a time
  1314. Status = kmxlSupportsMultiChannelControl(pfo,
  1315. pControl->Id,
  1316. pControl->PropertyId);
  1317. if (NT_SUCCESS(Status)) {
  1318. pControl->NumChannels = 2; // we have stereo
  1319. pControl->Control.fdwControl = 0;
  1320. } else {
  1321. pControl->NumChannels = 1; // we have mono or master channel
  1322. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  1323. }
  1324. }
  1325. // Done with the pPropDesc
  1326. AudioFreeMemory_Unknown( &pPropDesc );
  1327. ASSERT(pControl->NumChannels > 0);
  1328. ASSERT(pControl->pChannelStepping == NULL);
  1329. Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ),
  1330. TAG_AuDB_CHANNEL,
  1331. ZERO_FILL_MEMORY,
  1332. &pControl->pChannelStepping );
  1333. if( !NT_SUCCESS( Status ) ) {
  1334. pControl->NumChannels = 0;
  1335. return( Status );
  1336. }
  1337. // For a failure, set the default range.
  1338. pChannelStepping = pControl->pChannelStepping;
  1339. for (i = 0; i < pControl->NumChannels; i++, pChannelStepping++) {
  1340. pChannelStepping->MinValue = DEFAULT_RANGE_MIN;
  1341. pChannelStepping->MaxValue = DEFAULT_RANGE_MAX;
  1342. pChannelStepping->Steps = DEFAULT_RANGE_STEPS;
  1343. }
  1344. return( STATUS_SUCCESS );
  1345. }
  1346. ///////////////////////////////////////////////////////////////////////
  1347. //
  1348. // kmxlGetControlRange
  1349. //
  1350. //
  1351. NTSTATUS
  1352. kmxlGetControlRange(
  1353. IN PFILE_OBJECT pfo,
  1354. IN PMXLCONTROL pControl
  1355. )
  1356. {
  1357. NTSTATUS Status;
  1358. PKSPROPERTY_DESCRIPTION pPropDesc;
  1359. PKSPROPERTY_MEMBERSHEADER pMemberHeader;
  1360. PKSPROPERTY_STEPPING_LONG pSteppingLong;
  1361. PCHANNEL_STEPPING pChannelStepping;
  1362. ULONG i;
  1363. PAGED_CODE();
  1364. //
  1365. // Query the range for this control and initialize pControl in case of failure
  1366. //
  1367. ASSERT( pControl->pChannelStepping == NULL );
  1368. pControl->pChannelStepping = NULL;
  1369. Status = kmxlQueryPropertyRange(
  1370. pfo,
  1371. &KSPROPSETID_Audio,
  1372. pControl->PropertyId,
  1373. pControl->Id,
  1374. &pPropDesc
  1375. );
  1376. if( !NT_SUCCESS( Status ) ) {
  1377. DPF(DL_WARNING|FA_MIXER,( "Failed to get BASICSUPPORT on control %x!", pControl ) );
  1378. // If BASICSUPPORT fails, kmxlGetControlChannels to handle the default behavior
  1379. Status = kmxlGetControlChannels( pfo, pControl );
  1380. RETURN( Status );
  1381. }
  1382. //
  1383. // Do some checking on the returned value. Look for things that we
  1384. // support.
  1385. //
  1386. if( ( pPropDesc->MembersListCount == 0 ) ||
  1387. ( !IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General ) ) ||
  1388. ( pPropDesc->PropTypeSet.Id != VT_I4 ) )
  1389. {
  1390. AudioFreeMemory_Unknown( &pPropDesc );
  1391. RETURN( STATUS_NOT_SUPPORTED );
  1392. }
  1393. pMemberHeader = (PKSPROPERTY_MEMBERSHEADER) ( pPropDesc + 1 );
  1394. #ifdef DEBUG
  1395. //
  1396. // If the MembersListCount is greater then zero and the GUID's are equal
  1397. // then we will reference the pMemberHeader value that we create here.
  1398. // If we do, then we must make sure that the memory that we allocated
  1399. // is large enough to handle it!
  1400. //
  1401. if (pPropDesc->DescriptionSize < (sizeof(KSPROPERTY_DESCRIPTION) +
  1402. sizeof(KSPROPERTY_MEMBERSHEADER)) )
  1403. {
  1404. DPF(DL_ERROR|FA_ALL,("Incorrectly reported DescriptionSize in KSPROPERTY_DESCRIPTION structure") );
  1405. RETURN(STATUS_INVALID_PARAMETER);
  1406. }
  1407. #endif
  1408. //
  1409. // Do some more checking on the returned value.
  1410. //
  1411. if ( (pMemberHeader->MembersCount == 0) ||
  1412. (pMemberHeader->MembersSize != sizeof(KSPROPERTY_STEPPING_LONG)) ||
  1413. (!(pMemberHeader->MembersFlags & KSPROPERTY_MEMBER_STEPPEDRANGES)) )
  1414. {
  1415. AudioFreeMemory_Unknown( &pPropDesc );
  1416. RETURN( STATUS_NOT_SUPPORTED );
  1417. }
  1418. //
  1419. // Volume controls may either be of MIXERTYPE_CONTROLF_UNIFORM
  1420. // or not. Uniform controls adjust all channels (or are mono
  1421. // in the first place) with one control. Those that have the
  1422. // fdwControl field set to 0 can set all channels of the volume
  1423. // independently. This information will have to come from the
  1424. // node itself, by checking to see if the node uniform control.
  1425. //
  1426. if (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL) {
  1427. pControl->NumChannels = pMemberHeader->MembersCount;
  1428. if( (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM) ||
  1429. (pMemberHeader->MembersCount == 1) ) {
  1430. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  1431. }
  1432. } else {
  1433. // Use the old method which checks if volume is supported on
  1434. // each channel one at a time
  1435. Status = kmxlSupportsMultiChannelControl(pfo,
  1436. pControl->Id,
  1437. pControl->PropertyId);
  1438. if (NT_SUCCESS(Status)) {
  1439. pControl->NumChannels = 2; // we have stereo
  1440. pControl->Control.fdwControl = 0;
  1441. } else {
  1442. pControl->NumChannels = 1; // we have mono or master channel
  1443. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  1444. }
  1445. }
  1446. DPF(DL_TRACE|FA_MIXER,(
  1447. "KMXL: Found %d channel ranges on control %x",
  1448. pControl->NumChannels,
  1449. pControl
  1450. ) );
  1451. ASSERT(pControl->NumChannels > 0);
  1452. ASSERT(pControl->pChannelStepping == NULL);
  1453. Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ),
  1454. TAG_AuDA_CHANNEL,
  1455. ZERO_FILL_MEMORY,
  1456. &pControl->pChannelStepping );
  1457. if( !NT_SUCCESS( Status ) ) {
  1458. pControl->NumChannels = 0;
  1459. AudioFreeMemory_Unknown( &pPropDesc );
  1460. RETURN( Status );
  1461. }
  1462. pSteppingLong = (PKSPROPERTY_STEPPING_LONG) ( pMemberHeader + 1 );
  1463. pChannelStepping = pControl->pChannelStepping;
  1464. // Assuming that MemberSize is sizeof(KSPROPERTY_STEPPING_LONG) for now
  1465. for (i = 0; i < pControl->NumChannels; pChannelStepping++) {
  1466. if ( pSteppingLong->Bounds.SignedMaximum == pSteppingLong->Bounds.SignedMinimum ) {
  1467. DPF(DL_WARNING|FA_MIXER,( "Channel %d has pSteppingLong->Bounds.SignedMaximum == pSteppingLong->Bounds.SignedMinimum", i ) );
  1468. AudioFreeMemory_Unknown( &pPropDesc );
  1469. RETURN( STATUS_NOT_SUPPORTED );
  1470. }
  1471. pChannelStepping->MinValue = pSteppingLong->Bounds.SignedMinimum;
  1472. pChannelStepping->MaxValue = pSteppingLong->Bounds.SignedMaximum;
  1473. if( pSteppingLong->SteppingDelta == 0 ) {
  1474. DPF(DL_WARNING|FA_MIXER,( "Channel %d has pSteppingLong->SteppingDelta == 0", i ) );
  1475. AudioFreeMemory_Unknown( &pPropDesc );
  1476. RETURN( STATUS_NOT_SUPPORTED );
  1477. }
  1478. pChannelStepping->Steps = (LONG) ( ( (LONGLONG) pSteppingLong->Bounds.SignedMaximum -
  1479. (LONGLONG) pSteppingLong->Bounds.SignedMinimum ) /
  1480. (LONGLONG) pSteppingLong->SteppingDelta );
  1481. if( pChannelStepping->Steps == 0 ) {
  1482. DPF(DL_WARNING|FA_MIXER, ( "Channel %d has pChannelStepping->Steps == 0", i ) );
  1483. AudioFreeMemory_Unknown( &pPropDesc );
  1484. RETURN( STATUS_NOT_SUPPORTED );
  1485. }
  1486. //
  1487. // Need to correct any out of bounds min, max and stepping values. This code use to be
  1488. // in persist.c.
  1489. //
  1490. /*
  1491. ASSERT ( pChannelStepping->MinValue >= -150*65536 && pChannelStepping->MinValue <= 150*65536 );
  1492. ASSERT ( pChannelStepping->MaxValue >= -150*65536 && pChannelStepping->MaxValue <= 150*65536 );
  1493. ASSERT ( pChannelStepping->Steps >= 0 && pChannelStepping->Steps <= 65535 );
  1494. */
  1495. if (!(pChannelStepping->MinValue >= -150*65536 && pChannelStepping->MinValue <= 150*65536)) {
  1496. DPF(DL_WARNING|FA_MIXER,
  1497. ("MinValue %X of Control %X of type %X on Channel %X is out of range! Correcting",
  1498. pChannelStepping->MinValue,
  1499. pControl->Control.dwControlID,
  1500. pControl->Control.dwControlType,
  1501. i) );
  1502. pChannelStepping->MinValue = DEFAULT_RANGE_MIN;
  1503. }
  1504. if (!(pChannelStepping->MaxValue >= -150*65536 && pChannelStepping->MaxValue <= 150*65536)) {
  1505. DPF(DL_WARNING|FA_MIXER,
  1506. ("MaxValue %X of Control %X of type %X on Channel %X is out of range! Correcting",
  1507. pChannelStepping->MaxValue,
  1508. pControl->Control.dwControlID,
  1509. pControl->Control.dwControlType,
  1510. i) );
  1511. pChannelStepping->MaxValue = DEFAULT_RANGE_MAX;
  1512. }
  1513. if (!(pChannelStepping->Steps >= 0 && pChannelStepping->Steps <= 65535)) {
  1514. DPF(DL_WARNING|FA_MIXER,
  1515. ("Steps %X of Control %X of type %X on Channel %X is out of range! Correcting",
  1516. pChannelStepping->Steps,
  1517. pControl->Control.dwControlID,
  1518. pControl->Control.dwControlType,
  1519. i) );
  1520. pChannelStepping->Steps = DEFAULT_RANGE_STEPS;
  1521. pControl->Control.Metrics.cSteps = DEFAULT_RANGE_STEPS;
  1522. }
  1523. DPF(DL_TRACE|FA_MIXER,( "Channel %d ranges from %08x to %08x by %08x steps",
  1524. i,
  1525. pChannelStepping->MinValue,
  1526. pChannelStepping->MaxValue,
  1527. pChannelStepping->Steps ) );
  1528. // Use the next Stepping structure, if there is one.
  1529. if (++i < pMemberHeader->MembersCount) {
  1530. pSteppingLong++;
  1531. }
  1532. }
  1533. AudioFreeMemory_Unknown( &pPropDesc );
  1534. return( STATUS_SUCCESS );
  1535. }
  1536. ///////////////////////////////////////////////////////////////////////
  1537. //
  1538. // FindTopologyConnectionTo
  1539. //
  1540. // Scans through the connection table looking for a connection that
  1541. // matches the ToNode/ToNodePin criteria.
  1542. //
  1543. //
  1544. ULONG
  1545. kmxlFindTopologyConnectionTo(
  1546. IN CONST KSTOPOLOGY_CONNECTION* pConnections, // The connection table
  1547. IN ULONG cConnections, // The # of connections
  1548. IN ULONG StartIndex, // Index to start search
  1549. IN ULONG ToNode, // The Node ID to look for
  1550. IN ULONG ToNodePin // The Pin ID to look for
  1551. )
  1552. {
  1553. ULONG i;
  1554. PAGED_CODE();
  1555. for( i = StartIndex; i < cConnections; i++ ) {
  1556. if( ( ( pConnections[ i ].ToNode == ToNode ) ||
  1557. ( ToNode == PINID_WILDCARD ) ) &&
  1558. ( ( pConnections[ i ].ToNodePin == ToNodePin ) ||
  1559. ( ToNodePin == PINID_WILDCARD ) ) ) {
  1560. return( i );
  1561. }
  1562. }
  1563. return( (ULONG) -1 );
  1564. }
  1565. ///////////////////////////////////////////////////////////////////////
  1566. //
  1567. // kmxlGetNumMuxLines
  1568. //
  1569. //
  1570. DWORD
  1571. kmxlGetNumMuxLines(
  1572. IN PKSTOPOLOGY pTopology,
  1573. IN ULONG NodeId
  1574. )
  1575. {
  1576. ULONG Index = 0,
  1577. Count = 0;
  1578. PAGED_CODE();
  1579. do {
  1580. Index = kmxlFindTopologyConnectionTo(
  1581. pTopology->TopologyConnections,
  1582. pTopology->TopologyConnectionsCount,
  1583. Index,
  1584. NodeId,
  1585. PINID_WILDCARD
  1586. );
  1587. if( Index == (ULONG) -1 ) {
  1588. break;
  1589. }
  1590. ++Count;
  1591. ++Index;
  1592. } while( 1 );
  1593. return( Count );
  1594. }
  1595. ///////////////////////////////////////////////////////////////////////
  1596. //
  1597. // kmxlGetMuxLineNames
  1598. //
  1599. //
  1600. VOID
  1601. kmxlGetMuxLineNames(
  1602. IN PMIXEROBJECT pmxobj,
  1603. IN PMXLCONTROL pControl
  1604. )
  1605. {
  1606. PMXLNODE pNode;
  1607. ULONG i, Index = 0, NodeId;
  1608. ASSERT( pmxobj );
  1609. ASSERT( pControl );
  1610. PAGED_CODE();
  1611. if( !NT_SUCCESS( AudioAllocateMemory_Paged(pControl->Control.cMultipleItems * sizeof( MIXERCONTROLDETAILS_LISTTEXT ),
  1612. TAG_AudG_GETMUXLINE,
  1613. ZERO_FILL_MEMORY,
  1614. &pControl->Parameters.lpmcd_lt ) ) )
  1615. {
  1616. DPF(DL_WARNING|FA_USER,("Failing non failable routine!") );
  1617. return;
  1618. }
  1619. if( !NT_SUCCESS( AudioAllocateMemory_Paged(pControl->Control.cMultipleItems * sizeof( ULONG ),
  1620. TAG_AudG_GETMUXLINE,
  1621. ZERO_FILL_MEMORY,
  1622. &pControl->Parameters.pPins ) ) )
  1623. {
  1624. AudioFreeMemory( pControl->Control.cMultipleItems * sizeof( MIXERCONTROLDETAILS_LISTTEXT ),
  1625. &pControl->Parameters.lpmcd_lt );
  1626. pControl->Parameters.Count = 0;
  1627. DPF(DL_WARNING|FA_USER,("Failing non failable routine!") );
  1628. return;
  1629. }
  1630. ASSERT( pControl->Parameters.lpmcd_lt );
  1631. ASSERT( pControl->Parameters.pPins );
  1632. pControl->Parameters.Count = pControl->Control.cMultipleItems;
  1633. for( i = 0; i < pControl->Control.cMultipleItems; i++ ) {
  1634. Index = kmxlFindTopologyConnectionTo(
  1635. pmxobj->pTopology->TopologyConnections,
  1636. pmxobj->pTopology->TopologyConnectionsCount,
  1637. Index,
  1638. pControl->Id,
  1639. PINID_WILDCARD
  1640. );
  1641. if( Index != (ULONG) -1 ) {
  1642. NodeId = pmxobj->pTopology->TopologyConnections[ Index ].FromNode;
  1643. if( NodeId == KSFILTER_NODE ) {
  1644. pControl->Parameters.lpmcd_lt[ i ].dwParam1 = pmxobj->pTopology->TopologyConnections[ Index ].FromNodePin;
  1645. pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1;
  1646. pControl->Parameters.pPins[ i ]
  1647. = pmxobj->pTopology->TopologyConnections[ Index ].ToNodePin;
  1648. ++Index;
  1649. continue;
  1650. } else {
  1651. pNode = &pmxobj->pNodeTable[ NodeId ];
  1652. }
  1653. ++Index;
  1654. while( pNode ) {
  1655. if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) ||
  1656. IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ||
  1657. ( kmxlParentListLength( pNode ) > 1 ) )
  1658. {
  1659. pControl->Parameters.lpmcd_lt[ i ].dwParam1 = 0x8000 + pNode->Id;
  1660. pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1;
  1661. pControl->Parameters.pPins[ i ]
  1662. = pmxobj->pTopology->TopologyConnections[ Index - 1 ].ToNodePin;
  1663. break;
  1664. }
  1665. if( pNode->Type == SOURCE ) {
  1666. pControl->Parameters.lpmcd_lt[ i ].dwParam1 = pNode->Id;
  1667. pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1;
  1668. pControl->Parameters.pPins[ i ]
  1669. = pmxobj->pTopology->TopologyConnections[ Index - 1 ].ToNodePin;
  1670. break;
  1671. } // if
  1672. if( kmxlFirstParentNode( pNode ) ) {
  1673. pNode = (kmxlFirstParentNode( pNode ))->pNode;
  1674. } else {
  1675. pNode = NULL;
  1676. }
  1677. } // while
  1678. } // if
  1679. } // for
  1680. } // kmxlGetMuxLineNames