//--------------------------------------------------------------------------- // // Module: persist.c // // Description: // // Contains the routines that persist the mixer line driver settings. // // //@@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. // //--------------------------------------------------------------------------- #include "WDMSYS.H" #include "kmxluser.h" /////////////////////////////////////////////////////////////////////// // // kmxlGetInterfaceName // // NTSTATUS kmxlGetInterfaceName( IN PFILE_OBJECT pfo, IN ULONG Device, OUT PWCHAR* pszInterfaceName ) { NTSTATUS Status = STATUS_SUCCESS; ULONG Size; WCHAR* szInterfaceName = NULL; PAGED_CODE(); ASSERT( pfo ); // // Retrieve the size of the internface name. // Status = GetSysAudioProperty( pfo, KSPROPERTY_SYSAUDIO_DEVICE_INTERFACE_NAME, Device, sizeof( Size ), &Size ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_PERSIST,("GetSysAudioProperty failed Status=%X",Status) ); goto exit; } // // Allocate enough memory to hold the interface name // Status = AudioAllocateMemory_Paged(Size, TAG_Audp_NAME, ZERO_FILL_MEMORY | LIMIT_MEMORY, &szInterfaceName ); if( !NT_SUCCESS( Status ) ) { goto exit; } ASSERT( szInterfaceName ); // // Retieve the interface name for this device. // Status = GetSysAudioProperty( pfo, KSPROPERTY_SYSAUDIO_DEVICE_INTERFACE_NAME, Device, Size, szInterfaceName ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_PERSIST,("GetSysAudioProperty failed Status=%X",Status) ); goto exit; } exit: if( !NT_SUCCESS( Status ) ) { AudioFreeMemory_Unknown( &szInterfaceName ); } else { *pszInterfaceName = szInterfaceName; } RETURN( Status ); } /////////////////////////////////////////////////////////////////////// // // kmxlOpenInterfaceKey // // NTSTATUS kmxlOpenInterfaceKey( IN PFILE_OBJECT pfo, IN ULONG Device, OUT HANDLE* phKey ) { NTSTATUS Status; HANDLE hKey; WCHAR* szName; UNICODE_STRING ustr; PAGED_CODE(); Status = kmxlGetInterfaceName( pfo, Device, &szName ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_PERSIST,("kmxlGetInterfaceName failed Status=%X",Status) ); RETURN( Status ); } RtlInitUnicodeString( &ustr, szName ); Status = IoOpenDeviceInterfaceRegistryKey( &ustr, KEY_ALL_ACCESS, &hKey ); if( !NT_SUCCESS( Status ) ) { AudioFreeMemory_Unknown( &szName ); RETURN( Status ); } *phKey = hKey; AudioFreeMemory_Unknown( &szName ); return( STATUS_SUCCESS ); } /////////////////////////////////////////////////////////////////////// // // kmxlRegCreateKey // // NTSTATUS kmxlRegCreateKey( IN HANDLE hRootKey, IN PWCHAR szKeyName, OUT PHANDLE phKey ) { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING ustr; ULONG Disposition; PAGED_CODE(); RtlInitUnicodeString( &ustr, szKeyName ); InitializeObjectAttributes( &ObjectAttributes, &ustr, OBJ_KERNEL_HANDLE, // Attributes hRootKey, NULL // Security ); return( ZwCreateKey( phKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, // TitleIndex NULL, // Class REG_OPTION_NON_VOLATILE, &Disposition ) ); } /////////////////////////////////////////////////////////////////////// // // kmxlRegOpenKey // // NTSTATUS kmxlRegOpenKey( IN HANDLE hRootKey, IN PWCHAR szKeyName, OUT PHANDLE phKey ) { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING ustr; PAGED_CODE(); RtlInitUnicodeString( &ustr, szKeyName ); InitializeObjectAttributes( &ObjectAttributes, &ustr, OBJ_KERNEL_HANDLE, hRootKey, NULL ); return( ZwOpenKey( phKey, KEY_ALL_ACCESS, &ObjectAttributes ) ); } /////////////////////////////////////////////////////////////////////// // // kmxlRegSetValue // // NTSTATUS kmxlRegSetValue( IN HANDLE hKey, IN PWCHAR szValueName, IN ULONG Type, IN PVOID pData, IN ULONG cbData ) { UNICODE_STRING ustr; PAGED_CODE(); RtlInitUnicodeString( &ustr, szValueName ); return( ZwSetValueKey( hKey, &ustr, 0, Type, pData, cbData ) ); } /////////////////////////////////////////////////////////////////////// // // kmxlRegQueryValue // // NTSTATUS kmxlRegQueryValue( IN HANDLE hKey, IN PWCHAR szValueName, IN PVOID pData, IN ULONG cbData, OUT PULONG pResultLength ) { NTSTATUS Status; UNICODE_STRING ustr; KEY_VALUE_FULL_INFORMATION FullInfoHeader; PKEY_VALUE_FULL_INFORMATION FullInfoBuffer = NULL; PAGED_CODE(); RtlInitUnicodeString( &ustr, szValueName ); Status = ZwQueryValueKey( hKey, &ustr, KeyValueFullInformation, &FullInfoHeader, sizeof( FullInfoHeader ), pResultLength ); if( !NT_SUCCESS( Status ) ) { if( Status == STATUS_BUFFER_OVERFLOW ) { if( !NT_SUCCESS( AudioAllocateMemory_Paged(*pResultLength, TAG_AudA_PROPERTY, ZERO_FILL_MEMORY, &FullInfoBuffer ) ) ) { RETURN( STATUS_INSUFFICIENT_RESOURCES ); } Status = ZwQueryValueKey( hKey, &ustr, KeyValueFullInformation, FullInfoBuffer, *pResultLength, pResultLength ); if( NT_SUCCESS( Status ) ) { RtlCopyMemory( pData, (PUCHAR) FullInfoBuffer + FullInfoBuffer->DataOffset, cbData ); } AudioFreeMemory_Unknown( &FullInfoBuffer ); } } DPFRETURN( Status,(2,Status,STATUS_OBJECT_NAME_NOT_FOUND) ); } /////////////////////////////////////////////////////////////////////// // // kmxlRegOpenMixerKey // // NTSTATUS kmxlRegOpenMixerKey( IN PFILE_OBJECT pfo, IN PMIXERDEVICE pmxd, OUT PHANDLE phMixerKey ) { NTSTATUS Status; HANDLE hKey; PAGED_CODE(); Status = kmxlOpenInterfaceKey( pfo, pmxd->Device, &hKey ); if( !NT_SUCCESS( Status ) ) { RETURN( Status ); } Status = kmxlRegOpenKey( hKey, MIXER_KEY_NAME, phMixerKey ); if( NT_SUCCESS( Status ) ) { kmxlRegCloseKey( hKey ); } RETURN( Status ); } /////////////////////////////////////////////////////////////////////// // // kmxlFindDestById // // PMXLLINE kmxlFindDestById( IN LINELIST listLines, IN ULONG LineId ) { PMXLLINE pLine; PAGED_CODE(); pLine = kmxlFirstInList( listLines ); while( pLine ) { if( pLine->Line.dwLineID == LineId ) { return( pLine ); } pLine = kmxlNextLine( pLine ); } return( NULL ); } extern instancereleasedcount; /////////////////////////////////////////////////////////////////////// // // // NTSTATUS kmxlGetCurrentControlValue( IN PFILE_OBJECT pfo, // The instance to persist for IN PMIXERDEVICE pmxd, IN PMXLLINE pLine, IN PMXLCONTROL pControl, // The control to retrieve OUT PVOID* ppaDetails ) { NTSTATUS Status; LPDEVICEINFO pDevInfo = NULL; MIXERCONTROLDETAILS mcd; PMIXERCONTROLDETAILS_UNSIGNED paDetails = NULL; ULONG Index; ULONG Devices; PAGED_CODE(); *ppaDetails = NULL; // // Initialize a Device Info structure to make the query look like // it comes from user mode. // Status = kmxlAllocDeviceInfo(&pDevInfo, pmxd->DeviceInterface, MIXER_GETCONTROLDETAILSF_VALUE, TAG_AudD_DEVICEINFO ); if (!NT_SUCCESS(Status)) { RETURN( Status ); } for( Devices = 0, Index = 0; Devices < MAXNUMDEVS; Devices++ ) { if( pmxd == &pmxd->pWdmaContext->MixerDevs[ Devices ] ) { pDevInfo->DeviceNumber = Index; break; } if ( !MyWcsicmp(pmxd->DeviceInterface, pmxd->pWdmaContext->MixerDevs[ Devices ].DeviceInterface) ) { Index++; } } // // Create an MIXERCONTROLDETAILS structure for this query. // RtlZeroMemory( &mcd, sizeof( MIXERCONTROLDETAILS ) ); mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = pControl->Control.dwControlID; mcd.cMultipleItems = pControl->Control.cMultipleItems; mcd.cChannels = pControl->NumChannels; if( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) { Status = AudioAllocateMemory_Paged(mcd.cMultipleItems * sizeof( MIXERCONTROLDETAILS_UNSIGNED ), TAG_Audd_DETAILS, ZERO_FILL_MEMORY, &paDetails ); mcd.cbDetails = mcd.cMultipleItems * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); } else { Status = AudioAllocateMemory_Paged(mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ), TAG_Audd_DETAILS, ZERO_FILL_MEMORY, &paDetails ); mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); } if (NT_SUCCESS(Status)) { mcd.paDetails = paDetails; // // Make the actual query call. // Status = kmxlGetControlDetailsHandler(pmxd->pWdmaContext, pDevInfo, &mcd, paDetails); if( NT_SUCCESS( Status ) ) { *ppaDetails = paDetails; } else { AudioFreeMemory_Unknown( &paDetails ); } } AudioFreeMemory_Unknown( &pDevInfo ); RETURN( Status ); } /////////////////////////////////////////////////////////////////////// // // // NTSTATUS kmxlSetCurrentControlValue( IN PFILE_OBJECT pfo, // The instance to persist for IN PMIXERDEVICE pmxd, IN PMXLLINE pLine, IN PMXLCONTROL pControl, // The control to retrieve IN PVOID paDetails ) { NTSTATUS Status; LPDEVICEINFO pDevInfo = NULL; MIXERCONTROLDETAILS mcd; ULONG Index; ULONG Devices; PAGED_CODE(); // // Initialize a Device Info structure to make the query look like // it comes from user mode. // Status = kmxlAllocDeviceInfo(&pDevInfo, pmxd->DeviceInterface, MIXER_SETCONTROLDETAILSF_VALUE, TAG_AudD_DEVICEINFO ); if (!NT_SUCCESS(Status)) RETURN( Status ); for( Devices = 0, Index = 0; Devices < MAXNUMDEVS; Devices++ ) { if( pmxd == &pmxd->pWdmaContext->MixerDevs[ Devices ] ) { pDevInfo->DeviceNumber = Index; break; } if ( !MyWcsicmp(pmxd->DeviceInterface, pmxd->pWdmaContext->MixerDevs[ Devices ].DeviceInterface) ) { Index++; } } // // Create an MIXERCONTROLDETAILS structure for this query. // RtlZeroMemory( &mcd, sizeof( MIXERCONTROLDETAILS ) ); mcd.cMultipleItems = pControl->Control.cMultipleItems; mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = pControl->Control.dwControlID; mcd.cChannels = pControl->NumChannels; // // For a MUX, we know that NumChannels will be zero and cChannels will be zero. // if( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) { mcd.cbDetails = mcd.cMultipleItems * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); } else { mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); } mcd.paDetails = paDetails; // // Make the actual query call. // Status = kmxlSetControlDetailsHandler( pmxd->pWdmaContext, pDevInfo, &mcd, paDetails, 0 ); // // workitem: Should map the error code here for invalid topologies! // AudioFreeMemory_Unknown(&pDevInfo); RETURN( Status ); } /////////////////////////////////////////////////////////////////////// // // kmxlPersistAll // // NTSTATUS kmxlPersistAll( IN PFILE_OBJECT pfo, // The instance to persist IN PMIXERDEVICE pmxd ) { NTSTATUS Status = STATUS_SUCCESS; HANDLE hKey = NULL, hMixerKey = NULL, hLineKey = NULL, hAllControlsKey = NULL, hControlKey = NULL; WCHAR sz[ 16 ]; ULONG LineNum, ControlNum, i, Channels; PMXLLINE pLine; PMXLCONTROL pControl; PVOID paDetails; PCHANNEL_STEPPING pChannelStepping; BOOL bValidMultichannel; PAGED_CODE(); ASSERT( pfo ); ASSERT( pmxd ); Status = kmxlOpenInterfaceKey( pfo, pmxd->Device, &hKey ); if( !NT_SUCCESS( Status ) ) { goto exit; } Status = kmxlRegCreateKey( hKey, MIXER_KEY_NAME, &hMixerKey ); if( !NT_SUCCESS( Status ) ) { goto exit; } kmxlRegCloseKey( hKey ); i = kmxlListLength( pmxd->listLines ); kmxlRegSetValue( hMixerKey, LINE_COUNT_VALUE_NAME, REG_DWORD, &i, sizeof( i ) ); LineNum = 0; pLine = kmxlFirstInList( pmxd->listLines ); while( pLine ) { // // Store the line id as the key // swprintf( sz, LINE_KEY_NAME_FORMAT, LineNum++ ); Status = kmxlRegCreateKey( hMixerKey, sz, &hLineKey ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_PERSIST,("kmxlRegCreateKey failed Status=%X",Status) ); goto exit; } kmxlRegSetValue( hLineKey, LINE_ID_VALUE_NAME, REG_DWORD, &pLine->Line.dwLineID, sizeof( pLine->Line.dwLineID ) ); // // Save the number of controls underneath the line id key // kmxlRegSetValue( hLineKey, CONTROL_COUNT_VALUE_NAME, REG_DWORD, &pLine->Line.cControls, sizeof( pLine->Line.cControls ) ); // // Save the source pin Id underneath the line id key // kmxlRegSetValue( hLineKey, SOURCE_ID_VALUE_NAME, REG_DWORD, &pLine->SourceId, sizeof( pLine->SourceId ) ); // // Save the destination pin Id underneath the line id key // kmxlRegSetValue( hLineKey, DEST_ID_VALUE_NAME, REG_DWORD, &pLine->DestId, sizeof( pLine->DestId ) ); // // Create the Controls key to store all the controls under // Status = kmxlRegCreateKey( hLineKey, CONTROLS_KEY_NAME, &hAllControlsKey ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_PERSIST,("kmxlRegCreateKey failed Status=%X",Status) ); goto exit; } kmxlRegCloseKey( hLineKey ); // // Persist all the controls underneath the controls key // ControlNum = 0; pControl = kmxlFirstInList( pLine->Controls ); while( pControl ) { swprintf( sz, CONTROL_KEY_NAME_FORMAT, ControlNum++ ); Status = kmxlRegCreateKey( hAllControlsKey, sz, &hControlKey ); if( !NT_SUCCESS( Status ) ) { DPF(DL_WARNING|FA_PERSIST,("kmxlRegCreateKey failed Status=%X",Status) ); goto exit; } kmxlRegSetValue( hControlKey, CONTROL_TYPE_VALUE_NAME, REG_DWORD, &pControl->Control.dwControlType, sizeof( pControl->Control.dwControlType ) ); kmxlRegSetValue( hControlKey, CONTROL_MULTIPLEITEMS_VALUE_NAME, REG_DWORD, &pControl->Control.cMultipleItems, sizeof( pControl->Control.cMultipleItems ) ); // // As in kmxlRetrieveAll, this code should be in the control creation // code path as well as here. We should never write anything to the registry // that doesn't conform to what we understand. // if (pControl->pChannelStepping) { pChannelStepping = pControl->pChannelStepping; for (i = 0; i < pControl->NumChannels; i++, pChannelStepping++) { /* 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_PERSIST, ("MinValue %X of Control %X of type %X on Line %X Channel %X is out of range!", pChannelStepping->MinValue, pControl->Control.dwControlID, pControl->Control.dwControlType, pLine->Line.dwLineID, i) ); pChannelStepping->MinValue = DEFAULT_RANGE_MIN; } if (!(pChannelStepping->MaxValue >= -150*65536 && pChannelStepping->MaxValue <= 150*65536)) { DPF(DL_WARNING|FA_PERSIST, ("MaxValue %X of Control %X of type %X on Line %X Channel %X is out of range!", pChannelStepping->MaxValue, pControl->Control.dwControlID, pControl->Control.dwControlType, pLine->Line.dwLineID, i) ); pChannelStepping->MaxValue = DEFAULT_RANGE_MAX; } if (!(pChannelStepping->Steps >= 0 && pChannelStepping->Steps <= 65535)) { DPF(DL_WARNING|FA_PERSIST, ("Steps %X of Control %X of type %X on Line %X Channel %X is out of range!", pChannelStepping->Steps, pControl->Control.dwControlID, pControl->Control.dwControlType, pLine->Line.dwLineID, i) ); pChannelStepping->Steps = DEFAULT_RANGE_STEPS; pControl->Control.Metrics.cSteps = DEFAULT_RANGE_STEPS; } } } Status = kmxlGetCurrentControlValue( pfo, pmxd, pLine, pControl, &paDetails ); if( NT_SUCCESS( Status ) ) { if( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) { for( i = 0; i < pControl->Control.cMultipleItems; i++ ) { swprintf( sz, MULTIPLEITEM_VALUE_NAME_FORMAT, i ); Status = kmxlRegSetValue( hControlKey, sz, REG_DWORD, &((PMIXERCONTROLDETAILS_UNSIGNED) paDetails)[ i ], sizeof( ((PMIXERCONTROLDETAILS_UNSIGNED) paDetails)[ i ] ) ); if( !NT_SUCCESS( Status ) ) { AudioFreeMemory_Unknown( &paDetails ); DPF(DL_WARNING|FA_PERSIST,("KmxlRegSetValue failed Status=%X",Status) ); goto exit; } } } else { Channels = pControl->NumChannels; kmxlRegSetValue( hControlKey, CHANNEL_COUNT_VALUE_NAME, REG_DWORD, &Channels, sizeof( Channels ) ); for( i = 0; i < Channels; i++ ) { swprintf( sz, CHANNEL_VALUE_NAME_FORMAT, i ); Status = kmxlRegSetValue( hControlKey, sz, REG_DWORD, &((PMIXERCONTROLDETAILS_UNSIGNED) paDetails)[ i ], sizeof( ((PMIXERCONTROLDETAILS_UNSIGNED) paDetails)[ i ] ) ); if( !NT_SUCCESS( Status ) ) { AudioFreeMemory_Unknown( &paDetails ); DPF(DL_WARNING|FA_PERSIST,("KmxlRegSetValue failed Status=%X",Status) ); goto exit; } } } AudioFreeMemory_Unknown( &paDetails ); } kmxlRegCloseKey( hControlKey ); pControl = kmxlNextControl( pControl ); } kmxlRegCloseKey( hAllControlsKey ); pLine = kmxlNextLine( pLine ); } // // After all of the persisting is done, save out a value indicating that the channel // values are all valid for a multichannel restore. This is to avoid the situation // where the data for some of the channels is invalid. // bValidMultichannel = TRUE; kmxlRegSetValue( hMixerKey, VALID_MULTICHANNEL_MIXER_VALUE_NAME, REG_DWORD, &bValidMultichannel, sizeof( bValidMultichannel ) ); kmxlRegCloseKey( hMixerKey ); exit: if( hControlKey ) { kmxlRegCloseKey( hControlKey ); } if( hAllControlsKey ) { kmxlRegCloseKey( hAllControlsKey ); } if( hLineKey ) { kmxlRegCloseKey( hLineKey ); } if( hMixerKey ) { kmxlRegCloseKey( hMixerKey ); } if( hKey ) { kmxlRegCloseKey( hKey ); } RETURN( Status ); } /////////////////////////////////////////////////////////////////////// // // kmxlRetrieveAll // // NTSTATUS kmxlRetrieveAll( IN PFILE_OBJECT pfo, // The instance to retrieve IN PMIXERDEVICE pmxd // Mixer device info ) { NTSTATUS Status; WCHAR sz[ 16 ]; HANDLE hMixerKey = NULL, hLineKey = NULL, hAllControlsKey = NULL, hControlKey = NULL; ULONG ResultLength, Value, NumChannels, ControlCount; ULONG LineCount = 0; ULONG i,j; BOOL bInvalidTopology = FALSE; PMXLLINE pLine; PMXLCONTROL pControl; MIXERCONTROLDETAILS_UNSIGNED* paDetails = NULL; PCHANNEL_STEPPING pChannelStepping; BOOL bValidMultichannel = FALSE; PAGED_CODE(); // // Open the Mixer key under the interface key. If somethings goes // wrong here, this does not have a valid topology. // Status = kmxlRegOpenMixerKey( pfo, pmxd, &hMixerKey ); if( !NT_SUCCESS( Status ) ) { DPF(DL_TRACE|FA_PERSIST,( "failed to open mixer reg key!" ) ); bInvalidTopology = TRUE; goto exit; } // if // // Query for a valid multichannel mixer persistance // Status = kmxlRegQueryValue( hMixerKey, VALID_MULTICHANNEL_MIXER_VALUE_NAME, &bValidMultichannel, sizeof( bValidMultichannel ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { // This should be set to FALSE for upgrades from Win2000 where the registry // entries could be invalid for channels other than the first channel. bValidMultichannel = FALSE; } // if // // Query the total number of lines that have been persisted. // Status = kmxlRegQueryValue( hMixerKey, LINE_COUNT_VALUE_NAME, &LineCount, sizeof( LineCount ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { DPF(DL_TRACE|FA_PERSIST,( "failed to read number of persisted lines!" ) ); bInvalidTopology = TRUE; goto exit; } // if // // Check to ensure the number of lines persisted is the same as // what is stored in memory. // if( LineCount != kmxlListLength( pmxd->listLines ) ) { DPF(DL_TRACE|FA_PERSIST,( "# of persisted lines does not match current topology!" ) ); bInvalidTopology = TRUE; goto exit; } // if for( i = 0; i < LineCount; i++ ) { // // Construct the line key name and open the key. // swprintf( sz, LINE_KEY_NAME_FORMAT, i ); Status = kmxlRegOpenKey( hMixerKey, sz, &hLineKey ); if( !NT_SUCCESS( Status ) ) { break; } // if // // Query the line Id of this line. // Status = kmxlRegQueryValue( hLineKey, LINE_ID_VALUE_NAME, &Value, sizeof( Value ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { continue; } // if // // Verify the line Id is valid and retrieve a pointer to the line // structure. // pLine = kmxlFindDestById( pmxd->listLines, Value ); if( pLine == NULL ) { DPF(DL_TRACE|FA_PERSIST,( "persisted line ID is invalid!" ) ); bInvalidTopology = TRUE; break; } // if // // Retrieve the number of controls for this line. // Status = kmxlRegQueryValue( hLineKey, CONTROL_COUNT_VALUE_NAME, &Value, sizeof( Value ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { kmxlRegCloseKey( hLineKey ); continue; } // if if( Value != pLine->Line.cControls ) { DPF(DL_TRACE|FA_PERSIST,( "the number of controls for line %x is invalid!", pLine->Line.dwLineID ) ); bInvalidTopology = TRUE; break; } // if Status = kmxlRegOpenKey( hLineKey, CONTROLS_KEY_NAME, &hAllControlsKey ); if( !NT_SUCCESS( Status ) ) { kmxlRegCloseKey( hLineKey ); continue; } // if // // Query all the information for each control // ControlCount = 0; pControl = kmxlFirstInList( pLine->Controls ); while( pControl ) { swprintf( sz, CONTROL_KEY_NAME_FORMAT, ControlCount++ ); Status = kmxlRegOpenKey( hAllControlsKey, sz, &hControlKey ); if( !NT_SUCCESS( Status ) ) { break; } // if Status = kmxlRegQueryValue( hControlKey, CHANNEL_COUNT_VALUE_NAME, &NumChannels, sizeof( NumChannels ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { if( pControl->Control.cMultipleItems == 0 ) { // // Controls that have multiple items (such as MUXes) // don't have channel counts. If this control does // not have multiple items, then there is a problem // in the registry. // kmxlRegCloseKey( hControlKey ); pControl = kmxlNextControl( pControl ); continue; } } // if if( ( NumChannels != pControl->NumChannels ) && ( pControl->Control.cMultipleItems == 0 ) ) { DPF(DL_TRACE|FA_PERSIST,( "the number of channels for control %d on line %x is invalid.", pControl->Control.dwControlID, pLine->Line.dwLineID ) ); bInvalidTopology = TRUE; goto exit; } Status = kmxlRegQueryValue( hControlKey, CONTROL_TYPE_VALUE_NAME, &Value, sizeof( Value ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { kmxlRegCloseKey( hControlKey ); pControl = kmxlNextControl( pControl ); continue; } // if if( Value != pControl->Control.dwControlType ) { kmxlRegCloseKey( hControlKey ); pControl = kmxlNextControl( pControl ); continue; } // if Status = kmxlRegQueryValue( hControlKey, CONTROL_MULTIPLEITEMS_VALUE_NAME, &Value, sizeof( Value ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { bInvalidTopology = TRUE; DPF(DL_TRACE|FA_PERSIST, ( "cMultipleItems value not found!" ) ); goto exit; } if( Value != pControl->Control.cMultipleItems ) { bInvalidTopology = TRUE; DPF(DL_TRACE|FA_PERSIST, ( "cMultipleItems does not match for control %x!", pControl->Control.dwControlID ) ); goto exit; } // // Allocate memory for the data structures and // set the value. // if( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) { if( !NT_SUCCESS( AudioAllocateMemory_Paged(pControl->Control.cMultipleItems * sizeof( MIXERCONTROLDETAILS_UNSIGNED ), TAG_Audd_DETAILS, ZERO_FILL_MEMORY, &paDetails ) ) ) { kmxlRegCloseKey( hControlKey ); pControl = kmxlNextControl( pControl ); continue; } for( Value = 0; Value < pControl->Control.cMultipleItems; Value++ ) { swprintf( sz, MULTIPLEITEM_VALUE_NAME_FORMAT, Value ); Status = kmxlRegQueryValue( hControlKey, sz, &paDetails[ Value ].dwValue, sizeof( paDetails[ Value ].dwValue ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { break; } } } else { if( !NT_SUCCESS( AudioAllocateMemory_Paged(NumChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ), TAG_Audd_DETAILS, ZERO_FILL_MEMORY, &paDetails ) ) ) { kmxlRegCloseKey( hControlKey ); pControl = kmxlNextControl( pControl ); continue; } // if for( Value = 0; Value < NumChannels; Value++ ) { // check to see if the persisted values are valid for all channels if ( ( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE ) && ( bValidMultichannel == FALSE ) ) { swprintf( sz, CHANNEL_VALUE_NAME_FORMAT, 0 ); // Lock the persistance key to the first channel. // This is the only channel that we know is valid // at this time. } else { swprintf( sz, CHANNEL_VALUE_NAME_FORMAT, Value ); } Status = kmxlRegQueryValue( hControlKey, sz, &paDetails[ Value ].dwValue, sizeof( paDetails[ Value ].dwValue ), &ResultLength ); if( !NT_SUCCESS( Status ) ) { break; } // if } // for( Value ); } if( NT_SUCCESS( Status ) ) { // // This correction code should be here along with in the control // creation code. Basically, if we're reading something from the // registry that doesn't conform, we fix it up, but, chances are // it should be in the correct form. // if (pControl->pChannelStepping) { pChannelStepping = pControl->pChannelStepping; for (j = 0; j < pControl->NumChannels; j++, pChannelStepping++) { /* 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_PERSIST, ("MinValue %X of Control %X of type %X on Line %X Channel %X is out of range!", pChannelStepping->MinValue, pControl->Control.dwControlID, pControl->Control.dwControlType, pLine->Line.dwLineID, j) ); pChannelStepping->MinValue = DEFAULT_RANGE_MIN; } if (!(pChannelStepping->MaxValue >= -150*65536 && pChannelStepping->MaxValue <= 150*65536)) { DPF(DL_WARNING|FA_PERSIST, ("MaxValue %X of Control %X of type %X on Line %X Channel %X is out of range!", pChannelStepping->MaxValue, pControl->Control.dwControlID, pControl->Control.dwControlType, pLine->Line.dwLineID, j) ); pChannelStepping->MaxValue = DEFAULT_RANGE_MAX; } if (!(pChannelStepping->Steps >= 0 && pChannelStepping->Steps <= 65535)) { DPF(DL_TRACE|FA_PERSIST, ("Steps %X of Control %X of type %X on Line %X Channel %X is out of range!", pChannelStepping->Steps, pControl->Control.dwControlID, pControl->Control.dwControlType, pLine->Line.dwLineID, j) ); pChannelStepping->Steps = DEFAULT_RANGE_STEPS; pControl->Control.Metrics.cSteps = DEFAULT_RANGE_STEPS; } } } kmxlSetCurrentControlValue( pfo, pmxd, pLine, pControl, paDetails ); } AudioFreeMemory_Unknown( &paDetails ); kmxlRegCloseKey( hControlKey ); pControl = kmxlNextControl( pControl ); } // while( pControl ); kmxlRegCloseKey( hAllControlsKey ); kmxlRegCloseKey( hLineKey ); } // for( i ); exit: if( hLineKey ) { kmxlRegCloseKey( hLineKey ); } if( hMixerKey ) { kmxlRegCloseKey( hMixerKey ); } if( bInvalidTopology ) { DPF(DL_TRACE|FA_PERSIST,( "Invalid topology persisted or key not found. Rebuilding." ) ); Status = kmxlRegOpenMixerKey( pfo, pmxd, &hMixerKey ); if( NT_SUCCESS( Status ) ) { ZwDeleteKey( hMixerKey ); } return( kmxlPersistAll( pfo, pmxd ) ); } return( STATUS_SUCCESS ); } /////////////////////////////////////////////////////////////////////// // // kmxlFindLineForControl // // PMXLLINE kmxlFindLineForControl( IN PMXLCONTROL pControl, IN LINELIST listLines ) { PMXLLINE pLine; PMXLCONTROL pTControl; PAGED_CODE(); if( pControl == NULL ) { return( NULL ); } if( listLines == NULL ) { return( NULL ); } pLine = kmxlFirstInList( listLines ); while( pLine ) { pTControl = kmxlFirstInList( pLine->Controls ); while( pTControl ) { if( pTControl == pControl ) { return( pLine ); } pTControl = kmxlNextControl( pTControl ); } pLine = kmxlNextLine( pLine ); } return( NULL ); } /////////////////////////////////////////////////////////////////////// // // kmxlPersistSingleControl // // NTSTATUS kmxlPersistSingleControl( IN PFILE_OBJECT pfo, // The instance to retrieve IN PMIXERDEVICE pmxd, // Mixer device info IN PMXLCONTROL pControl, // The control to persist IN PVOID paDetails // The channel values to persist ) { NTSTATUS Status; HANDLE hMixerKey = NULL, hLineKey = NULL, hAllControlsKey = NULL, hControlKey = NULL; PMXLLINE pTLine, pLine; PMXLCONTROL pTControl; ULONG LineNum, ControlNum, i, Channels; WCHAR sz[ 16 ]; BOOL bPersistAll = FALSE; BOOL bValidMultichannel = FALSE; ULONG ResultLength; PAGED_CODE(); Status = kmxlRegOpenMixerKey( pfo, pmxd, &hMixerKey ); if( !NT_SUCCESS( Status ) ) { return( kmxlPersistAll( pfo, pmxd ) ); } // // If we've never written out valid multichannel mixer settings, go ahead and // do it here. // Status = kmxlRegQueryValue( hMixerKey, VALID_MULTICHANNEL_MIXER_VALUE_NAME, &bValidMultichannel, sizeof( bValidMultichannel ), &ResultLength ); if( !NT_SUCCESS( Status ) || !(bValidMultichannel) ) { return( kmxlPersistAll( pfo, pmxd ) ); } pLine = kmxlFindLineForControl( pControl, pmxd->listLines ); if( pLine == NULL ) { Status = STATUS_INVALID_PARAMETER; DPF(DL_WARNING|FA_PERSIST,("KmxlFindLineForControl failed Status=%X",Status) ); goto exit; } LineNum = 0; pTLine = kmxlFirstInList( pmxd->listLines ); while( pTLine ) { if( pTLine == pLine ) { swprintf( sz, LINE_KEY_NAME_FORMAT, LineNum ); Status = kmxlRegOpenKey( hMixerKey, sz, &hLineKey ); if( !NT_SUCCESS( Status ) ) { bPersistAll = TRUE; goto exit; } Status = kmxlRegOpenKey( hLineKey, CONTROLS_KEY_NAME, &hAllControlsKey ); if( !NT_SUCCESS( Status ) ) { bPersistAll = TRUE; goto exit; } ControlNum = 0; pTControl = kmxlFirstInList( pTLine->Controls ); while( pTControl ) { if( pTControl == pControl ) { swprintf( sz, CONTROL_KEY_NAME_FORMAT, ControlNum ); Status = kmxlRegOpenKey( hAllControlsKey, sz, &hControlKey ); if( !NT_SUCCESS( Status ) ) { bPersistAll = TRUE; goto exit; } kmxlRegSetValue( hControlKey, CONTROL_TYPE_VALUE_NAME, REG_DWORD, &pControl->Control.dwControlType, sizeof( pControl->Control.dwControlType ) ); Status = kmxlGetCurrentControlValue( pfo, pmxd, pLine, pControl, &paDetails ); if( NT_SUCCESS( Status ) ) { if( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) { for( i = 0; i < pControl->Control.cMultipleItems; i++ ) { swprintf( sz, MULTIPLEITEM_VALUE_NAME_FORMAT, i ); Status = kmxlRegSetValue( hControlKey, sz, REG_DWORD, &((PMIXERCONTROLDETAILS_UNSIGNED) paDetails)[ i ], sizeof( ((PMIXERCONTROLDETAILS_UNSIGNED) paDetails)[ i ] ) ); } } else { Channels = pControl->NumChannels; kmxlRegSetValue( hControlKey, CHANNEL_COUNT_VALUE_NAME, REG_DWORD, &Channels, sizeof( Channels ) ); for( i = 0; i < Channels; i++ ) { swprintf( sz, CHANNEL_VALUE_NAME_FORMAT, i ); Status = kmxlRegSetValue( hControlKey, sz, REG_DWORD, &((PMIXERCONTROLDETAILS_UNSIGNED) paDetails)[ i ], sizeof( ((PMIXERCONTROLDETAILS_UNSIGNED) paDetails)[ i ] ) ); } } AudioFreeMemory_Unknown( &paDetails ); } goto exit; } else { pTControl = kmxlNextControl( pTControl ); ++ControlNum; } } Status = STATUS_SUCCESS; goto exit; } else { pTLine = kmxlNextLine( pTLine ); ++LineNum; } } Status = STATUS_OBJECT_NAME_NOT_FOUND; DPF(DL_WARNING|FA_PERSIST,("kmxlPersistSingleControl failing Status=%X",Status) ); exit: if( hMixerKey ) { kmxlRegCloseKey( hMixerKey ); } if( hLineKey ) { kmxlRegCloseKey( hLineKey ); } if( hAllControlsKey ) { kmxlRegCloseKey( hAllControlsKey ); } if( hControlKey ) { kmxlRegCloseKey( hControlKey ); } if( bPersistAll ) { return( kmxlPersistAll( pfo, pmxd ) ); } RETURN( Status ); } /////////////////////////////////////////////////////////////////////// // // kmxlPersistControl // // NTSTATUS kmxlPersistControl( IN PFILE_OBJECT pfo, // The instance to retrieve IN PMIXERDEVICE pmxd, // Mixer device info IN PMXLCONTROL pControl, // The control to persist IN PVOID paDetails // The channel values to persist ) { PMXLLINE pLine; PMXLCONTROL pCtrl; NTSTATUS Status; NTSTATUS OverallStatus; PAGED_CODE(); OverallStatus=STATUS_SUCCESS; // // Persist the control that just changed. Do not abort if this persist fails. // Status = kmxlPersistSingleControl( pfo, pmxd, pControl, paDetails ); if( !NT_SUCCESS( Status ) ) { OverallStatus=Status; } // // Check all other controls and see if another control shares the same // node ID. If so, persist that control with the new value also. // Again, do not abort if any of the persists fail. Simply return the last // error status. // pLine = kmxlFirstInList( pmxd->listLines ); while( pLine ) { pCtrl = kmxlFirstInList( pLine->Controls ); while( pCtrl ) { if( pCtrl->Id==pControl->Id && pCtrl!=pControl ) { Status = kmxlPersistSingleControl( pfo, pmxd, pCtrl, paDetails ); if( !NT_SUCCESS( Status ) ) { OverallStatus=Status; } } pCtrl = kmxlNextControl( pCtrl ); } pLine = kmxlNextLine( pLine ); } RETURN( OverallStatus ); }