|
|
//---------------------------------------------------------------------------
//
// Module: kmxltop.c
//
// Description:
// Topology parsing routines for the kernel mixer line driver
//
//
//@@BEGIN_MSINTERNAL
// Development Team:
// D. Baumberger
//
// History: Date Author Comment
//
//@@END_MSINTERNAL
//
//---------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
//
//---------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// //
// I N C L U D E S //
// //
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
#include "WDMSYS.H"
#include "kmxluser.h"
///////////////////////////////////////////////////////////////////////
//
// kmxlQueryTopology
//
// Queries the topology from the device and stores all the information
// in pTopology.
//
//
NTSTATUS kmxlQueryTopology( IN PFILE_OBJECT pfoInstance, // The handle to query the topology for
OUT PKSTOPOLOGY pTopology // The topology structure to fill in
) { NTSTATUS Status; PKSMULTIPLE_ITEM pCategories = NULL; PKSMULTIPLE_ITEM pNodes = NULL; PKSMULTIPLE_ITEM pConnections = NULL;
ASSERT( pfoInstance ); ASSERT( pTopology );
PAGED_CODE();
//
// Get device's topology categories
//
Status = kmxlGetProperty( pfoInstance, &KSPROPSETID_Topology, KSPROPERTY_TOPOLOGY_CATEGORIES, 0, // 0 extra input bytes
NULL, // No input data
0, // Flags
&pCategories ); if( !NT_SUCCESS( Status ) ) { RETURN( Status ); }
//
// Get the list of nodes types in the topology
//
Status = kmxlGetProperty( pfoInstance, &KSPROPSETID_Topology, KSPROPERTY_TOPOLOGY_NODES, 0, // 0 extra input bytes
NULL, // No input data
0, // Flags
&pNodes ); if( !NT_SUCCESS( Status ) ) { AudioFreeMemory_Unknown( &pCategories ); RETURN( Status ); }
//
// Get the list of connections in the meta-topology
//
Status = kmxlGetProperty( pfoInstance, &KSPROPSETID_Topology, KSPROPERTY_TOPOLOGY_CONNECTIONS, 0, // 0 extra input butes
NULL, // No input data
0, // Flags
&pConnections ); if( !NT_SUCCESS( Status ) ) { AudioFreeMemory_Unknown( &pCategories ); AudioFreeMemory_Unknown( &pNodes ); RETURN( Status ); }
//
// Fill in the topology structure so this information is available
// later. For the Categories and TopologyNodes, the pointers are
// pointers to a KSMULTIPLE_ITEM structure. The definition of this
// is that the data will follow immediately after the structure.
//
pTopology->CategoriesCount = pCategories->Count; pTopology->Categories = ( GUID* )( pCategories + 1 ); pTopology->TopologyNodesCount = pNodes->Count; pTopology->TopologyNodes = ( GUID* )( pNodes + 1 ); pTopology->TopologyConnectionsCount = pConnections->Count; pTopology->TopologyConnections = (PKSTOPOLOGY_CONNECTION) ( pConnections + 1 );
return( STATUS_SUCCESS ); }
///////////////////////////////////////////////////////////////////////
//
// kmxlParseTopology
//
// Loops through all the pins building up lists of sources and
// destinations. For each source, a child graph is the built.
//
//
NTSTATUS kmxlParseTopology( IN PMIXEROBJECT pmxobj, OUT NODELIST* plistSources, // Pointer to the sources list to build
OUT NODELIST* plistDests // Pointer to the dests list to build
) { NTSTATUS Status; ULONG cPins, PinID; PMXLNODE pTemp; NODELIST listSources = NULL; NODELIST listDests = NULL;
ASSERT( pmxobj ); ASSERT( plistSources ); ASSERT( plistDests );
PAGED_CODE();
//
// Query the number of pins
//
DPF(DL_TRACE|FA_MIXER,("Parsing Topology for: %ls",pmxobj->pMixerDevice->DeviceInterface) ); Status = GetPinProperty( pmxobj->pfo, KSPROPERTY_PIN_CTYPES, 0, sizeof( cPins ), &cPins ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_USER,("GetPinProperty CTYPES Failed Status=%X",Status) ); RETURN( Status ); }
DPF(DL_TRACE|FA_MIXER,("Number of Pins %u",cPins)); //
// Now scan through each of the pins identifying those that are
// sources and destinations.
//
for( PinID = 0; PinID < cPins; PinID++ ) { KSPIN_DATAFLOW DataFlow;
//
// Read the direction of dataflow of this pin.
//
Status = GetPinProperty( pmxobj->pfo, KSPROPERTY_PIN_DATAFLOW, PinID, sizeof( KSPIN_DATAFLOW ), &DataFlow ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_USER,("GetPinProperty DATAFLOW Failed Status=%X",Status) ); continue; }
//
// Based on the DataFlow, identify if the pin is a source,
// a destination, or neither.
//
switch( DataFlow ) {
///////////////////////////////////////////////////////////
case KSPIN_DATAFLOW_IN: ///////////////////////////////////////////////////////////
// DATAFLOW_IN pins are sources. //
///////////////////////////////////////////////////////////
//
// Create a new mixer node structure for this source
// and fill in the known information about it.
//
pTemp = kmxlAllocateNode( TAG_AudN_NODE ); if( !pTemp ) { Status=STATUS_INSUFFICIENT_RESOURCES; goto exit; }
pTemp->Type = SOURCE; pTemp->Id = PinID;
//
// Retrieve the category of this pin and store it away.
// The return does not need to be checked because the
// GUID will remain at GUID_NULL and be categorized
// properly.
//
GetPinProperty( pmxobj->pfo, KSPROPERTY_PIN_CATEGORY, PinID, sizeof( pTemp->NodeType ), &pTemp->NodeType );
DPF(DL_TRACE|FA_MIXER,( "Identified SOURCE Pin %d: %s", PinID, PinCategoryToString( &pTemp->NodeType ) ) ); //
// Retrieve the commmunication of this pin and store it away so
// we can tell if this is a wave out or wave in source
//
Status = GetPinProperty( pmxobj->pfo, KSPROPERTY_PIN_COMMUNICATION, PinID, sizeof( pTemp->Communication ), &pTemp->Communication ); if (!NT_SUCCESS(Status)) { pTemp->Communication = KSPIN_COMMUNICATION_NONE; }
//
// Add this new source node to the list of source
// nodes.
//
kmxlAddToList( listSources, pTemp ); break;
///////////////////////////////////////////////////////////
case KSPIN_DATAFLOW_OUT: ///////////////////////////////////////////////////////////
// DATAFLOW_OUT pins are destinations //
///////////////////////////////////////////////////////////
//
// Create a new mixer node structure for this dest
// and fill in the known information about it.
//
pTemp = kmxlAllocateNode( TAG_AudN_NODE ); if( !pTemp ) { Status=STATUS_INSUFFICIENT_RESOURCES; goto exit; }
pTemp->Type = DESTINATION; pTemp->Id = PinID;
//
// Retrieve the category of this pin and store it away.
// The return does not need to be checked because the
// GUID will remain at GUID_NULL and be categorized
// properly.
//
GetPinProperty( pmxobj->pfo, KSPROPERTY_PIN_CATEGORY, PinID, sizeof( pTemp->NodeType ), &pTemp->NodeType );
DPF(DL_TRACE|FA_MIXER,( "Identified DESTINATION Pin %d: %s", PinID, PinCategoryToString( &pTemp->NodeType ) ) );
//
// Retrieve the commmunication of this pin and store it away so
// we can tell if this is a wave out or wave in destination
//
Status = GetPinProperty( pmxobj->pfo, KSPROPERTY_PIN_COMMUNICATION, PinID, sizeof( pTemp->Communication ), &pTemp->Communication ); if (!NT_SUCCESS(Status)) { pTemp->Communication = KSPIN_COMMUNICATION_NONE; }
//
// Add this new destination node to the list of destination
// nodes.
//
kmxlAddToList( listDests, pTemp ); break;
///////////////////////////////////////////////////////////
default: ///////////////////////////////////////////////////////////
// DATAFLOW_BOTH and others are currently not supported. //
///////////////////////////////////////////////////////////
DPF(DL_WARNING|FA_USER,("Invalid DataFlow value =%X",DataFlow) ); }
}
DPF(DL_TRACE|FA_MIXER,("DataFlow done. PIN_COMMUNICATION read.") ); //
// For each source found, build the graphs of their children. This
// will recurse builing the graph of the children's children, etc.
//
pTemp = kmxlFirstInList( listSources ); while( pTemp ) {
Status=kmxlBuildChildGraph( pmxobj, // The mixer object
listDests, // The list of all the destinations
pTemp, // The source node to build the graph for
KSFILTER_NODE, // Sources are always KSFILTER_NODEs
pTemp->Id // The Pin id of the source
);
if (!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_USER,("kmxlBuildChildGraph failed Status=%X",Status) ); goto exit; }
pTemp = kmxlNextNode( pTemp );
}
exit:
//
// Finally fill in the client pointers
//
*plistSources = listSources; *plistDests = listDests;
//We must have a destination and a source
if (listSources == NULL || listDests == NULL) { Status = STATUS_INVALID_DEVICE_REQUEST; }
return Status; }
///////////////////////////////////////////////////////////////////////
//
// BuildChildGraph
//
// Builds the graph of the child of the given node. For each child
// of the node, it recurses to find their child, etc.
//
//
NTSTATUS kmxlBuildChildGraph( IN PMIXEROBJECT pmxobj, IN NODELIST listDests, // The list of destinations
IN PMXLNODE pNode, // The node to build the graph for
IN ULONG FromNode, // The node's ID
IN ULONG FromNodePin // The Pin connection to look for
) { ULONG Index = 0; PMXLNODE pNewNode = NULL; PMXLNODE pTemp = NULL; BOOL bEndOfTheLine = FALSE; PEERNODE* pPeerNode = NULL; NTSTATUS Status=STATUS_SUCCESS;
PAGED_CODE();
//
// Find the index of the requested connection. A return of -1
// indicates that the connection was not found. Searches start
// at Index, which starts with 0 and is > 0 if the last was a match.
//
while ( (Index = kmxlFindTopologyConnection(pmxobj, Index, FromNode, FromNodePin)) != (ULONG) -1) {
//
// Check to see if this connection is a KSFILTER_NODE. That will
// indicate that it's connected to a destination and not another node.
//
if( pmxobj->pTopology->TopologyConnections[ Index ].ToNode == KSFILTER_NODE ) {
//
// Find the destination node so that the parent field can be
// updated to include this node. bEndOfTheLine is set to TRUE
// since there can be no other connections after the destination.
//
pNewNode = kmxlFindDestination( listDests, pmxobj->pTopology->TopologyConnections[ Index ].ToNodePin );
bEndOfTheLine = TRUE;
//
// We better find a destination; if not, something's really wrong.
//
if (pNewNode==NULL) { RETURN( STATUS_UNSUCCESSFUL ); }
} else {
//
// Using the identifier stored in the ToNode of the topology
// connections, index into the node table and retrieve the
// mixer node associated with that id.
//
pNewNode = &pmxobj->pNodeTable[ pmxobj->pTopology->TopologyConnections[ Index ].ToNode ];
//
// Fill in a couple of missing details. Note that these details
// may already be filled in but it doesn't hurt to overwrite
// them with the same values.
//
pNewNode->Type = NODE; pNewNode->Id = pmxobj->pTopology->TopologyConnections[ Index ].ToNode; }
//
// Insert the new node into the childlist of the current node only
// if it isn't already there. It only wastes memory to add it more
// than once and prevents the proper updating of the child and parent
// lists.
//
if( !kmxlInChildList( pNode, pNewNode ) ) { pPeerNode = kmxlAllocatePeerNode( pNewNode, TAG_Audn_PEERNODE ); if( !pPeerNode ) { RETURN( STATUS_INSUFFICIENT_RESOURCES ); }
DPF(DL_TRACE|FA_MIXER,( "Added %s(%d-0x%08x) to child list of %s(%d-0x%08x).", pPeerNode->pNode->Type == SOURCE ? "SOURCE" : pPeerNode->pNode->Type == DESTINATION ? "DEST" : pPeerNode->pNode->Type == NODE ? "NODE" : "Huh?", pPeerNode->pNode->Id, pPeerNode, pNode->Type == SOURCE ? "SOURCE" : pNode->Type == DESTINATION ? "DEST" : pNode->Type == NODE ? "NODE" : "Huh?", pNode->Id, pNode ) );
kmxlAddToChildList( pNode, pPeerNode ); }
//
// Insert the new node into the parentlist of the new node only
// if it isn't already there. It only wastes memory to add it more
// than once and prevents the proper updating the child and parent
// lists.
//
if( !kmxlInParentList( pNewNode, pNode ) ) { pPeerNode = kmxlAllocatePeerNode( pNode, TAG_Audn_PEERNODE ); if( !pPeerNode ) { RETURN( STATUS_INSUFFICIENT_RESOURCES ); }
DPF(DL_TRACE|FA_MIXER,("Added %s(%d-0x%08x) to parent list of %s(%d-0x%08x).", pPeerNode->pNode->Type == SOURCE ? "SOURCE" : pPeerNode->pNode->Type == DESTINATION ? "DEST" : pPeerNode->pNode->Type == NODE ? "NODE" : "Huh?", pPeerNode->pNode->Id, pPeerNode, pNewNode->Type == SOURCE ? "SOURCE" : pNewNode->Type == DESTINATION ? "DEST" : pNewNode->Type == NODE ? "NODE" : "Huh?", pNewNode->Id, pNewNode ) );
kmxlAddToParentList( pNewNode, pPeerNode ); }
//
// Skip past the connection we just processed.
//
++Index;
} // Loop until FindConnection fails.
//
// The last connection found connects to a destination node. Do not
// try to enumerate the children, since there are none.
//
if( bEndOfTheLine ) { RETURN( Status ); }
//
// For each of the children of this node, recurse to build up the lists
// of the child's nodes.
//
pPeerNode = kmxlFirstChildNode( pNode ); while( pPeerNode ) {
Status = kmxlBuildChildGraph( pmxobj, listDests, // The list of destination nodes
pPeerNode->pNode, // The parent node
pPeerNode->pNode->Id, // The Id of the parent
PINID_WILDCARD // Look for any connection by this node
);
if (!NT_SUCCESS(Status)) { break; }
pPeerNode = kmxlNextPeerNode( pPeerNode ); }
RETURN( Status );
}
///////////////////////////////////////////////////////////////////////
//
// BuildNodeTable
//
// Allocates enough memory to hold TopologyNodeCount MXLNODE structures.
// The GUIDs from the Topology are copied over into the MXLNODE structures.
//
//
PMXLNODE kmxlBuildNodeTable( IN PKSTOPOLOGY pTopology // The topology structure
) { PMXLNODE pTable = NULL; ULONG i;
ASSERT( pTopology );
PAGED_CODE();
//
// If we don't have any node count, we don't want to allocate a zero byte buffer.
// simply return the error case.
//
if( 0 == pTopology->TopologyNodesCount ) { return NULL; }
//
// Allocate an array of nodes the same size as the Topology Node
// table.
//
if( !NT_SUCCESS( AudioAllocateMemory_Paged(pTopology->TopologyNodesCount * sizeof( MXLNODE ), TAG_AudN_NODE, ZERO_FILL_MEMORY, &pTable) ) ) { return( NULL ); }
//
// Initialize the nodes. All the can be filled in here is the GUIDs,
// copied from the node table.
//
for( i = 0; i < pTopology->TopologyNodesCount; i++ ) { pTable[ i ].NodeType = pTopology->TopologyNodes[ i ]; }
return( pTable ); }
///////////////////////////////////////////////////////////////////////
//
// FindTopologyConnection
//
// Scans through the connection table looking for a connection that
// matches the FromNode/FromNodePin criteria.
//
//
ULONG kmxlFindTopologyConnection( IN PMIXEROBJECT pmxobj, IN ULONG StartIndex, // Index to start search
IN ULONG FromNode, // The Node ID to look for
IN ULONG FromNodePin // The Pin ID to look for
) { ULONG i;
PAGED_CODE(); for( i = StartIndex; i < pmxobj->pTopology->TopologyConnectionsCount; i++ ) { if( ( ( pmxobj->pTopology->TopologyConnections[ i ].FromNode == FromNode )|| ( FromNode == PINID_WILDCARD ) ) && ( ( pmxobj->pTopology->TopologyConnections[ i ].FromNodePin == FromNodePin ) || ( FromNodePin == PINID_WILDCARD ) ) ) { //#ifdef PARSE_TRACE
//TRACE( "WDMAUD: Found connection from (%d,%d) -> %d.\n",
// FromNode, FromNodePin, i );
//#endif
return( i ); } } return( (ULONG) -1 ); }
///////////////////////////////////////////////////////////////////////
//
// kmxlGetProperty
//
// Queries a property by first determining the correct number of
// output bytes, allocating that much memory, and quering the
// actual data.
//
//
NTSTATUS kmxlGetProperty( PFILE_OBJECT pFileObject, // The instance of the filter
CONST GUID *pguidPropertySet, // The requested property set
ULONG ulPropertyId, // The ID of the specific property
ULONG cbInput, // The number of extra input bytes
PVOID pInputData, // Pointer to the extra input bytes
ULONG Flags, // Additional flags
PVOID *ppPropertyOutput // Pointer to a pointer of the output
) { ULONG BytesReturned; ULONG cbPropertyInput = sizeof(KSPROPERTY); PKSPROPERTY pPropertyInput = NULL; NTSTATUS Status;
PAGED_CODE();
ASSERT( pFileObject );
//
// Allocate enough memory for the KSPROPERTY structure and any additional
// input the callers wants to include.
//
cbPropertyInput += cbInput; Status = AudioAllocateMemory_Paged(cbPropertyInput, TAG_AudV_PROPERTY, ZERO_FILL_MEMORY, &pPropertyInput ); if(!NT_SUCCESS(Status)) { goto exit; }
//
// Set up the field of the KSPROPERTY structure
//
pPropertyInput->Set = *pguidPropertySet; pPropertyInput->Id = ulPropertyId; pPropertyInput->Flags = KSPROPERTY_TYPE_GET | Flags;
//
// Copy the additional input from the caller.
//
if(pInputData != NULL) { RtlCopyMemory(pPropertyInput + 1, pInputData, cbInput); }
//
// This first call will query the number of bytes the output needs.
//
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY pPropertyInput=%X",pPropertyInput) );
Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_PROPERTY, pPropertyInput, cbPropertyInput, NULL, 0, &BytesReturned );
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Status=%X",Status) );
ASSERT(!NT_SUCCESS(Status)); if(Status != STATUS_BUFFER_OVERFLOW) { goto exit; }
if(BytesReturned == 0) { *ppPropertyOutput = NULL; Status = STATUS_SUCCESS; goto exit; }
//
// Allocate enough memory to hold all of the output.
//
Status = AudioAllocateMemory_Paged(BytesReturned, TAG_Audv_PROPERTY, ZERO_FILL_MEMORY | LIMIT_MEMORY, ppPropertyOutput ); if(!NT_SUCCESS(Status)) { goto exit; }
//
// Now actually get the output data.
//
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY pPropertyInput=%X",pPropertyInput) );
Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_PROPERTY, pPropertyInput, cbPropertyInput, *ppPropertyOutput, BytesReturned, &BytesReturned );
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Status=%X",Status) );
if(!NT_SUCCESS(Status)) { AudioFreeMemory_Unknown(ppPropertyOutput); goto exit; }
exit:
AudioFreeMemory_Unknown(&pPropertyInput); if(!NT_SUCCESS(Status)) { *ppPropertyOutput = NULL; DPF(DL_WARNING|FA_USER,("Failed to get Property Status=%X",Status) ); } RETURN(Status); }
///////////////////////////////////////////////////////////////////////
//
// kmxlNodeProperty
//
// Creates a KSNODEPROPERTY structure with additional input data
// after it and uses KsSychronousIoControlDevice() to query or set the
// property. Only memory for the input is allocated here.
//
//
NTSTATUS kmxlNodeProperty( IN PFILE_OBJECT pFileObject, // Instance of the filter owning node
IN CONST GUID* pguidPropertySet, // The GUID of the property set
IN ULONG ulPropertyId, // The specific property in the set
IN ULONG ulNodeId, // The virtual node id
IN ULONG cbInput, // # of extra input bytes
IN PVOID pInputData, // Pointer to the extra input bytes
OUT PVOID pPropertyOutput, // Pointer to the output data
IN ULONG cbPropertyOutput, // Size of the output data buffer
IN ULONG Flags // KSPROPERTY_TYPE_GET or SET
) { NTSTATUS Status; KSNODEPROPERTY NodeProperty; ULONG cbPropertyIn = sizeof( KSNODEPROPERTY ); PKSNODEPROPERTY pInData = NULL; ULONG BytesReturned;
PAGED_CODE();
ASSERT( pFileObject ); ASSERT( pguidPropertySet );
if( cbInput > 0 ) {
//
// If the caller passed in some extra input, add that size
// to the size of the required KSNODEPROPERTY and allocate
// a chunk of memory.
//
cbPropertyIn += cbInput; Status = AudioAllocateMemory_Paged(cbPropertyIn, TAG_AudU_PROPERTY, ZERO_FILL_MEMORY, &pInData ); if( !NT_SUCCESS( Status ) ) { goto exit; }
RtlCopyMemory( pInData + 1, pInputData, cbInput );
} else {
pInData = &NodeProperty;
}
//
// Fill in the property and node information.
//
pInData->Property.Set = *pguidPropertySet; pInData->Property.Id = ulPropertyId; pInData->Property.Flags = Flags | KSPROPERTY_TYPE_TOPOLOGY; pInData->NodeId = ulNodeId; pInData->Reserved = 0;
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY pInData=%X",pInData) );
Status = KsSynchronousIoControlDevice( pFileObject, // The FILE_OBJECT for SysAudio
KernelMode, // Call originates in Kernel mode
IOCTL_KS_PROPERTY, // KS PROPERTY IOCTL
pInData, // Pointer to the KSNODEPROPERTY struct
cbPropertyIn, // Number or bytes input
pPropertyOutput, // Pointer to the buffer to store output
cbPropertyOutput, // Size of the output buffer
&BytesReturned // Number of bytes returned from the call
);
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Status=%X",Status) );
if(!NT_SUCCESS(Status)) { goto exit; }
exit:
//
// If the user passed in extra byte, we allocated memory to hold them.
// Now the memory must be deallocated.
//
if( cbInput > 0 ) { AudioFreeMemory_Unknown( &pInData ); }
RETURN( Status );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAudioNodeProperty
//
// Similar to kmxlNodeProperty except for the property set is assumed
// to be KSPROPSETID_Audio and a KSNODEPROPERTY_AUDIO_CHANNEL structure
// is used instead of KSNODEPROPERTY to allow channel selection.
//
//
NTSTATUS kmxlAudioNodeProperty( IN PFILE_OBJECT pfo, // Instance of the filter owning node
IN ULONG ulPropertyId, // The audio property to get
IN ULONG ulNodeId, // The virtual node id
IN LONG lChannel, // The channel number
IN PVOID pInData, // Pointer to extra input bytes
IN ULONG cbInData, // Number of extra input bytes
OUT PVOID pOutData, // Pointer to output buffer
IN LONG cbOutData, // Size of the output buffer
IN ULONG Flags // KSPROPERTY_TYPE_GET or SET
) { NTSTATUS Status; KSNODEPROPERTY_AUDIO_CHANNEL Channel; PKSNODEPROPERTY_AUDIO_CHANNEL pInput = NULL; ULONG cbInput; ULONG BytesReturned;
PAGED_CODE();
ASSERT( pfo );
//
// Determine the minimum number of input bytes
//
cbInput = sizeof( KSNODEPROPERTY_AUDIO_CHANNEL );
//
// If the caller passed in additional data, allocate enough memory
// to hold the KSNODEPROPERTY_AUDIO_CHANNEL plus the input bytes
// and copy the input bytes into the new memory immediately after
// the KSNODEPROPERTY_AUDIO_CHANNEL structure.
//
if( cbInData > 0 ) {
cbInput += cbInData; Status = AudioAllocateMemory_Paged(cbInput, TAG_Audu_PROPERTY, ZERO_FILL_MEMORY, &pInput ); if( !NT_SUCCESS( Status ) ) { goto exit; }
RtlCopyMemory( pInput + 1, pInData, cbInData );
} else {
//
// Memory saving hack... if the user didn't give any additional
// bytes, just point to memory on the stack.
//
pInput = &Channel;
}
//
// Fill in the property fields.
//
pInput->NodeProperty.Property.Set = KSPROPSETID_Audio; pInput->NodeProperty.Property.Id = ulPropertyId; pInput->NodeProperty.Property.Flags = Flags | KSPROPERTY_TYPE_TOPOLOGY;
//
// Fill in the node details.
//
pInput->NodeProperty.NodeId = ulNodeId; pInput->NodeProperty.Reserved = 0;
//
// Fill in the channel details.
//
pInput->Channel = lChannel; pInput->Reserved = 0;
//
// And execute the property.
//
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY pInput=%X",pInput) );
Status = KsSynchronousIoControlDevice( pfo, // The FILE_OBJECT for SysAudio
KernelMode, // Call originates in Kernel mode
IOCTL_KS_PROPERTY, // KS PROPERTY IOCTL
pInput, // Pointer to the KSNODEPROPERTY struct
cbInput, // Number or bytes input
pOutData, // Pointer to the buffer to store output
cbOutData, // Size of the output buffer
&BytesReturned // Number of bytes returned from the call
);
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
if(!NT_SUCCESS(Status)) { goto exit; }
exit:
//
// If the user passed in extra bytes, we allocated memory to hold them.
// Now the memory must be deallocated.
//
if( cbInData > 0 ) { AudioFreeMemory_Unknown( &pInData ); }
RETURN( Status ); }
///////////////////////////////////////////////////////////////////////
//
// kmxlGetPinName
//
// Calls GetPinPropertyEx to guery and allocate memory for the pin
// name. If that call fails, a default name is copy based on the
// pin type.
//
// The short name is made identical to the long name, but using only
// the first sizeof( szShortName ) / sizeof( WCHAR ) characters.
//
//
VOID kmxlGetPinName( IN PFILE_OBJECT pfo, // Instance of the owning filter
IN ULONG PinId, // Id of the pin
IN PMXLLINE pLine // The line to store the name into
) { WCHAR* szName = NULL; NTSTATUS Status; KSP_PIN Pin; ULONG BytesReturned = 0; ULONG BytesReturned2 = 0;
PAGED_CODE(); Pin.Property.Set = KSPROPSETID_Pin; Pin.Property.Id = KSPROPERTY_PIN_NAME; Pin.Property.Flags = KSPROPERTY_TYPE_GET; Pin.PinId = PinId; Pin.Reserved = 0;
//
// Query to see how many bytes of storage we need to allocate.
// Note that the pointer and number of bytes must both be zero
// or this will fail!
//
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Pin=%X",&Pin) );
Status = KsSynchronousIoControlDevice( pfo, KernelMode, IOCTL_KS_PROPERTY, &Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned );
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
ASSERT(!NT_SUCCESS(Status)); if( Status != STATUS_BUFFER_OVERFLOW ) { goto exit; }
//
// Allocate what was returned.
//
Status = AudioAllocateMemory_Paged(BytesReturned, TAG_Audp_NAME, ZERO_FILL_MEMORY | LIMIT_MEMORY, &szName ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_USER,("Setting Default szName") ); goto exit; }
//
// Call again to get the pin name.
//
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Pin=%X",&Pin) );
BytesReturned2=BytesReturned; Status = KsSynchronousIoControlDevice( pfo, KernelMode, IOCTL_KS_PROPERTY, &Pin, sizeof(KSP_PIN), szName, BytesReturned2, &BytesReturned2 );
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
//
// If successful, copy as much of the name that will fit into the
// short name and name fields of the line.
//
if( NT_SUCCESS( Status ) && szName ) { #ifdef DEBUG
//
// There is no good reason that I can think of for a driver to return
// a different return value the second time it's called. That would just
// be stupid.
//
if( BytesReturned != BytesReturned2 ) { DPF(DL_WARNING|FA_SYSAUDIO,("Unequal returns! BR=%08x,BR2=%08x",BytesReturned,BytesReturned2)); } //
// Let's explicitly look for the case that made this driver fault. The
// BytesReturned value was 8 and it contained MUX\0 in the buffer. The problem
// was that wcsncpy walked MIXER_SHORT_NAME_CHARS number of characters.
// Thus it walked off the end of the source buffer.
//
if( (BytesReturned/sizeof(WCHAR) < MIXER_SHORT_NAME_CHARS) && (szName[BytesReturned/sizeof(WCHAR)-1] != (WCHAR)NULL) ) { DPF(DL_ERROR|FA_SYSAUDIO,("Hit short name assert! BR=%08x",BytesReturned)); } #endif
wcsncpy( pLine->Line.szShortName, szName, min(BytesReturned/sizeof(WCHAR),MIXER_SHORT_NAME_CHARS) ); pLine->Line.szShortName[ min(BytesReturned/sizeof(WCHAR),MIXER_SHORT_NAME_CHARS) - 1 ] = (WCHAR)NULL; wcsncpy( pLine->Line.szName, szName, min(BytesReturned/sizeof(WCHAR),MIXER_LONG_NAME_CHARS) ); pLine->Line.szName[ min(BytesReturned/sizeof(WCHAR),MIXER_LONG_NAME_CHARS) - 1 ] = (WCHAR)NULL; AudioFreeMemory_Unknown( &szName ); return; }
AudioFreeMemory_Unknown( &szName );
exit:
//
// The pin doesn't support the property. Copy in a good default.
//
CopyAnsiStringtoUnicodeString( pLine->Line.szName, PinCategoryToString( &pLine->Type ), min(MIXER_LONG_NAME_CHARS, strlen(PinCategoryToString(&pLine->Type)) + 1) );
wcsncpy( pLine->Line.szShortName, pLine->Line.szName, MIXER_SHORT_NAME_CHARS ); pLine->Line.szShortName[ MIXER_SHORT_NAME_CHARS - 1 ] = 0x00;
}
///////////////////////////////////////////////////////////////////////
//
// kmxlGetNodeName
//
// Retrieves the name of a node (control).
//
//
VOID kmxlGetNodeName( IN PFILE_OBJECT pfo, // Instance of the owning filter
IN ULONG NodeId, // The node id
IN PMXLCONTROL pControl // The control to store the name
) { NTSTATUS Status; LONG cbName=0; WCHAR* szName = NULL; KSNODEPROPERTY NodeProperty;
PAGED_CODE(); ASSERT( pfo ); ASSERT( pControl );
//
// Query the number of bytes the node name is
//
NodeProperty.Property.Set = KSPROPSETID_Topology; NodeProperty.Property.Id = KSPROPERTY_TOPOLOGY_NAME; NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY; NodeProperty.NodeId = NodeId; NodeProperty.Reserved = 0;
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Node=%X",&NodeProperty) );
Status = KsSynchronousIoControlDevice( pfo, KernelMode, IOCTL_KS_PROPERTY, &NodeProperty, sizeof( NodeProperty ), NULL, 0, &cbName );
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
if( ( Status == STATUS_BUFFER_OVERFLOW ) || ( Status == STATUS_BUFFER_TOO_SMALL ) ) {
//
// Allocate enough space to hold the entire name
//
if( !NT_SUCCESS( AudioAllocateMemory_Paged(cbName, TAG_Audp_NAME, ZERO_FILL_MEMORY | LIMIT_MEMORY, &szName ) ) ) { goto exit; }
ASSERT( szName );
//
// Requery for the name with the previously allocated buffer.
//
Status = kmxlNodeProperty( pfo, &KSPROPSETID_Topology, KSPROPERTY_TOPOLOGY_NAME, NodeId, 0, NULL, szName, cbName, KSPROPERTY_TYPE_GET ); if( NT_SUCCESS( Status ) && szName ) {
//
// Copy the names retrieved into the szShortName and Name
// fields of the control. The short name is just a shortened
// version of the full name.
//
//
// Note: cbName is a byte value and wcsncpy takes a count of characters,
// We are dealing with wide characters, thus we must adjust the
// memory size to characters! Note that the driver could have
// returned a source buffer less then MIXER_SHORT_NAME_CHARS in length!
//
#ifdef DEBUG
if( (cbName/sizeof(WCHAR) < MIXER_SHORT_NAME_CHARS) && (szName[cbName/sizeof(WCHAR)-1] != (WCHAR)NULL) ) { DPF(DL_ERROR|FA_SYSAUDIO,("Hit short name assert! cbName=%08x",cbName)); } #endif
wcsncpy( pControl->Control.szShortName, szName, min(cbName/sizeof(WCHAR),MIXER_SHORT_NAME_CHARS) ); pControl->Control.szShortName[ min(cbName/sizeof(WCHAR),MIXER_SHORT_NAME_CHARS) - 1 ] = (WCHAR)NULL; wcsncpy( pControl->Control.szName, szName, min(cbName/sizeof(WCHAR),MIXER_LONG_NAME_CHARS) ); pControl->Control.szName[ min(cbName/sizeof(WCHAR),MIXER_LONG_NAME_CHARS) - 1 ] = (WCHAR)NULL; AudioFreeMemory_Unknown( &szName ); return; } }
//
// Looks like we might leak memory on the error condition. See
// kmxlGetPinName above!
//
AudioFreeMemory_Unknown( &szName ); exit:
//
// The node doesn't support the property. Copy in a good default.
//
CopyAnsiStringtoUnicodeString( pControl->Control.szName, NodeTypeToString( pControl->NodeType ), min(MIXER_LONG_NAME_CHARS, strlen(NodeTypeToString(pControl->NodeType)) + 1) );
wcsncpy( pControl->Control.szShortName, pControl->Control.szName, MIXER_SHORT_NAME_CHARS ); pControl->Control.szShortName[ MIXER_SHORT_NAME_CHARS - 1 ] = 0x00;
}
///////////////////////////////////////////////////////////////////////
//
// kmxlGetSuperMixCaps
//
//
NTSTATUS kmxlGetSuperMixCaps( IN PFILE_OBJECT pfo, IN ULONG ulNodeId, OUT PKSAUDIO_MIXCAP_TABLE* paMixCaps ) { NTSTATUS Status; ULONG Size; struct { ULONG InputChannels; ULONG OutputChannels; } SuperMixSize; PKSAUDIO_MIXCAP_TABLE pMixCaps = NULL;
PAGED_CODE();
ASSERT( pfo ); ASSERT( paMixCaps );
*paMixCaps = NULL;
//
// Query the node with just the first 2 DWORDs of the MIXCAP table.
// This will return the dimensions of the supermixer.
//
Status = kmxlNodeProperty( pfo, &KSPROPSETID_Audio, KSPROPERTY_AUDIO_MIX_LEVEL_CAPS, ulNodeId, 0, NULL, &SuperMixSize, sizeof( SuperMixSize ), KSPROPERTY_TYPE_GET ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_MIXER,( "kmxlNodeProperty failed with %X!", Status ) ); RETURN( Status ); }
//
// Allocate a MIXCAPS table big enough to hold all the entires.
// The size needs to include the first 2 DWORDs in the MIXCAP
// table besides the array ( InputCh * OutputCh ) of MIXCAPs
//
Size = sizeof( SuperMixSize ) + SuperMixSize.InputChannels * SuperMixSize.OutputChannels * sizeof( KSAUDIO_MIX_CAPS );
Status = AudioAllocateMemory_Paged(Size, TAG_AudS_SUPERMIX, ZERO_FILL_MEMORY | LIMIT_MEMORY, &pMixCaps ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_MIXER,( "failed to allocate caps memory!" ) ); RETURN( Status ); }
//
// Query the node once again to fill in the MIXCAPS structures.
//
Status = kmxlNodeProperty( pfo, &KSPROPSETID_Audio, KSPROPERTY_AUDIO_MIX_LEVEL_CAPS, ulNodeId, 0, NULL, pMixCaps, Size, KSPROPERTY_TYPE_GET ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_PROPERTY ,( "kmxlNodeProperty failed with %X!", Status ) ); AudioFreeMemory( Size,&pMixCaps ); RETURN( Status ); }
*paMixCaps = pMixCaps; RETURN( Status ); }
///////////////////////////////////////////////////////////////////////
//
// kmxlQueryPropertyRange
//
//
NTSTATUS kmxlQueryPropertyRange( IN PFILE_OBJECT pfo, IN CONST GUID* pguidPropSet, IN ULONG ulPropertyId, IN ULONG ulNodeId, OUT PKSPROPERTY_DESCRIPTION* ppPropDesc ) { NTSTATUS Status; KSNODEPROPERTY NodeProperty; KSPROPERTY_DESCRIPTION PropertyDescription; PKSPROPERTY_DESCRIPTION pPropDesc = NULL; ULONG BytesReturned;
PAGED_CODE(); //
// We don't want to allocate some arbitrary memory size if the driver
// does not set this value.
//
PropertyDescription.DescriptionSize=0;
NodeProperty.Property.Set = *pguidPropSet; NodeProperty.Property.Id = ulPropertyId; NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY; NodeProperty.NodeId = ulNodeId; NodeProperty.Reserved = 0;
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Query Node=%X",&NodeProperty) );
Status = KsSynchronousIoControlDevice( pfo, KernelMode, IOCTL_KS_PROPERTY, &NodeProperty, sizeof( NodeProperty ), &PropertyDescription, sizeof( PropertyDescription ), &BytesReturned );
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
if( !NT_SUCCESS( Status ) ) { RETURN( Status ); } //
// Never use a buffer that is smaller then we think it should be!
//
if( PropertyDescription.DescriptionSize < sizeof(KSPROPERTY_DESCRIPTION) ) { #ifdef DEBUG
DPF(DL_ERROR|FA_ALL,("KSPROPERTY_DESCRIPTION.DescriptionSize!>=sizeof(KSPROPERTY_DESCRIPTION)") ); #endif
RETURN(STATUS_INVALID_PARAMETER); }
Status = AudioAllocateMemory_Paged(PropertyDescription.DescriptionSize, TAG_Auda_PROPERTY, ZERO_FILL_MEMORY | LIMIT_MEMORY, &pPropDesc ); if( !NT_SUCCESS( Status ) ) { RETURN( Status ); }
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Get Node=%X",&NodeProperty) );
Status = KsSynchronousIoControlDevice( pfo, KernelMode, IOCTL_KS_PROPERTY, &NodeProperty, sizeof( NodeProperty ), pPropDesc, PropertyDescription.DescriptionSize, &BytesReturned );
DPF(DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY result=%X",Status) );
if( !NT_SUCCESS( Status ) ) { AudioFreeMemory( PropertyDescription.DescriptionSize,&pPropDesc ); RETURN( Status ); }
*ppPropDesc = pPropDesc; return( STATUS_SUCCESS ); }
///////////////////////////////////////////////////////////////////////
//
// kmxlGetControlChannels
//
//
NTSTATUS kmxlGetControlChannels( IN PFILE_OBJECT pfo, IN PMXLCONTROL pControl ) { NTSTATUS Status; PKSPROPERTY_DESCRIPTION pPropDesc = NULL; PKSPROPERTY_MEMBERSHEADER pMemberHeader; PCHANNEL_STEPPING pChannelStepping; ULONG i;
PAGED_CODE();
Status = kmxlQueryPropertyRange( pfo, &KSPROPSETID_Audio, pControl->PropertyId, pControl->Id, &pPropDesc );
//
// Do some checking on the returned value. Look for things that we
// support.
//
if ( NT_SUCCESS(Status) ) { ASSERT(pPropDesc); pMemberHeader = (PKSPROPERTY_MEMBERSHEADER) ( pPropDesc + 1 ); #ifdef DEBUG
//
// If the MembersListCount is greater then zero and the GUID's are equal
// then we will reference the pMemberHeader value that we create here.
// If we do, then we must make sure that the memory that we allocated
// is large enough to handle it!
//
if( ( pPropDesc->MembersListCount > 0 ) && (IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General )) ) { //
// if this is the case, we will touch the pMemberHeader->MembersCount
// field.
//
if (pPropDesc->DescriptionSize < (sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER)) ) { DPF(DL_ERROR|FA_ALL,("Incorrectly reported DescriptionSize in KSPROPERTY_DESCRIPTION structure") ); RETURN(STATUS_INVALID_PARAMETER); } } #endif
}
if( ( NT_SUCCESS( Status ) ) && ( pPropDesc->MembersListCount > 0 ) && ( IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General )) && ( pMemberHeader->MembersCount > 0 ) && ( pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL ) ) { //
// Volume controls may either be of MIXERTYPE_CONTROLF_UNIFORM
// or not. Uniform controls adjust all channels (or are mono
// in the first place) with one control. Those that have the
// fdwControl field set to 0 can set all channels of the volume
// independently. This information will have to come from the
// node itself, by checking to see if the node uniform control.
//
pControl->NumChannels = pMemberHeader->MembersCount;
if( (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM) || (pMemberHeader->MembersCount == 1) ) { pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; } } else {
// Fall through to using the old method which checks if volume is supported on
// each channel one at a time
Status = kmxlSupportsMultiChannelControl(pfo, pControl->Id, pControl->PropertyId); if (NT_SUCCESS(Status)) { pControl->NumChannels = 2; // we have stereo
pControl->Control.fdwControl = 0; } else { pControl->NumChannels = 1; // we have mono or master channel
pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; } }
// Done with the pPropDesc
AudioFreeMemory_Unknown( &pPropDesc );
ASSERT(pControl->NumChannels > 0); ASSERT(pControl->pChannelStepping == NULL);
Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ), TAG_AuDB_CHANNEL, ZERO_FILL_MEMORY, &pControl->pChannelStepping ); if( !NT_SUCCESS( Status ) ) { pControl->NumChannels = 0; return( Status ); }
// For a failure, set the default range.
pChannelStepping = pControl->pChannelStepping; for (i = 0; i < pControl->NumChannels; i++, pChannelStepping++) { pChannelStepping->MinValue = DEFAULT_RANGE_MIN; pChannelStepping->MaxValue = DEFAULT_RANGE_MAX; pChannelStepping->Steps = DEFAULT_RANGE_STEPS; }
return( STATUS_SUCCESS ); }
///////////////////////////////////////////////////////////////////////
//
// kmxlGetControlRange
//
//
NTSTATUS kmxlGetControlRange( IN PFILE_OBJECT pfo, IN PMXLCONTROL pControl ) { NTSTATUS Status; PKSPROPERTY_DESCRIPTION pPropDesc; PKSPROPERTY_MEMBERSHEADER pMemberHeader; PKSPROPERTY_STEPPING_LONG pSteppingLong; PCHANNEL_STEPPING pChannelStepping; ULONG i;
PAGED_CODE();
//
// Query the range for this control and initialize pControl in case of failure
//
ASSERT( pControl->pChannelStepping == NULL ); pControl->pChannelStepping = NULL;
Status = kmxlQueryPropertyRange( pfo, &KSPROPSETID_Audio, pControl->PropertyId, pControl->Id, &pPropDesc ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_MIXER,( "Failed to get BASICSUPPORT on control %x!", pControl ) ); // If BASICSUPPORT fails, kmxlGetControlChannels to handle the default behavior
Status = kmxlGetControlChannels( pfo, pControl ); RETURN( Status ); }
//
// Do some checking on the returned value. Look for things that we
// support.
//
if( ( pPropDesc->MembersListCount == 0 ) || ( !IsEqualGUID( &pPropDesc->PropTypeSet.Set, &KSPROPTYPESETID_General ) ) || ( pPropDesc->PropTypeSet.Id != VT_I4 ) ) { AudioFreeMemory_Unknown( &pPropDesc ); RETURN( STATUS_NOT_SUPPORTED ); }
pMemberHeader = (PKSPROPERTY_MEMBERSHEADER) ( pPropDesc + 1 );
#ifdef DEBUG
//
// If the MembersListCount is greater then zero and the GUID's are equal
// then we will reference the pMemberHeader value that we create here.
// If we do, then we must make sure that the memory that we allocated
// is large enough to handle it!
//
if (pPropDesc->DescriptionSize < (sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER)) ) { DPF(DL_ERROR|FA_ALL,("Incorrectly reported DescriptionSize in KSPROPERTY_DESCRIPTION structure") ); RETURN(STATUS_INVALID_PARAMETER); }
#endif
//
// Do some more checking on the returned value.
//
if ( (pMemberHeader->MembersCount == 0) || (pMemberHeader->MembersSize != sizeof(KSPROPERTY_STEPPING_LONG)) || (!(pMemberHeader->MembersFlags & KSPROPERTY_MEMBER_STEPPEDRANGES)) ) { AudioFreeMemory_Unknown( &pPropDesc ); RETURN( STATUS_NOT_SUPPORTED ); }
//
// Volume controls may either be of MIXERTYPE_CONTROLF_UNIFORM
// or not. Uniform controls adjust all channels (or are mono
// in the first place) with one control. Those that have the
// fdwControl field set to 0 can set all channels of the volume
// independently. This information will have to come from the
// node itself, by checking to see if the node uniform control.
//
if (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL) {
pControl->NumChannels = pMemberHeader->MembersCount;
if( (pMemberHeader->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM) || (pMemberHeader->MembersCount == 1) ) { pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; } } else { // Use the old method which checks if volume is supported on
// each channel one at a time
Status = kmxlSupportsMultiChannelControl(pfo, pControl->Id, pControl->PropertyId); if (NT_SUCCESS(Status)) { pControl->NumChannels = 2; // we have stereo
pControl->Control.fdwControl = 0; } else { pControl->NumChannels = 1; // we have mono or master channel
pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; } }
DPF(DL_TRACE|FA_MIXER,( "KMXL: Found %d channel ranges on control %x", pControl->NumChannels, pControl ) );
ASSERT(pControl->NumChannels > 0); ASSERT(pControl->pChannelStepping == NULL);
Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ), TAG_AuDA_CHANNEL, ZERO_FILL_MEMORY, &pControl->pChannelStepping ); if( !NT_SUCCESS( Status ) ) { pControl->NumChannels = 0; AudioFreeMemory_Unknown( &pPropDesc ); RETURN( Status ); }
pSteppingLong = (PKSPROPERTY_STEPPING_LONG) ( pMemberHeader + 1 ); pChannelStepping = pControl->pChannelStepping;
// Assuming that MemberSize is sizeof(KSPROPERTY_STEPPING_LONG) for now
for (i = 0; i < pControl->NumChannels; pChannelStepping++) { if ( pSteppingLong->Bounds.SignedMaximum == pSteppingLong->Bounds.SignedMinimum ) { DPF(DL_WARNING|FA_MIXER,( "Channel %d has pSteppingLong->Bounds.SignedMaximum == pSteppingLong->Bounds.SignedMinimum", i ) ); AudioFreeMemory_Unknown( &pPropDesc ); RETURN( STATUS_NOT_SUPPORTED ); }
pChannelStepping->MinValue = pSteppingLong->Bounds.SignedMinimum; pChannelStepping->MaxValue = pSteppingLong->Bounds.SignedMaximum;
if( pSteppingLong->SteppingDelta == 0 ) { DPF(DL_WARNING|FA_MIXER,( "Channel %d has pSteppingLong->SteppingDelta == 0", i ) ); AudioFreeMemory_Unknown( &pPropDesc ); RETURN( STATUS_NOT_SUPPORTED ); }
pChannelStepping->Steps = (LONG) ( ( (LONGLONG) pSteppingLong->Bounds.SignedMaximum - (LONGLONG) pSteppingLong->Bounds.SignedMinimum ) / (LONGLONG) pSteppingLong->SteppingDelta );
if( pChannelStepping->Steps == 0 ) { DPF(DL_WARNING|FA_MIXER, ( "Channel %d has pChannelStepping->Steps == 0", i ) ); AudioFreeMemory_Unknown( &pPropDesc ); RETURN( STATUS_NOT_SUPPORTED ); }
//
// Need to correct any out of bounds min, max and stepping values. This code use to be
// in persist.c.
//
/*
ASSERT ( pChannelStepping->MinValue >= -150*65536 && pChannelStepping->MinValue <= 150*65536 ); ASSERT ( pChannelStepping->MaxValue >= -150*65536 && pChannelStepping->MaxValue <= 150*65536 ); ASSERT ( pChannelStepping->Steps >= 0 && pChannelStepping->Steps <= 65535 ); */
if (!(pChannelStepping->MinValue >= -150*65536 && pChannelStepping->MinValue <= 150*65536)) { DPF(DL_WARNING|FA_MIXER, ("MinValue %X of Control %X of type %X on Channel %X is out of range! Correcting", pChannelStepping->MinValue, pControl->Control.dwControlID, pControl->Control.dwControlType, i) ); pChannelStepping->MinValue = DEFAULT_RANGE_MIN; } if (!(pChannelStepping->MaxValue >= -150*65536 && pChannelStepping->MaxValue <= 150*65536)) { DPF(DL_WARNING|FA_MIXER, ("MaxValue %X of Control %X of type %X on Channel %X is out of range! Correcting", pChannelStepping->MaxValue, pControl->Control.dwControlID, pControl->Control.dwControlType, i) ); pChannelStepping->MaxValue = DEFAULT_RANGE_MAX; } if (!(pChannelStepping->Steps >= 0 && pChannelStepping->Steps <= 65535)) { DPF(DL_WARNING|FA_MIXER, ("Steps %X of Control %X of type %X on Channel %X is out of range! Correcting", pChannelStepping->Steps, pControl->Control.dwControlID, pControl->Control.dwControlType, i) ); pChannelStepping->Steps = DEFAULT_RANGE_STEPS; pControl->Control.Metrics.cSteps = DEFAULT_RANGE_STEPS; }
DPF(DL_TRACE|FA_MIXER,( "Channel %d ranges from %08x to %08x by %08x steps", i, pChannelStepping->MinValue, pChannelStepping->MaxValue, pChannelStepping->Steps ) );
// Use the next Stepping structure, if there is one.
if (++i < pMemberHeader->MembersCount) { pSteppingLong++; } }
AudioFreeMemory_Unknown( &pPropDesc ); return( STATUS_SUCCESS ); }
///////////////////////////////////////////////////////////////////////
//
// FindTopologyConnectionTo
//
// Scans through the connection table looking for a connection that
// matches the ToNode/ToNodePin criteria.
//
//
ULONG kmxlFindTopologyConnectionTo( IN CONST KSTOPOLOGY_CONNECTION* pConnections, // The connection table
IN ULONG cConnections, // The # of connections
IN ULONG StartIndex, // Index to start search
IN ULONG ToNode, // The Node ID to look for
IN ULONG ToNodePin // The Pin ID to look for
) { ULONG i;
PAGED_CODE(); for( i = StartIndex; i < cConnections; i++ ) { if( ( ( pConnections[ i ].ToNode == ToNode ) || ( ToNode == PINID_WILDCARD ) ) && ( ( pConnections[ i ].ToNodePin == ToNodePin ) || ( ToNodePin == PINID_WILDCARD ) ) ) { return( i ); } } return( (ULONG) -1 ); }
///////////////////////////////////////////////////////////////////////
//
// kmxlGetNumMuxLines
//
//
DWORD kmxlGetNumMuxLines( IN PKSTOPOLOGY pTopology, IN ULONG NodeId ) { ULONG Index = 0, Count = 0;
PAGED_CODE(); do {
Index = kmxlFindTopologyConnectionTo( pTopology->TopologyConnections, pTopology->TopologyConnectionsCount, Index, NodeId, PINID_WILDCARD ); if( Index == (ULONG) -1 ) { break; }
++Count; ++Index;
} while( 1 );
return( Count ); }
///////////////////////////////////////////////////////////////////////
//
// kmxlGetMuxLineNames
//
//
VOID kmxlGetMuxLineNames( IN PMIXEROBJECT pmxobj, IN PMXLCONTROL pControl ) { PMXLNODE pNode; ULONG i, Index = 0, NodeId;
ASSERT( pmxobj ); ASSERT( pControl ); PAGED_CODE();
if( !NT_SUCCESS( AudioAllocateMemory_Paged(pControl->Control.cMultipleItems * sizeof( MIXERCONTROLDETAILS_LISTTEXT ), TAG_AudG_GETMUXLINE, ZERO_FILL_MEMORY, &pControl->Parameters.lpmcd_lt ) ) ) { DPF(DL_WARNING|FA_USER,("Failing non failable routine!") ); return; }
if( !NT_SUCCESS( AudioAllocateMemory_Paged(pControl->Control.cMultipleItems * sizeof( ULONG ), TAG_AudG_GETMUXLINE, ZERO_FILL_MEMORY, &pControl->Parameters.pPins ) ) ) { AudioFreeMemory( pControl->Control.cMultipleItems * sizeof( MIXERCONTROLDETAILS_LISTTEXT ), &pControl->Parameters.lpmcd_lt ); pControl->Parameters.Count = 0; DPF(DL_WARNING|FA_USER,("Failing non failable routine!") ); return; }
ASSERT( pControl->Parameters.lpmcd_lt ); ASSERT( pControl->Parameters.pPins );
pControl->Parameters.Count = pControl->Control.cMultipleItems;
for( i = 0; i < pControl->Control.cMultipleItems; i++ ) {
Index = kmxlFindTopologyConnectionTo( pmxobj->pTopology->TopologyConnections, pmxobj->pTopology->TopologyConnectionsCount, Index, pControl->Id, PINID_WILDCARD ); if( Index != (ULONG) -1 ) {
NodeId = pmxobj->pTopology->TopologyConnections[ Index ].FromNode; if( NodeId == KSFILTER_NODE ) { pControl->Parameters.lpmcd_lt[ i ].dwParam1 = pmxobj->pTopology->TopologyConnections[ Index ].FromNodePin; pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1; pControl->Parameters.pPins[ i ] = pmxobj->pTopology->TopologyConnections[ Index ].ToNodePin;
++Index; continue; } else { pNode = &pmxobj->pNodeTable[ NodeId ]; } ++Index; while( pNode ) {
if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) || IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) || ( kmxlParentListLength( pNode ) > 1 ) ) { pControl->Parameters.lpmcd_lt[ i ].dwParam1 = 0x8000 + pNode->Id; pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1; pControl->Parameters.pPins[ i ] = pmxobj->pTopology->TopologyConnections[ Index - 1 ].ToNodePin; break; }
if( pNode->Type == SOURCE ) { pControl->Parameters.lpmcd_lt[ i ].dwParam1 = pNode->Id; pControl->Parameters.lpmcd_lt[ i ].dwParam2 = (DWORD) -1; pControl->Parameters.pPins[ i ] = pmxobj->pTopology->TopologyConnections[ Index - 1 ].ToNodePin; break; } // if
if( kmxlFirstParentNode( pNode ) ) { pNode = (kmxlFirstParentNode( pNode ))->pNode; } else { pNode = NULL; } } // while
} // if
} // for
} // kmxlGetMuxLineNames
|