//--------------------------------------------------------------------------- // // 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