|
|
/****************************************************************************
* * sysaudio.c * * System Audio Device (SAD) interfaces * * Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved. * * History * 5-12-97 - Mike McLaughlin (MikeM) * 5-19-97 - Noel Cross (NoelC) * ***************************************************************************/
#include "wdmsys.h"
#include <wdmguid.h>
static const WCHAR MediaCategories[] = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"; static const WCHAR NodeNameValue[] = L"Name";
#pragma PAGEABLE_DATA
ULONG gMidiPreferredDeviceNumber = MAXULONG; ULONG gWavePreferredSysaudioDevice = MAXULONG;
#pragma PAGEABLE_CODE
#pragma PAGEABLE_DATA
int MyWcsicmp(const wchar_t *pwstr1, const wchar_t *pwstr2) { PAGED_CODE(); if (!pwstr1) { DPF( DL_TRACE|FA_SYSAUDIO,("pwstr1 == NULL")); return (-1); }
if (!pwstr2) { DPF( DL_TRACE|FA_SYSAUDIO, ("pwstr2 == NULL")); return (-1); }
return _wcsicmp(pwstr1, pwstr2); }
extern DWORD _cdecl _NtKernPhysicalDeviceObjectToDevNode( IN PDEVICE_OBJECT PhysicalDeviceObject );
#ifndef IO_NO_PARAMETER_CHECKING
#define IO_NO_PARAMETER_CHECKING 0x0100
NTKERNELAPI NTSTATUS IoCreateFile ( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG Disposition, IN ULONG CreateOptions, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength, IN CREATE_FILE_TYPE CreateFileType, IN PVOID ExtraCreateParameters OPTIONAL, IN ULONG Options ); #endif // !IO_NO_PARAMETER_CHECKING
NTSTATUS OpenSysAudioPin ( ULONG Device, ULONG PinId, KSPIN_DATAFLOW DataFlowRequested, PKSPIN_CONNECT pPinConnect, PFILE_OBJECT *ppFileObjectPin, PDEVICE_OBJECT *ppDeviceObjectPin, PCONTROLS_LIST pControlList ) { PFILE_OBJECT pFileObjectDevice = NULL; KSPIN_COMMUNICATION Communication; HANDLE hDevice = NULL; HANDLE hPin = NULL; NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE(); Status = OpenSysAudio(&hDevice, &pFileObjectDevice);
if(!NT_SUCCESS(Status)) { goto exit; }
//
// Set the default renderer
//
Status = SetSysAudioProperty(pFileObjectDevice, KSPROPERTY_SYSAUDIO_DEVICE_INSTANCE, sizeof(Device), &Device); if(!NT_SUCCESS(Status)) { goto exit; }
Status = GetPinProperty(pFileObjectDevice, KSPROPERTY_PIN_COMMUNICATION, PinId, sizeof(KSPIN_COMMUNICATION), &Communication); if(!NT_SUCCESS(Status)) { goto exit; }
if(Communication != KSPIN_COMMUNICATION_SINK && Communication != KSPIN_COMMUNICATION_BOTH) { Status = STATUS_INVALID_DEVICE_REQUEST; goto exit; }
pPinConnect->PinId = PinId; pPinConnect->PinToHandle = NULL;
if (DataFlowRequested == KSPIN_DATAFLOW_OUT) { Status = KsCreatePin(hDevice, pPinConnect, GENERIC_READ, &hPin); } else // KSPIN_DATAFLOW_OUT
{ Status = KsCreatePin(hDevice, pPinConnect, GENERIC_WRITE, &hPin); }
if(!NT_SUCCESS(Status)) { if(STATUS_NO_MATCH == Status) { Status = STATUS_INVALID_DEVICE_REQUEST; } hPin = NULL; goto exit; }
Status = ObReferenceObjectByHandle(hPin, GENERIC_READ | GENERIC_WRITE, NULL, KernelMode, ppFileObjectPin, NULL); if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("ObReferenceObjectByHandle failed Status=%X",Status) ); goto exit; }
GetControlNodes ( pFileObjectDevice, *ppFileObjectPin, PinId, pControlList ) ;
*ppDeviceObjectPin = IoGetRelatedDeviceObject(*ppFileObjectPin);
exit: if(hPin != NULL) { NtClose(hPin); } if(pFileObjectDevice != NULL) { ObDereferenceObject(pFileObjectDevice); } if(hDevice != NULL) { NtClose(hDevice); } RETURN(Status); }
VOID CloseSysAudio ( PWDMACONTEXT pWdmaContext, PFILE_OBJECT pFileObjectPin ) { ULONG d;
PAGED_CODE(); ObDereferenceObject(pFileObjectPin); UpdatePreferredDevice(pWdmaContext); }
NTSTATUS OpenSysAudio ( PHANDLE pHandle, PFILE_OBJECT *ppFileObject ) { NTSTATUS Status = STATUS_SUCCESS; PWSTR pwstrSymbolicLinkList = NULL; PWSTR pwstr;
PAGED_CODE(); ASSERT(*pHandle == NULL); ASSERT(*ppFileObject == NULL);
Status = IoGetDeviceInterfaces( &KSCATEGORY_SYSAUDIO, NULL, 0, &pwstrSymbolicLinkList);
if(!NT_SUCCESS(Status)) { DPF( DL_TRACE|FA_SYSAUDIO, ("IoGetDeviceInterfaces failed: Status=%08x", Status)); goto exit; }
// There is a double UNICODE_NULL at the end of the list
pwstr = pwstrSymbolicLinkList; while(*pwstr != UNICODE_NULL) {
Status = OpenDevice(pwstr, pHandle); if(NT_SUCCESS(Status)) { break; } ASSERT(*pHandle == NULL);
// Get next symbolic link
while(*pwstr++ != UNICODE_NULL); }
if(*pHandle == NULL) { Status = OpenDevice(L"\\DosDevices\\sysaudio", pHandle); if(!NT_SUCCESS(Status)) { DPF( DL_TRACE|FA_SYSAUDIO, ("OpenDevice failed: Status=%08x", Status)); goto exit; } }
//
// Security-Penetration issue:
//
// It has been brought up that using this handle is a security issue since in the time
// between creating the handle and now, the handle might be pointing to a different
// fileobject altogether. I am assured that as long as I don't touch any of the fields
// in the file object and only send 'safe' Ioctls, this will not be a problem.
//
Status = ObReferenceObjectByHandle( *pHandle, FILE_READ_DATA | FILE_WRITE_DATA, *IoFileObjectType, ExGetPreviousMode(), ppFileObject, NULL);
if(!NT_SUCCESS(Status)) { DPF( DL_TRACE|FA_SYSAUDIO, ("ObReferenceObjectByHandle failed: Status=%08x", Status)); goto exit; } exit: if(!NT_SUCCESS(Status)) { if(*ppFileObject != NULL) { ObDereferenceObject(*ppFileObject); *ppFileObject = NULL; } if(*pHandle != NULL) { NtClose(*pHandle); *pHandle = NULL; } } AudioFreeMemory_Unknown(&pwstrSymbolicLinkList); RETURN(Status); }
NTSTATUS OpenDevice ( PWSTR pwstrDevice, PHANDLE pHandle ) { IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING UnicodeDeviceString; OBJECT_ATTRIBUTES ObjectAttributes;
PAGED_CODE(); RtlInitUnicodeString(&UnicodeDeviceString, pwstrDevice);
InitializeObjectAttributes( &ObjectAttributes, &UnicodeDeviceString, 0, NULL, NULL);
return(IoCreateFile( pHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, CreateFileTypeNone, NULL, IO_FORCE_ACCESS_CHECK | IO_NO_PARAMETER_CHECKING)); }
NTSTATUS GetPinProperty ( PFILE_OBJECT pFileObject, ULONG PropertyId, ULONG PinId, ULONG cbProperty, PVOID pProperty ) { ULONG BytesReturned; KSP_PIN Property; NTSTATUS Status = STATUS_INVALID_PARAMETER;
PAGED_CODE(); if (pFileObject) { Property.Property.Set = KSPROPSETID_Pin; Property.Property.Id = PropertyId; Property.Property.Flags = KSPROPERTY_TYPE_GET; Property.PinId = PinId; Property.Reserved = 0;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Property.Id=%X, PinId=%X", PropertyId, PinId) );
Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_PROPERTY, &Property, sizeof(Property), pProperty, cbProperty, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, pProperty=%X,cbProperty=%X,BytesRet=%d", Status,pProperty,cbProperty,BytesReturned) );
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Property query failed Status=%X",Status) ); goto exit; } ASSERT(BytesReturned == cbProperty); } exit: RETURN(Status); }
NTSTATUS GetPinPropertyEx ( PFILE_OBJECT pFileObject, ULONG PropertyId, ULONG PinId, PVOID *ppProperty ) { ULONG BytesReturned; NTSTATUS Status = STATUS_INVALID_PARAMETER; KSP_PIN Pin;
PAGED_CODE(); if (pFileObject) { Pin.Property.Set = KSPROPSETID_Pin; Pin.Property.Id = PropertyId; Pin.Property.Flags = KSPROPERTY_TYPE_GET; Pin.PinId = PinId; Pin.Reserved = 0;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Property.Id=%X, PinId=%X", PropertyId, PinId) );
Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_PROPERTY, &Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,BytesRet=%d", Status,BytesReturned) );
ASSERT(!NT_SUCCESS(Status)); if(Status != STATUS_BUFFER_OVERFLOW) { //
// The driver should have returned the number of bytes that we needed
// to allocate and STATUS_BUFFER_OVERFLOW. But, if they returned a
// successful error code, we must not return success. Thus, we're making
// up a new return code - STATUS_INVALID_BUFFER_SIZE.
//
if( NT_SUCCESS(Status) ) Status = STATUS_INVALID_BUFFER_SIZE;
goto exit; }
if(BytesReturned == 0) { Status = STATUS_BUFFER_TOO_SMALL; goto exit; }
Status = AudioAllocateMemory_Paged(BytesReturned, TAG_AudQ_PROPERTY, ZERO_FILL_MEMORY, ppProperty ); if(!NT_SUCCESS(Status)) { goto exit; }
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Property.Id=%X, PinId=%X", PropertyId, PinId) );
Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_PROPERTY, &Pin, sizeof(KSP_PIN), *ppProperty, BytesReturned, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,BytesRet=%d", Status,BytesReturned) );
if(!NT_SUCCESS(Status)) { AudioFreeMemory_Unknown(ppProperty); goto exit; } }
exit: if( !NT_SUCCESS(Status) ) { *ppProperty = NULL; }
RETURN(Status); }
VOID GetControlNodes ( PFILE_OBJECT pDeviceFileObject, PFILE_OBJECT pPinFileObject, ULONG PinId, PCONTROLS_LIST pControlList ) { ULONG i ;
PAGED_CODE(); if ( pControlList == NULL ) { return ; }
for ( i = 0; i < pControlList->Count; i++ ) \ { pControlList->Controls[i].NodeId = ControlNodeFromGuid ( pDeviceFileObject, pPinFileObject, PinId, &pControlList->Controls[i].Control ) ; } }
ULONG ControlNodeFromGuid ( PFILE_OBJECT pDeviceFileObject, PFILE_OBJECT pPinFileObject, ULONG PinId, GUID* NodeType ) { ULONG NumNodes, NumConnections ; ULONG FirstConnectionIndex ; PKSMULTIPLE_ITEM pNodeItems, pConnectionItems ; GUID* pNodes ; PKSTOPOLOGY_CONNECTION pConnections, pConnection ; ULONG NodeId ;
PAGED_CODE(); // assume there are no nodes
NodeId = INVALID_NODE ; pNodeItems = NULL ; pConnectionItems = NULL ;
// Get the array of Node GUIDs
pNodeItems = GetTopologyProperty ( pDeviceFileObject, KSPROPERTY_TOPOLOGY_NODES ) ;
if ( pNodeItems == NULL ) { DPF(DL_WARNING|FA_SYSAUDIO,("GetTopologyProperty NODES failed") ); goto exit ; }
NumNodes = pNodeItems->Count ; pNodes = (GUID *)(pNodeItems+1) ;
// Get the array of Connections
pConnectionItems = GetTopologyProperty ( pDeviceFileObject, KSPROPERTY_TOPOLOGY_CONNECTIONS ) ;
if ( pConnectionItems == NULL ) { DPF(DL_WARNING|FA_SYSAUDIO,("GetTopologyProperty CONNECTIONS failed") ); goto exit ; } NumConnections = pConnectionItems->Count ; pConnections = (PKSTOPOLOGY_CONNECTION)(pConnectionItems+1) ;
// First get the start connection for the given PinId
FirstConnectionIndex = GetFirstConnectionIndex ( pPinFileObject ) ;
if ( FirstConnectionIndex == 0xffffffff ) { DPF(DL_WARNING|FA_SYSAUDIO,("GetFirstConnectionIndex failed") ); goto exit ; } pConnection = pConnections + FirstConnectionIndex ;
ASSERT ( pConnection ) ;
// NOTE : Assumes DataFlowOut. Need to modify if we want to support
// Volume for wavein pins.
while ((pConnection) && (pConnection->ToNode != KSFILTER_NODE) ) { if ( pConnection->ToNode >= NumNodes ) { ASSERT ( 0 ) ; } else { if (IsEqualGUID(&pNodes[pConnection->ToNode], NodeType)) { NodeId = pConnection->ToNode ; break ; } } pConnection = FindConnection ( pConnections, NumConnections, pConnection->ToNode, 0, WILD_CARD, WILD_CARD ) ; }
exit: AudioFreeMemory_Unknown( &pConnectionItems ) ; AudioFreeMemory_Unknown( &pNodeItems ) ; return ( NodeId ) ; }
PVOID GetTopologyProperty ( PFILE_OBJECT pDeviceFileObject, ULONG PropertyId ) { KSPROPERTY Property ; PVOID pBuf = NULL; ULONG BytesReturned ; NTSTATUS Status = STATUS_INVALID_PARAMETER;
PAGED_CODE(); pBuf = NULL ;
if (pDeviceFileObject) { Property.Set = KSPROPSETID_Topology ; Property.Id = PropertyId ; Property.Flags = KSPROPERTY_TYPE_GET;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Property.Id=%X", PropertyId) );
Status = KsSynchronousIoControlDevice( pDeviceFileObject, KernelMode, IOCTL_KS_PROPERTY, &Property, sizeof(Property), NULL, 0, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, BytesRet=%d", Status,BytesReturned) );
ASSERT(!NT_SUCCESS(Status)); if(Status != STATUS_BUFFER_OVERFLOW) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Property query Status=%X",Status) ); goto exit; }
Status = AudioAllocateMemory_Paged(BytesReturned, TAG_Audq_PROPERTY, ZERO_FILL_MEMORY|LIMIT_MEMORY, &pBuf ); if(!NT_SUCCESS(Status)) { goto exit; }
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Property.Id=%X", PropertyId) );
Status = KsSynchronousIoControlDevice( pDeviceFileObject, KernelMode, IOCTL_KS_PROPERTY, &Property, sizeof(Property), pBuf, BytesReturned, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,pBuf=%X,BytesRet=%d", Status,pBuf,BytesReturned) );
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Property query Status=%X",Status) ); AudioFreeMemory_Unknown ( &pBuf ) ; goto exit; } } exit: return pBuf ; }
PKSTOPOLOGY_CONNECTION FindConnection ( PKSTOPOLOGY_CONNECTION pConnections, ULONG NumConnections, ULONG FromNode, ULONG FromPin, ULONG ToNode, ULONG ToPin ) { PKSTOPOLOGY_CONNECTION pConnection ; ULONG i ;
PAGED_CODE(); pConnection = pConnections ;
for ( i = 0; i < NumConnections; i++ ) { if ( ((FromNode == WILD_CARD) || (FromNode == pConnection->FromNode)) && ((FromPin == WILD_CARD) || (FromPin == pConnection->FromNodePin)) && ((ToNode == WILD_CARD) || (ToNode == pConnection->ToNode)) && ((ToPin == WILD_CARD) || (ToPin == pConnection->ToNodePin)) ) return pConnection ; pConnection++ ; } return ( NULL ) ; }
ULONG GetFirstConnectionIndex ( PFILE_OBJECT pPinFileObject ) { KSPROPERTY Property ; ULONG Index = 0xffffffff; ULONG BytesReturned ; NTSTATUS Status = STATUS_INVALID_PARAMETER ;
PAGED_CODE(); if (pPinFileObject) { Property.Set = KSPROPSETID_Sysaudio_Pin ; Property.Id = KSPROPERTY_SYSAUDIO_TOPOLOGY_CONNECTION_INDEX ; Property.Flags = KSPROPERTY_TYPE_GET;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Property.Id=%X", Property.Id) );
Status = KsSynchronousIoControlDevice( pPinFileObject, KernelMode, IOCTL_KS_PROPERTY, &Property, sizeof(Property), &Index, sizeof ( Index ), &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, Index=%X,BytesRet=%d", Status,Index,BytesReturned) ); }
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Property query Status=%X",Status) ); return ( 0xffffffff ) ; } return ( Index ) ; }
VOID UpdatePreferredDevice ( PWDMACONTEXT pWdmaContext ) { ULONG d; PAGED_CODE(); //
// This causes the preferred sysaudio device to be queried if there
// are no open midi out streams.
//
for(d = 0; d < MAXNUMDEVS; d++) { if(pWdmaContext->MidiOutDevs[d].Device != UNUSED_DEVICE && pWdmaContext->MidiOutDevs[d].pMidiPin && pWdmaContext->MidiOutDevs[d].pMidiPin->fGraphRunning) { return; } } pWdmaContext->PreferredSysaudioWaveDevice = gWavePreferredSysaudioDevice; }
NTSTATUS SetPreferredDevice ( PWDMACONTEXT pContext, LPDEVICEINFO pDeviceInfo ) { SYSAUDIO_PREFERRED_DEVICE Preferred; ULONG TranslatedDeviceNumber; ULONG SysaudioDevice; ULONG BytesReturned; NTSTATUS Status;
PAGED_CODE(); if(pContext->pFileObjectSysaudio == NULL) { Status = STATUS_SUCCESS; goto exit; }
TranslatedDeviceNumber = wdmaudTranslateDeviceNumber( pContext, pDeviceInfo->DeviceType, pDeviceInfo->wstrDeviceInterface, pDeviceInfo->DeviceNumber);
if(MAXULONG == TranslatedDeviceNumber) { DPF(DL_WARNING|FA_SYSAUDIO,("Invalid Device Number") ); Status = STATUS_INVALID_PARAMETER; goto exit; } SysaudioDevice = pContext->apCommonDevice [pDeviceInfo->DeviceType][TranslatedDeviceNumber]->Device;
switch(pDeviceInfo->DeviceType) { case WaveOutDevice: Preferred.Index = KSPROPERTY_SYSAUDIO_PLAYBACK_DEFAULT; gWavePreferredSysaudioDevice = SysaudioDevice; UpdatePreferredDevice(pContext); break;
case WaveInDevice: Preferred.Index = KSPROPERTY_SYSAUDIO_RECORD_DEFAULT; break;
case MidiOutDevice: Preferred.Index = KSPROPERTY_SYSAUDIO_MIDI_DEFAULT; gMidiPreferredDeviceNumber = TranslatedDeviceNumber; break;
default: Status = STATUS_SUCCESS; goto exit; } Preferred.Property.Set = KSPROPSETID_Sysaudio; Preferred.Property.Id = KSPROPERTY_SYSAUDIO_PREFERRED_DEVICE; Preferred.Property.Flags = KSPROPERTY_TYPE_SET;
if(pDeviceInfo->dwFlags == 0) { Preferred.Flags = 0; } else { Preferred.Flags = SYSAUDIO_FLAGS_CLEAR_PREFERRED; if(pDeviceInfo->DeviceType == WaveOutDevice) { gWavePreferredSysaudioDevice = MAXULONG; } else if(pDeviceInfo->DeviceType == MidiOutDevice) { gMidiPreferredDeviceNumber = MAXULONG; } }
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Property.Id=%X, Preferred=%X", Preferred,Preferred.Property.Id) );
Status = KsSynchronousIoControlDevice( pContext->pFileObjectSysaudio, KernelMode, IOCTL_KS_PROPERTY, &Preferred, sizeof(Preferred), &SysaudioDevice, sizeof(SysaudioDevice), &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, SysaudioDevice=%X,BytesRet=%d", Status,SysaudioDevice,BytesReturned) );
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Property Query failed Status=%X",Status) ); goto exit; } if(pDeviceInfo->DeviceType == WaveOutDevice && gMidiPreferredDeviceNumber != MAXULONG) { ULONG d;
d = pContext->apCommonDevice[MidiOutDevice] [gMidiPreferredDeviceNumber]->PreferredDevice;
if(d != MAXULONG && (d == gMidiPreferredDeviceNumber || pContext->apCommonDevice[MidiOutDevice][d]->PreferredDevice == d)) {
Preferred.Index = KSPROPERTY_SYSAUDIO_MIDI_DEFAULT;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Property.Id=%X, Preferred=%X", Preferred,Preferred.Property.Id) );
Status = KsSynchronousIoControlDevice( pContext->pFileObjectSysaudio, KernelMode, IOCTL_KS_PROPERTY, &Preferred, sizeof(Preferred), &SysaudioDevice, sizeof(SysaudioDevice), &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, SysaudioDevice=%X,BytesRet=%d", Status,SysaudioDevice,BytesReturned) );
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Property Query failed Status=%X",Status) ); goto exit; } } } exit: RETURN(Status); }
NTSTATUS GetSysAudioProperty ( PFILE_OBJECT pFileObject, ULONG PropertyId, ULONG DeviceIndex, ULONG cbProperty, PVOID pProperty ) { ULONG BytesReturned; KSPROPERTYPLUS Property; NTSTATUS Status = STATUS_INVALID_PARAMETER;
PAGED_CODE(); if (pFileObject) { Property.Property.Set = KSPROPSETID_Sysaudio; Property.Property.Id = PropertyId; Property.Property.Flags = KSPROPERTY_TYPE_GET; Property.DeviceIndex = DeviceIndex;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Id=%X, DI=%X",PropertyId,DeviceIndex) );
Status = KsSynchronousIoControlDevice(pFileObject, KernelMode, IOCTL_KS_PROPERTY, &Property, sizeof(Property), pProperty, cbProperty, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, pProperty=%X,cbProperty=%X,BytesRet=%d", Status,pProperty,cbProperty,BytesReturned) ); }
RETURN( Status ); }
NTSTATUS SetSysAudioProperty ( PFILE_OBJECT pFileObject, ULONG PropertyId, ULONG cbProperty, PVOID pProperty ) { ULONG BytesReturned; KSPROPERTY Property; NTSTATUS Status = STATUS_INVALID_PARAMETER;
PAGED_CODE(); if (pFileObject) { Property.Set = KSPROPSETID_Sysaudio; Property.Id = PropertyId; Property.Flags = KSPROPERTY_TYPE_SET;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Id=%X", PropertyId) );
Status = KsSynchronousIoControlDevice(pFileObject, KernelMode, IOCTL_KS_PROPERTY, &Property, sizeof(Property), pProperty, cbProperty, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, pProperty=%X,cbProperty=%X,BytesRet=%d", Status,pProperty,cbProperty,BytesReturned) ); }
RETURN(Status); }
DWORD wdmaudTranslateDeviceNumber ( PWDMACONTEXT pWdmaContext, DWORD DeviceType, PCWSTR DeviceInterface, DWORD DeviceNumber ) { PCOMMONDEVICE *ppCommonDevice; DWORD d, j;
PAGED_CODE(); ppCommonDevice = &pWdmaContext->apCommonDevice[DeviceType][0];
for (d = 0; d < MAXNUMDEVS; d++ ) { if (ppCommonDevice[d]->Device == UNUSED_DEVICE || MyWcsicmp(ppCommonDevice[d]->DeviceInterface, DeviceInterface)) { continue; } if(ppCommonDevice[d]->PreferredDevice == MAXULONG || ppCommonDevice[d]->PreferredDevice == d) { if (DeviceNumber == 0) { if (ppCommonDevice[d]->PreferredDevice == d) { if(pWdmaContext->PreferredSysaudioWaveDevice != MAXULONG) { for (j = 0; j < MAXNUMDEVS; j++) { if (j == d) continue;
if (ppCommonDevice[j]->PreferredDevice == d && ppCommonDevice[j]->Device == pWdmaContext->PreferredSysaudioWaveDevice) { return j; } } } } return d; } else { DeviceNumber--; } } }
return MAXULONG; }
int CmpStr( PWSTR pwstr1, PWSTR pwstr2 ) { PAGED_CODE(); if(pwstr1 == NULL && pwstr2 == NULL) { return(0); } if(pwstr1 == NULL || pwstr2 == NULL) { return(1); } return(wcscmp(pwstr1, pwstr2)); }
NTSTATUS AddDevice ( PWDMACONTEXT pWdmaContext, ULONG Device, DWORD DeviceType, PCWSTR DeviceInterface, ULONG PinId, PWSTR pwstrName, BOOL fUsePreferred, PDATARANGES pDataRanges, PKSCOMPONENTID ComponentId ) { PCOMMONDEVICE *papCommonDevice; DWORD DeviceNumber; DWORD d; PKSDATARANGE_MUSIC MusicDataRange;
PAGED_CODE(); switch(DeviceType) { case MidiOutDevice: MusicDataRange = (PKSDATARANGE_MUSIC)&pDataRanges->aDataRanges[0]; if ( !IsEqualGUID( &KSMUSIC_TECHNOLOGY_SWSYNTH, &MusicDataRange->Technology ) ) { fUsePreferred = FALSE; } break;
case MidiInDevice: fUsePreferred = FALSE; break;
default: // Do nothing
break; }
DPF( DL_TRACE|FA_SYSAUDIO, ("D# %02x DT %02x DI %ls PI %02x %01x", Device, DeviceType, DeviceInterface, PinId, fUsePreferred));
papCommonDevice = &pWdmaContext->apCommonDevice[DeviceType][0];
for (DeviceNumber = 0; DeviceNumber < MAXNUMDEVS; DeviceNumber++) { if (papCommonDevice[DeviceNumber]->Device != UNUSED_DEVICE && !MyWcsicmp(papCommonDevice[DeviceNumber]->DeviceInterface, DeviceInterface) && !CmpStr(papCommonDevice[DeviceNumber]->pwstrName, pwstrName)) { papCommonDevice[DeviceNumber]->Device = Device; papCommonDevice[DeviceNumber]->PinId = PinId; ASSERT( (!fUsePreferred && papCommonDevice[DeviceNumber]->PreferredDevice == MAXULONG) || (fUsePreferred && papCommonDevice[DeviceNumber]->PreferredDevice != MAXULONG)); break; } }
if (DeviceNumber < MAXNUMDEVS) { // We found an existing device that matches this one. We need to free
// some stuff before setting up the new stuff
AudioFreeMemory_Unknown(&papCommonDevice[DeviceNumber]->pwstrName); AudioFreeMemory_Unknown(&papCommonDevice[DeviceNumber]->DeviceInterface); AudioFreeMemory_Unknown(&papCommonDevice[DeviceNumber]->ComponentId); switch (DeviceType) { case WaveOutDevice: AudioFreeMemory_Unknown(&pWdmaContext->WaveOutDevs[DeviceNumber].AudioDataRanges); break; case WaveInDevice: AudioFreeMemory_Unknown(&pWdmaContext->WaveInDevs[DeviceNumber].AudioDataRanges); break; case MidiOutDevice: AudioFreeMemory_Unknown(&pWdmaContext->MidiOutDevs[DeviceNumber].MusicDataRanges); break; case MidiInDevice: AudioFreeMemory_Unknown(&pWdmaContext->MidiInDevs[DeviceNumber].MusicDataRanges); break; } } else { // We didn't find an existing device that matches the new one. Search
// for an unused slot in our device lists
for (DeviceNumber = 0; DeviceNumber < MAXNUMDEVS; DeviceNumber++) { if (papCommonDevice[DeviceNumber]->Device == UNUSED_DEVICE) break; } }
if (DeviceNumber == MAXNUMDEVS) RETURN( STATUS_INSUFFICIENT_RESOURCES );
if (!NT_SUCCESS(AudioAllocateMemory_Paged((wcslen(DeviceInterface)+1)*sizeof(WCHAR), TAG_AudD_DEVICEINFO, DEFAULT_MEMORY, &papCommonDevice[DeviceNumber]->DeviceInterface))) { RETURN( STATUS_INSUFFICIENT_RESOURCES ); } wcscpy(papCommonDevice[DeviceNumber]->DeviceInterface, DeviceInterface);
papCommonDevice[DeviceNumber]->Device = Device; papCommonDevice[DeviceNumber]->PinId = PinId; papCommonDevice[DeviceNumber]->pwstrName = pwstrName; papCommonDevice[DeviceNumber]->PreferredDevice = MAXULONG; papCommonDevice[DeviceNumber]->ComponentId = ComponentId;
switch(DeviceType) { case WaveOutDevice: pWdmaContext->WaveOutDevs[DeviceNumber].AudioDataRanges = pDataRanges; break;
case WaveInDevice: pWdmaContext->WaveInDevs[DeviceNumber].AudioDataRanges= pDataRanges; break;
case MidiOutDevice: pWdmaContext->MidiOutDevs[DeviceNumber].MusicDataRanges = pDataRanges; break;
case MidiInDevice: pWdmaContext->MidiInDevs[DeviceNumber].MusicDataRanges = pDataRanges; break;
case AuxDevice: break;
default: ASSERT(FALSE); RETURN(STATUS_INVALID_PARAMETER); }
if (fUsePreferred) { papCommonDevice[DeviceNumber]->PreferredDevice = DeviceNumber;
for (d = 0; d < MAXNUMDEVS; d++) { if (d == DeviceNumber) continue;
if (papCommonDevice[d]->Device == UNUSED_DEVICE) continue;
if (papCommonDevice[d]->PreferredDevice != d) continue;
if(CmpStr(papCommonDevice[d]->pwstrName, pwstrName) != 0) continue;
papCommonDevice[DeviceNumber]->PreferredDevice = d; ASSERT(papCommonDevice[d]->PreferredDevice == d); break; } }
return STATUS_SUCCESS; }
WORD GetMidiTechnology ( PKSDATARANGE_MUSIC MusicDataRange ) { WORD Technology = MOD_MIDIPORT; // default to MIDIPORT
PAGED_CODE(); if ( IsEqualGUID( &KSMUSIC_TECHNOLOGY_FMSYNTH, &MusicDataRange->Technology ) ) { Technology = MOD_FMSYNTH; } else if ( IsEqualGUID( &KSMUSIC_TECHNOLOGY_WAVETABLE, &MusicDataRange->Technology ) ) { Technology = MOD_WAVETABLE; } else if ( IsEqualGUID( &KSMUSIC_TECHNOLOGY_SWSYNTH, &MusicDataRange->Technology ) ) { Technology = MOD_SWSYNTH; } else if ( IsEqualGUID( &KSMUSIC_TECHNOLOGY_SQSYNTH, &MusicDataRange->Technology ) ) { Technology = MOD_SQSYNTH; } else if ( IsEqualGUID( &KSMUSIC_TECHNOLOGY_PORT, &MusicDataRange->Technology ) ) { Technology = MOD_MIDIPORT; }
return Technology; }
DWORD GetFormats ( PKSDATARANGE_AUDIO AudioDataRange ) { DWORD dwSamples = 0; DWORD dwChannels = 0; DWORD dwBits = 0;
PAGED_CODE(); // The WAVE_FORMAT_XXXX flags are bit flags
//
// So we take advantage of that by determining three
// sets of information:
// - frequencies that are in the valid range
// - valid bits per sample
// - number of channels
//
// We than bitwise-AND the three values to get
// the intersection of valid formats
//
// Is 11.025 KHz valid?
if (AudioDataRange->MinimumSampleFrequency <= 11025 && AudioDataRange->MaximumSampleFrequency >= 11025) { dwSamples |= WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16; }
// Is 22.05 KHz valid?
if (AudioDataRange->MinimumSampleFrequency <= 22050 && AudioDataRange->MaximumSampleFrequency >= 22050) { dwSamples |= WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16; }
// Is 44.1KHz valid?
if (AudioDataRange->MinimumSampleFrequency <= 44100 && AudioDataRange->MaximumSampleFrequency >= 44100) { dwSamples |= WAVE_FORMAT_44M08 | WAVE_FORMAT_44S08 | WAVE_FORMAT_44M16 | WAVE_FORMAT_44S16; }
// Is 48 KHz valid?
if (AudioDataRange->MinimumSampleFrequency <= 48000 && AudioDataRange->MaximumSampleFrequency >= 48000) { dwSamples |= WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16; }
// Is 96 KHz valid?
if (AudioDataRange->MinimumSampleFrequency <= 96000 && AudioDataRange->MaximumSampleFrequency >= 96000) { dwSamples |= WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16; }
// Is 8 bit per sample valid?
if (AudioDataRange->MinimumBitsPerSample <= 8 && AudioDataRange->MaximumBitsPerSample >= 8) { dwBits |= WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_44M08 | WAVE_FORMAT_44S08 | WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08; }
// Is 16 bits per sample valid?
if (AudioDataRange->MinimumBitsPerSample <= 16 && AudioDataRange->MaximumBitsPerSample >= 16) { dwBits |= WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_44M16 | WAVE_FORMAT_44S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16; }
// Is one channel (aka mono sound) valid?
if (AudioDataRange->MaximumChannels >= 1) { dwChannels |= WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_44M08 | WAVE_FORMAT_44M16 | WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_96M08 | WAVE_FORMAT_48M16; }
// Are two channels (aka stereo sound) valid?
if (AudioDataRange->MaximumChannels >= 2) { dwChannels |= WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16 | WAVE_FORMAT_44S08 | WAVE_FORMAT_44S16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16; }
dwSamples = dwSamples & dwBits & dwChannels;
return dwSamples; }
//
// Assist with unicode conversions
//
VOID CopyUnicodeStringtoAnsiString ( LPSTR lpstr, LPCWSTR lpwstr, int len ) { UNICODE_STRING SourceString; ANSI_STRING DestinationString; NTSTATUS Status;
PAGED_CODE(); RtlInitUnicodeString(&SourceString, lpwstr);
Status = RtlUnicodeStringToAnsiString(&DestinationString, &SourceString, TRUE);
if (NT_SUCCESS(Status)) {
if (DestinationString.MaximumLength<len) { len=DestinationString.MaximumLength; }
RtlCopyMemory(lpstr, DestinationString.Buffer, len);
RtlFreeAnsiString(&DestinationString);
lpstr[len-1]=0;
} else if (len>0) {
*lpstr=0;
}
}
VOID CopyAnsiStringtoUnicodeString ( LPWSTR lpwstr, LPCSTR lpstr, int len ) {
PAGED_CODE(); while (len) { *lpwstr = (WCHAR) *lpstr; lpwstr++; lpstr++; len--; }
lpwstr--; *lpwstr=0;
}
UINT GetCapsIndex ( PWDMACONTEXT pWdmaContext, PWSTR pwstrName, DWORD DeviceType, DWORD DeviceNumber ) { PCOMMONDEVICE *ppCommonDevice; UINT MatchCount = 0; DWORD d;
PAGED_CODE(); ppCommonDevice = &pWdmaContext->apCommonDevice[DeviceType][0];
//
// Loop through all of the devices for a particular devicetype
//
for( d = 0; d < MAXNUMDEVS; d++ ) { if (ppCommonDevice[d]->Device != UNUSED_DEVICE && !CmpStr(ppCommonDevice[d]->pwstrName, pwstrName)) { MatchCount++; if (DeviceNumber == d) { break; } } }
//
// returns index of the friendly name.
//
return MatchCount; }
NTSTATUS ReadProductNameFromMediaCategories( IN REFGUID ProductNameGuid, OUT PWSTR *NameBuffer ) /*++
Routine Description:
Queries the "Name" key from the specified category GUID.
Arguments:
ProductNameGuid - The GUID to locate the name value for.
NameBuffer - The place in which to put the value. --*/ { OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; HANDLE CategoryKey; KEY_VALUE_PARTIAL_INFORMATION PartialInfoHeader; WCHAR RegistryPath[sizeof(MediaCategories) + 39]; UNICODE_STRING RegistryString; UNICODE_STRING ValueName; ULONG BytesReturned;
PAGED_CODE(); //
// Build the registry key path to the specified category GUID.
//
Status = RtlStringFromGUID(ProductNameGuid, &RegistryString); if (!NT_SUCCESS(Status)) { RETURN( Status ); } wcscpy(RegistryPath, MediaCategories); wcscat(RegistryPath, RegistryString.Buffer); RtlFreeUnicodeString(&RegistryString); RtlInitUnicodeString(&RegistryString, RegistryPath); InitializeObjectAttributes(&ObjectAttributes, &RegistryString, OBJ_CASE_INSENSITIVE, NULL, NULL); if (!NT_SUCCESS(Status = ZwOpenKey(&CategoryKey, KEY_READ, &ObjectAttributes))) { RETURN( Status ); } //
// Read the "Name" value beneath this category key.
//
RtlInitUnicodeString(&ValueName, NodeNameValue); Status = ZwQueryValueKey( CategoryKey, &ValueName, KeyValuePartialInformation, &PartialInfoHeader, sizeof(PartialInfoHeader), &BytesReturned); //
// Even if the read did not cause an overflow, just take the same
// code path, as such a thing would not normally happen.
//
if ((Status == STATUS_BUFFER_OVERFLOW) || NT_SUCCESS(Status)) { PKEY_VALUE_PARTIAL_INFORMATION PartialInfoBuffer = NULL;
//
// Allocate a buffer for the actual size of data needed.
//
Status = AudioAllocateMemory_Paged(BytesReturned, TAG_Audp_NAME, ZERO_FILL_MEMORY, &PartialInfoBuffer ); if (NT_SUCCESS(Status)) { //
// Retrieve the actual name.
//
Status = ZwQueryValueKey( CategoryKey, &ValueName, KeyValuePartialInformation, PartialInfoBuffer, BytesReturned, &BytesReturned); if (NT_SUCCESS(Status)) { //
// Make sure that there is always a value.
//
if (!PartialInfoBuffer->DataLength || (PartialInfoBuffer->Type != REG_SZ)) { Status = STATUS_UNSUCCESSFUL; } else { Status = AudioAllocateMemory_Paged(PartialInfoBuffer->DataLength, TAG_Audp_NAME, DEFAULT_MEMORY, NameBuffer ); if (NT_SUCCESS(Status)) {
RtlCopyMemory( *NameBuffer, PartialInfoBuffer->Data, PartialInfoBuffer->DataLength); } } } AudioFreeMemory_Unknown(&PartialInfoBuffer); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } }
ZwClose(CategoryKey); RETURN( Status ); }
WORD ChooseCorrectMid ( REFGUID Manufacturer ) { PAGED_CODE(); if (IS_COMPATIBLE_MMREG_MID(Manufacturer)) { return EXTRACT_MMREG_MID(Manufacturer); } else { return MM_UNMAPPED; } }
WORD ChooseCorrectPid ( REFGUID Product ) { PAGED_CODE(); if (IS_COMPATIBLE_MMREG_PID(Product)) { return EXTRACT_MMREG_PID(Product); } else { return MM_PID_UNMAPPED; } }
NTSTATUS FillWaveOutDevCaps ( PWDMACONTEXT pWdmaContext, DWORD DeviceNumber, LPBYTE lpCaps, DWORD dwSize ) { WAVEOUTCAPS2W wc2; WAVEDEVICE WaveDevice; PDATARANGES AudioDataRanges; PKSDATARANGE_AUDIO pDataRange; ULONG d; UINT CapsIndex; WCHAR szTemp[256];
PAGED_CODE(); WaveDevice = pWdmaContext->WaveOutDevs[DeviceNumber];
//
// If available, use the ComponentId to gather information about the device.
// Otherwise, fall back to hardcoded devcaps.
//
if ( (WaveDevice.PreferredDevice == MAXULONG) && (WaveDevice.ComponentId) ) { wc2.NameGuid = WaveDevice.ComponentId->Name;
wc2.wMid = ChooseCorrectMid(&WaveDevice.ComponentId->Manufacturer); wc2.ManufacturerGuid = WaveDevice.ComponentId->Manufacturer;
wc2.wPid = ChooseCorrectPid(&WaveDevice.ComponentId->Product); wc2.ProductGuid = WaveDevice.ComponentId->Product;
wc2.vDriverVersion = (WaveDevice.ComponentId->Version << 8) | (WaveDevice.ComponentId->Revision & 0xFF); } else { wc2.NameGuid = GUID_NULL;
wc2.wMid = MM_MICROSOFT; INIT_MMREG_MID( &wc2.ManufacturerGuid, wc2.wMid );
wc2.wPid = MM_MSFT_WDMAUDIO_WAVEOUT; INIT_MMREG_PID( &wc2.ProductGuid, wc2.wPid );
wc2.vDriverVersion = 0x050a; }
//
// Assume that KMixer is sample accurate
//
wc2.dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME | WAVECAPS_SAMPLEACCURATE ;
//
// Compute the wChannels and dwFormats by consolidating the caps
// from each of the dataranges
//
wc2.wChannels = 0; wc2.dwFormats = 0;
AudioDataRanges = WaveDevice.AudioDataRanges; pDataRange = (PKSDATARANGE_AUDIO)&AudioDataRanges->aDataRanges[0];
for(d = 0; d < AudioDataRanges->Count; d++) { if (pDataRange->DataRange.FormatSize >= sizeof(KSDATARANGE_AUDIO)) { //
// Only produce caps for PCM formats
//
if ( EXTRACT_WAVEFORMATEX_ID(&pDataRange->DataRange.SubFormat) == WAVE_FORMAT_PCM ) { // Get the largest number of supported channels
if ( (WORD)pDataRange->MaximumChannels > wc2.wChannels) wc2.wChannels = (WORD)pDataRange->MaximumChannels;
wc2.dwFormats |= GetFormats( pDataRange ); } }
// Get the pointer to the next data range
(PUCHAR)pDataRange += ((pDataRange->DataRange.FormatSize + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT); }
//
// Add an index in the form of "(%d)" to the end of the szPname string if two or more
// devices have the same name
//
ASSERT(WaveDevice.pwstrName); CapsIndex = GetCapsIndex( pWdmaContext, WaveDevice.pwstrName, WaveOutDevice, DeviceNumber ); if (CapsIndex < 2) { wcsncpy(wc2.szPname, WaveDevice.pwstrName, MAXPNAMELEN); } else { swprintf(szTemp, STR_PNAME, WaveDevice.pwstrName, CapsIndex); wcsncpy(wc2.szPname, szTemp, MAXPNAMELEN); } wc2.szPname[MAXPNAMELEN-1] = UNICODE_NULL;
//
// Copy the caps information into the caller supplied buffer
//
RtlCopyMemory(lpCaps, &wc2, min(dwSize, sizeof(wc2)));
return STATUS_SUCCESS; }
NTSTATUS FillWaveInDevCaps ( PWDMACONTEXT pWdmaContext, DWORD DeviceNumber, LPBYTE lpCaps, DWORD dwSize ) { WAVEINCAPS2W wc2; WAVEDEVICE WaveDevice; PDATARANGES AudioDataRanges; PKSDATARANGE_AUDIO pDataRange; ULONG d; UINT CapsIndex; WCHAR szTemp[256];
PAGED_CODE(); WaveDevice = pWdmaContext->WaveInDevs[DeviceNumber];
//
// If available, use the ComponentId to gather information about the device.
// Otherwise, fall back to hardcoded devcaps.
//
if ( (WaveDevice.PreferredDevice == MAXULONG) && (WaveDevice.ComponentId) ) { wc2.NameGuid = WaveDevice.ComponentId->Name;
wc2.wMid = ChooseCorrectMid(&WaveDevice.ComponentId->Manufacturer); wc2.ManufacturerGuid = WaveDevice.ComponentId->Manufacturer;
wc2.wPid = ChooseCorrectPid(&WaveDevice.ComponentId->Product); wc2.ProductGuid = WaveDevice.ComponentId->Product;
wc2.vDriverVersion = (WaveDevice.ComponentId->Version << 8) | (WaveDevice.ComponentId->Revision & 0xFF); } else { wc2.NameGuid = GUID_NULL;
wc2.wMid = MM_MICROSOFT; INIT_MMREG_MID( &wc2.ManufacturerGuid, wc2.wMid );
wc2.wPid = MM_MSFT_WDMAUDIO_WAVEIN; INIT_MMREG_PID( &wc2.ProductGuid, wc2.wPid );
wc2.vDriverVersion = 0x050a; }
//
// Compute the wChannels and dwFormats by consolidating the caps
// from each of the dataranges
//
wc2.wChannels = 0; wc2.dwFormats = 0;
AudioDataRanges = WaveDevice.AudioDataRanges; pDataRange = (PKSDATARANGE_AUDIO)&AudioDataRanges->aDataRanges[0];
for(d = 0; d < AudioDataRanges->Count; d++) { if (pDataRange->DataRange.FormatSize >= sizeof(KSDATARANGE_AUDIO)) { //
// Only produce caps for PCM formats
//
if ( EXTRACT_WAVEFORMATEX_ID(&pDataRange->DataRange.SubFormat) == WAVE_FORMAT_PCM ) { // Get the largest number of supported channels
if ( (WORD)pDataRange->MaximumChannels > wc2.wChannels) wc2.wChannels = (WORD)pDataRange->MaximumChannels;
wc2.dwFormats |= GetFormats( pDataRange ); } }
// Get the pointer to the next data range
(PUCHAR)pDataRange += ((pDataRange->DataRange.FormatSize + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT); }
//
// Add an index in the form of "(%d)" to the end of the szPname string if two or more
// devices have the same name
//
ASSERT(WaveDevice.pwstrName); CapsIndex = GetCapsIndex( pWdmaContext, WaveDevice.pwstrName, WaveInDevice, DeviceNumber ); if (CapsIndex < 2) { wcsncpy(wc2.szPname, WaveDevice.pwstrName, MAXPNAMELEN); } else { swprintf(szTemp, STR_PNAME, WaveDevice.pwstrName, CapsIndex); wcsncpy(wc2.szPname, szTemp, MAXPNAMELEN); } wc2.szPname[MAXPNAMELEN-1] = UNICODE_NULL;
//
// Copy the caps information into the caller supplied buffer
//
RtlCopyMemory(lpCaps, &wc2, min(dwSize, sizeof(wc2)));
return STATUS_SUCCESS; }
NTSTATUS FillMidiOutDevCaps ( PWDMACONTEXT pWdmaContext, DWORD DeviceNumber, LPBYTE lpCaps, DWORD dwSize ) { MIDIOUTCAPS2W mc2; MIDIDEVICE MidiDevice; PDATARANGES MusicDataRanges; PKSDATARANGE_MUSIC pDataRange; UINT CapsIndex; WCHAR szTemp[256];
PAGED_CODE(); MidiDevice = pWdmaContext->MidiOutDevs[DeviceNumber];
//
// If available, use the ComponentId to gather information about the device.
// Otherwise, fall back to hardcoded devcaps.
//
if ( (MidiDevice.PreferredDevice == MAXULONG) && (MidiDevice.ComponentId) ) { mc2.NameGuid = MidiDevice.ComponentId->Name;
mc2.wMid = ChooseCorrectMid(&MidiDevice.ComponentId->Manufacturer); mc2.ManufacturerGuid = MidiDevice.ComponentId->Manufacturer;
mc2.wPid = ChooseCorrectPid(&MidiDevice.ComponentId->Product); mc2.ProductGuid = MidiDevice.ComponentId->Product;
mc2.vDriverVersion = (MidiDevice.ComponentId->Version << 8) | (MidiDevice.ComponentId->Revision & 0xFF); } else { mc2.NameGuid = GUID_NULL;
mc2.wMid = MM_MICROSOFT; INIT_MMREG_MID( &mc2.ManufacturerGuid, mc2.wMid );
mc2.wPid = MM_MSFT_WDMAUDIO_MIDIOUT; INIT_MMREG_PID( &mc2.ProductGuid, mc2.wMid );
mc2.vDriverVersion = 0x050a; }
MusicDataRanges = MidiDevice.MusicDataRanges; pDataRange = (PKSDATARANGE_MUSIC)&MusicDataRanges->aDataRanges[0];
//
// Use the first datarange. Could cause problems for pins
// that support multiple music dataranges.
//
if (pDataRange->DataRange.FormatSize < sizeof(KSDATARANGE_MUSIC)) { mc2.wTechnology = MOD_MIDIPORT; mc2.wVoices = 0; mc2.wNotes = 0; mc2.wChannelMask= 0; } else { mc2.wTechnology = GetMidiTechnology( pDataRange ); mc2.wVoices = (WORD)pDataRange->Channels; mc2.wNotes = (WORD)pDataRange->Notes; mc2.wChannelMask= (WORD)pDataRange->ChannelMask; }
mc2.dwSupport = 0L; if (mc2.wTechnology != MOD_MIDIPORT) { mc2.dwSupport |= MIDICAPS_VOLUME | MIDICAPS_LRVOLUME; }
ASSERT(MidiDevice.pwstrName); CapsIndex = GetCapsIndex( pWdmaContext, MidiDevice.pwstrName, MidiOutDevice, DeviceNumber ); if (CapsIndex < 2) { wcsncpy(mc2.szPname, MidiDevice.pwstrName, MAXPNAMELEN); } else { // Only add the index to the string if we need to
swprintf(szTemp, STR_PNAME, MidiDevice.pwstrName, CapsIndex); wcsncpy(mc2.szPname, szTemp, MAXPNAMELEN); } mc2.szPname[MAXPNAMELEN-1] = UNICODE_NULL;
RtlCopyMemory(lpCaps, &mc2, min(dwSize, sizeof(mc2)));
return STATUS_SUCCESS; }
NTSTATUS FillMidiInDevCaps ( PWDMACONTEXT pWdmaContext, DWORD DeviceNumber, LPBYTE lpCaps, DWORD dwSize ) { MIDIINCAPS2W mc2; MIDIDEVICE MidiDevice; UINT CapsIndex; WCHAR szTemp[256];
PAGED_CODE(); MidiDevice = pWdmaContext->MidiInDevs[DeviceNumber];
//
// If available, use the ComponentId to gather information about the device.
// Otherwise, fall back to hardcoded devcaps.
//
if ( (MidiDevice.PreferredDevice == MAXULONG) && (MidiDevice.ComponentId) ) { mc2.NameGuid = MidiDevice.ComponentId->Name;
mc2.wMid = ChooseCorrectMid(&MidiDevice.ComponentId->Manufacturer); mc2.ManufacturerGuid = MidiDevice.ComponentId->Manufacturer;
mc2.wPid = ChooseCorrectPid(&MidiDevice.ComponentId->Product); mc2.ProductGuid = MidiDevice.ComponentId->Product;
mc2.vDriverVersion = (MidiDevice.ComponentId->Version << 8) | (MidiDevice.ComponentId->Revision & 0xFF); } else { mc2.NameGuid = GUID_NULL;
mc2.wMid = MM_MICROSOFT; INIT_MMREG_MID( &mc2.ManufacturerGuid, mc2.wMid );
mc2.wPid = MM_MSFT_WDMAUDIO_MIDIIN; INIT_MMREG_PID( &mc2.ProductGuid, mc2.wPid );
mc2.vDriverVersion = 0x050a; }
mc2.dwSupport = 0L; /* functionality supported by driver */
ASSERT(MidiDevice.pwstrName); CapsIndex = GetCapsIndex( pWdmaContext, MidiDevice.pwstrName, MidiInDevice, DeviceNumber ); if (CapsIndex < 2) { wcsncpy(mc2.szPname, MidiDevice.pwstrName, MAXPNAMELEN); } else { // Only add the index to the string if we need to
swprintf(szTemp, STR_PNAME, MidiDevice.pwstrName, CapsIndex); wcsncpy(mc2.szPname, szTemp, MAXPNAMELEN); } mc2.szPname[MAXPNAMELEN-1] = UNICODE_NULL;
RtlCopyMemory(lpCaps, &mc2, min(dwSize, sizeof(mc2)));
return STATUS_SUCCESS; }
NTSTATUS FillMixerDevCaps ( PWDMACONTEXT pWdmaContext, DWORD DeviceNumber, LPBYTE lpCaps, DWORD dwSize ) { MIXERCAPS2W mc2; MIXERDEVICE Mixer; UINT CapsIndex; WCHAR szTemp[256];
PAGED_CODE(); Mixer = pWdmaContext->MixerDevs[DeviceNumber];
//
// If available, use the ComponentId to gather information about the device.
// Otherwise, fall back to hardcoded devcaps.
//
if ( (Mixer.PreferredDevice == MAXULONG) && (Mixer.ComponentId) ) { mc2.NameGuid = Mixer.ComponentId->Name;
mc2.wMid = ChooseCorrectMid(&Mixer.ComponentId->Manufacturer); mc2.ManufacturerGuid = Mixer.ComponentId->Manufacturer;
mc2.wPid = ChooseCorrectPid(&Mixer.ComponentId->Product); mc2.ProductGuid = Mixer.ComponentId->Product;
mc2.vDriverVersion = (Mixer.ComponentId->Version << 8) | (Mixer.ComponentId->Revision & 0xFF); } else { mc2.NameGuid = GUID_NULL;
mc2.wMid = MM_MICROSOFT; INIT_MMREG_MID( &mc2.ManufacturerGuid, mc2.wMid );
mc2.wPid = MM_MSFT_WDMAUDIO_MIXER; INIT_MMREG_PID( &mc2.ProductGuid, mc2.wPid );
mc2.vDriverVersion = 0x050a; }
mc2.fdwSupport = 0L; /* functionality supported by driver */ mc2.cDestinations = kmxlGetNumDestinations( &Mixer );
ASSERT(Mixer.pwstrName); CapsIndex = GetCapsIndex( pWdmaContext, Mixer.pwstrName, MixerDevice, DeviceNumber ); if (CapsIndex < 2) { wcsncpy(mc2.szPname, Mixer.pwstrName, MAXPNAMELEN); } else { // Only add the index to the string if we need to
swprintf(szTemp, STR_PNAME, Mixer.pwstrName, CapsIndex); wcsncpy(mc2.szPname, szTemp, MAXPNAMELEN); } mc2.szPname[MAXPNAMELEN-1] = UNICODE_NULL;
RtlCopyMemory(lpCaps, &mc2, min(dwSize, sizeof(mc2)));
return STATUS_SUCCESS; }
NTSTATUS FillAuxDevCaps ( PWDMACONTEXT pWdmaContext, DWORD DeviceNumber, LPBYTE lpCaps, DWORD dwSize ) { AUXCAPS2W ac2; AUXDEVICE AuxDev; UINT CapsIndex; WCHAR szTemp[256];
PAGED_CODE(); AuxDev = pWdmaContext->AuxDevs[DeviceNumber];
//
// If available, use the ComponentId to gather information about the device.
// Otherwise, fall back to hardcoded devcaps.
//
if ( (AuxDev.PreferredDevice == MAXULONG) && (AuxDev.ComponentId) ) { ac2.NameGuid = AuxDev.ComponentId->Name;
ac2.wMid = ChooseCorrectMid(&AuxDev.ComponentId->Manufacturer); ac2.ManufacturerGuid = AuxDev.ComponentId->Manufacturer;
ac2.wPid = ChooseCorrectPid(&AuxDev.ComponentId->Product); ac2.ProductGuid = AuxDev.ComponentId->Product;
ac2.vDriverVersion = (AuxDev.ComponentId->Version << 8) | (AuxDev.ComponentId->Revision & 0xFF); } else { ac2.NameGuid = GUID_NULL;
ac2.wMid = MM_MICROSOFT; INIT_MMREG_MID( &ac2.ManufacturerGuid, ac2.wMid );
ac2.wPid = MM_MSFT_WDMAUDIO_AUX; INIT_MMREG_PID( &ac2.ProductGuid, ac2.wPid );
ac2.vDriverVersion = 0x050a; }
ac2.wTechnology = AUXCAPS_CDAUDIO ; // | AUXCAPS_AUXIN ;
ac2.dwSupport = AUXCAPS_LRVOLUME | AUXCAPS_VOLUME;
ASSERT(AuxDev.pwstrName); CapsIndex = GetCapsIndex( pWdmaContext, AuxDev.pwstrName, AuxDevice, DeviceNumber ); if (CapsIndex < 2) { wcsncpy(ac2.szPname, AuxDev.pwstrName, MAXPNAMELEN); } else { // Only add the index to the string if we need to
swprintf(szTemp, STR_PNAME, AuxDev.pwstrName, CapsIndex); wcsncpy(ac2.szPname, szTemp, MAXPNAMELEN); } ac2.szPname[MAXPNAMELEN-1] = UNICODE_NULL;
RtlCopyMemory(lpCaps, &ac2, min(dwSize, sizeof(ac2)));
return STATUS_SUCCESS; }
NTSTATUS wdmaudGetDevCaps ( PWDMACONTEXT pWdmaContext, DWORD DeviceType, DWORD DeviceNumber, LPBYTE lpCaps, DWORD dwSize ) { NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE(); ASSERT(DeviceType == WaveOutDevice || DeviceType == WaveInDevice || DeviceType == MidiOutDevice || DeviceType == MidiInDevice || DeviceType == MixerDevice || DeviceType == AuxDevice);
switch(DeviceType) { case WaveOutDevice: Status = FillWaveOutDevCaps(pWdmaContext,DeviceNumber,lpCaps,dwSize); break;
case WaveInDevice: Status = FillWaveInDevCaps(pWdmaContext,DeviceNumber,lpCaps,dwSize); break;
case MidiOutDevice: Status = FillMidiOutDevCaps(pWdmaContext,DeviceNumber,lpCaps,dwSize); break;
case MidiInDevice: Status = FillMidiInDevCaps(pWdmaContext,DeviceNumber,lpCaps,dwSize); break;
case MixerDevice: Status = FillMixerDevCaps(pWdmaContext,DeviceNumber,lpCaps,dwSize); break;
case AuxDevice: Status = FillAuxDevCaps(pWdmaContext,DeviceNumber,lpCaps,dwSize); break;
default: ASSERT(0); }
return Status; }
BOOL IsEqualInterface ( PKSPIN_INTERFACE pInterface1, PKSPIN_INTERFACE pInterface2 ) { PAGED_CODE(); return ( IsEqualGUID(&pInterface1->Set, &pInterface2->Set) && (pInterface1->Id == pInterface2->Id) && (pInterface1->Flags == pInterface2->Flags) ); }
/****************************************************************************
* * PnPCompletionRoutine - Finish the PnP Irp * * Not Exported. * * ENTRY: Standard PIO_COMPLETION_ROUTINE. * * EXIT: Standard NT status. * ***************************************************************************/ NTSTATUS PnPCompletionRoutine(PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID pContext) { PAGED_CODE(); //
// Wake ourselves: the device has finally started/stopped.
//
KeSetEvent((PKEVENT)pContext, 0, FALSE);
//
// The completion itself never fails.
//
RETURN(STATUS_MORE_PROCESSING_REQUIRED); }
/****************************************************************************
* * SynchronousCallDriver - Synchronously send a plug and play irp * * Not exported. * * ENTRY: pfdo is the function device object. * * pIrp is the IRP to send. * * ppResult is filled in with the information value. * * EXIT: Standard NT status. * ***************************************************************************/ NTSTATUS SynchronousCallDriver(PDEVICE_OBJECT pfdo, PIRP pIrp, PVOID *ppResult) { NTSTATUS ntStatus; KEVENT keEventObject;
PAGED_CODE(); //
// Set the thread (should typically be msgsrv32's).
//
pIrp->Tail.Overlay.Thread=PsGetCurrentThread();
//
// Initialize the status block.
//
pIrp->IoStatus.Status=STATUS_NOT_SUPPORTED;
//
// Initialize our wait event, in case we need to wait.
//
KeInitializeEvent( &keEventObject, SynchronizationEvent, FALSE);
//
// Set our completion routine so we can free the IRP and wake
// ourselfs.
//
IoSetCompletionRoutine( pIrp, PnPCompletionRoutine, &keEventObject, TRUE, TRUE, TRUE);
//
// Call the stack now.
//
ntStatus=IoCallDriver(pfdo, pIrp);
//
// Wait if it is pending.
//
if (ntStatus==STATUS_PENDING) {
//
// Wait for the completion.
//
ntStatus=KeWaitForSingleObject( &keEventObject, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL );
//
// Three cases: timeout (which can't be since we pass null),
// success or USER_APC (which I don't know what to do).
//
if (ntStatus==STATUS_USER_APC) {
// IopCancelAlertedRequest(&keEventObject, pIrp );
} }
//
// Initialize the result, if requested.
//
if (ppResult) *ppResult=NULL;
//
// Otherwise return the result of the operation.
//
ntStatus=pIrp->IoStatus.Status;
//
// Fill in the result if requested.
//
if (ppResult) *ppResult=(PVOID)(pIrp->IoStatus.Information);
RETURN(ntStatus); }
BOOL IsPinForDevNode ( PFILE_OBJECT pFileObjectDevice, ULONG Index, PCWSTR DeviceInterface ) { NTSTATUS Status; WCHAR szInterfaceName[256]; BOOL Result;
PAGED_CODE(); Status = GetSysAudioProperty(pFileObjectDevice, KSPROPERTY_SYSAUDIO_DEVICE_INTERFACE_NAME, Index, sizeof(szInterfaceName), szInterfaceName); if (NT_SUCCESS(Status)) { // TODO: Eventually will not need to munge the strings
PWSTR pszIn = NULL; PWSTR pszSysaudio = NULL;
Status = AudioAllocateMemory_Paged((wcslen(DeviceInterface)+1) * sizeof(WCHAR), TAG_Audp_NAME, DEFAULT_MEMORY, &pszIn ); if (NT_SUCCESS(Status)) { Status = AudioAllocateMemory_Paged((wcslen(szInterfaceName)+1) * sizeof(WCHAR), TAG_Audp_NAME, DEFAULT_MEMORY, &pszSysaudio ); if (NT_SUCCESS(Status)) { PWCHAR pch;
wcscpy(pszIn, DeviceInterface); wcscpy(pszSysaudio, szInterfaceName);
// pszIn[1] = '\\';
pszSysaudio[1] = '\\';
// _DbgPrintF( DEBUGLVL_VERBOSE, ("IsPinForDevnode: Sysaudio returns interface name %ls", pszSysaudio));
// _DbgPrintF( DEBUGLVL_VERBOSE, ("IsPinForDevnode: Comparing against %ls", pszIn));
if (!MyWcsicmp(pszIn, pszSysaudio)) { Result = TRUE; } else { Result = FALSE; } AudioFreeMemory_Unknown(&pszSysaudio); } else { Result = FALSE; } AudioFreeMemory_Unknown(&pszIn); } else { Result = FALSE; } } else { Result = FALSE; }
return Result; }
ULONG FindMixerForDevNode( IN PMIXERDEVICE paMixerDevs, IN PCWSTR DeviceInterface ) { ULONG i;
PAGED_CODE(); for( i = 0; i < MAXNUMDEVS; i++ ) {
if( ( paMixerDevs[ i ].Device != UNUSED_DEVICE ) && !MyWcsicmp( paMixerDevs[ i ].DeviceInterface, DeviceInterface ) ) { return( i ); } }
return( UNUSED_DEVICE ); }
NTSTATUS InitializeAuxGetNumDevs ( PWDMACONTEXT pWdmaContext, PCWSTR DeviceInterface ) { NTSTATUS Status; PWSTR pwstrNameAux = NULL; DWORD dw; ULONG MixerIndex; PKSCOMPONENTID ComponentId = NULL;
PAGED_CODE(); //
// Get the name from the mixer device
//
MixerIndex = FindMixerForDevNode(pWdmaContext->MixerDevs, DeviceInterface); if ( (MixerIndex != UNUSED_DEVICE) && (pWdmaContext->MixerDevs[MixerIndex].pwstrName != NULL) ) { //
// Check for CD volume control
//
Status = IsVolumeControl( pWdmaContext, DeviceInterface, MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC, &dw, &dw );
if(NT_SUCCESS(Status)) { Status = AudioAllocateMemory_Paged((wcslen(pWdmaContext->MixerDevs[MixerIndex].pwstrName) + 1) * sizeof(WCHAR), TAG_Audp_NAME, DEFAULT_MEMORY, &pwstrNameAux); if (NT_SUCCESS(Status)) { wcscpy(pwstrNameAux, pWdmaContext->MixerDevs[MixerIndex].pwstrName);
if (pWdmaContext->MixerDevs[MixerIndex].ComponentId) { Status = AudioAllocateMemory_Paged(sizeof(*ComponentId), TAG_Audp_NAME, DEFAULT_MEMORY, &ComponentId); if (NT_SUCCESS(Status)) { RtlCopyMemory(ComponentId, pWdmaContext->MixerDevs[MixerIndex].ComponentId, sizeof(*ComponentId)); } } else { ComponentId = NULL; }
Status = AddDevice(pWdmaContext, 0, AuxDevice, DeviceInterface, 0, pwstrNameAux, FALSE, NULL, ComponentId); if (NT_SUCCESS(Status)) { FindVolumeControl(pWdmaContext, DeviceInterface, AuxDevice); } else { AudioFreeMemory_Unknown(&ComponentId); AudioFreeMemory_Unknown(&pwstrNameAux); } } } }
// if anything fails, still return success so InitializeGetNumDevs
// returns 0 devices
return(STATUS_SUCCESS); }
NTSTATUS InitializeMixerGetNumDevs ( IN PWDMACONTEXT pWdmaContext, IN PCWSTR DeviceInterface ) { NTSTATUS Status; PMIXERDEVICE paMixerDevs; PWAVEDEVICE paWaveOutDevs; PWAVEDEVICE paWaveInDevs; ULONG i, j;
PAGED_CODE(); // WARNING !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING
//
// This function makes a few assumptions. If any of the assumptions
// below change, this function must be updated accordingly!
//
// 1) Mixer devices are initialized after all other device classes.
//
// 2) SysAudio device numbers are the same for the different interfaces
// (WaveOut,WaveIn,MidiOut,MidiIn,Mixer) for a devnode.
//
// WARNING !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING
paWaveOutDevs = pWdmaContext->WaveOutDevs; paWaveInDevs = pWdmaContext->WaveInDevs; paMixerDevs = pWdmaContext->MixerDevs;
for( i = 0; i < MAXNUMDEVS; i++ ) {
//
// Look for WaveOut interfaces
//
if( ( paWaveOutDevs[ i ].Device != UNUSED_DEVICE ) && ( !MyWcsicmp(paWaveOutDevs[ i ].DeviceInterface, DeviceInterface) ) ) {
for( j = 0; j < MAXNUMDEVS; j++ ) {
//ASSERT(paMixerDevs[j].Device == UNUSED_DEVICE ?
// NULL == paMixerDevs[j].DeviceInterface :
// NULL != paMixerDevs[j].DeviceInterface);
if( ( paMixerDevs[ j ].Device != UNUSED_DEVICE ) && ( !MyWcsicmp(paMixerDevs[ j ].DeviceInterface, DeviceInterface) ) ) { //
// We've found a devnode that has already been added
// to the mixer list.
//
kmxlDeInit(&paMixerDevs[j]); paMixerDevs[ j ].Device = paWaveOutDevs[ i ].Device; break; }
} // for
if( j == MAXNUMDEVS ) {
for( j = 0; j < MAXNUMDEVS; j++ ) { if( paMixerDevs[ j ].Device == UNUSED_DEVICE ) { break; } }
if( j == MAXNUMDEVS ) { RETURN( STATUS_INSUFFICIENT_RESOURCES ); }
Status = AudioAllocateMemory_Paged((wcslen(paWaveOutDevs[i].pwstrName) + 1) * sizeof(WCHAR), TAG_Audp_NAME, DEFAULT_MEMORY, &paMixerDevs[j].pwstrName);
if (NT_SUCCESS(Status)) { wcscpy(paMixerDevs[j].pwstrName, paWaveOutDevs[i].pwstrName);
Status = AudioAllocateMemory_Paged((wcslen(paWaveOutDevs[i].DeviceInterface) + 1) * sizeof(WCHAR), TAG_AudD_DEVICEINFO, DEFAULT_MEMORY, &paMixerDevs[j].DeviceInterface);
if (NT_SUCCESS(Status)) { wcscpy(paMixerDevs[j].DeviceInterface, paWaveOutDevs[i].DeviceInterface); paMixerDevs[j].Device = paWaveOutDevs[i].Device; paMixerDevs[j].PreferredDevice = paWaveOutDevs[i].PreferredDevice;
if (paWaveOutDevs[i].ComponentId) { Status = AudioAllocateMemory_Paged(sizeof(KSCOMPONENTID), TAG_Audp_NAME, DEFAULT_MEMORY, &paMixerDevs[j].ComponentId); if (NT_SUCCESS(Status)) { RtlCopyMemory(paMixerDevs[j].ComponentId, paWaveOutDevs[i].ComponentId, sizeof(KSCOMPONENTID)); } } else { paMixerDevs[j].ComponentId = NULL; } } else { AudioFreeMemory_Unknown(&paMixerDevs[j].pwstrName); } }
if (!NT_SUCCESS(Status)) { RETURN( Status ); } } // if
} // if
//
// Loop for WaveIn interfaces.
//
if( ( paWaveInDevs[ i ].Device != UNUSED_DEVICE ) && ( !MyWcsicmp(paWaveInDevs[ i ].DeviceInterface, DeviceInterface) ) ) {
for( j = 0; j < MAXNUMDEVS; j++ ) {
ASSERT(paMixerDevs[j].Device == UNUSED_DEVICE ? NULL == paMixerDevs[j].DeviceInterface : NULL != paMixerDevs[j].DeviceInterface);
if( ( paMixerDevs[ j ].Device != UNUSED_DEVICE ) && ( !MyWcsicmp(paMixerDevs[ j ].DeviceInterface, DeviceInterface) ) ) { //
// We've found a devnode that has already been added
// to the mixer list.
//
kmxlDeInit(&paMixerDevs[j]); paMixerDevs[ j ].Device = paWaveInDevs[ i ].Device; break; }
} // for
if( j == MAXNUMDEVS ) {
for( j = 0; j < MAXNUMDEVS; j++ ) { if( paMixerDevs[ j ].Device == UNUSED_DEVICE ) { break; } }
if( j == MAXNUMDEVS ) { RETURN( STATUS_INSUFFICIENT_RESOURCES ); }
Status = AudioAllocateMemory_Paged((wcslen(paWaveInDevs[i].pwstrName) + 1) * sizeof(WCHAR), TAG_AudD_DEVICEINFO, DEFAULT_MEMORY, &paMixerDevs[j].pwstrName);
if (NT_SUCCESS(Status)) { wcscpy(paMixerDevs[j].pwstrName, paWaveInDevs[i].pwstrName);
Status = AudioAllocateMemory_Paged((wcslen(paWaveInDevs[i].DeviceInterface) + 1) * sizeof(WCHAR), TAG_AudD_DEVICEINFO, DEFAULT_MEMORY, &paMixerDevs[j].DeviceInterface);
if (NT_SUCCESS(Status)) { wcscpy(paMixerDevs[j].DeviceInterface, paWaveInDevs[i].DeviceInterface); paMixerDevs[j].Device = paWaveInDevs[i].Device; paMixerDevs[j].PreferredDevice = paWaveInDevs[i].PreferredDevice;
if (paWaveInDevs[i].ComponentId) { Status = AudioAllocateMemory_Paged(sizeof(KSCOMPONENTID), TAG_Audp_NAME, DEFAULT_MEMORY, &paMixerDevs[j].ComponentId); if (NT_SUCCESS(Status)) { RtlCopyMemory(paMixerDevs[j].ComponentId, paWaveInDevs[i].ComponentId, sizeof(KSCOMPONENTID)); } } else { paMixerDevs[j].ComponentId = NULL; } } else { AudioFreeMemory_Unknown(&paMixerDevs[j].pwstrName); } }
if (!NT_SUCCESS(Status)) { RETURN( Status ); } } // if
} // if
} // for
return( STATUS_SUCCESS ); }
NTSTATUS InitializeGetNumDevs ( PWDMACONTEXT pWdmaContext, DWORD DeviceType, PCWSTR DeviceInterface, LPDWORD lpNumberOfDevices ) { NTSTATUS Status=STATUS_SUCCESS; HANDLE hDevice = NULL; PDATARANGES pDataRanges = NULL; PIDENTIFIERS pPinInterfaces = NULL; PFILE_OBJECT pFileObjectDevice = NULL; KSPIN_INTERFACE RequestedInterface; KSPIN_DATAFLOW RequestedDataFlow; GUID RequestedMajorFormat; GUID RequestedSubFormat; GUID guidCategory; ULONG cPins; ULONG PinId; ULONG Device; ULONG TotalDevices; ULONG ulSize; DWORD cDevs; ULONG i; BOOL fDeviceAdded = FALSE;
PAGED_CODE(); ASSERT(DeviceType == WaveOutDevice || DeviceType == WaveInDevice || DeviceType == MidiOutDevice || DeviceType == MidiInDevice || DeviceType == MixerDevice || DeviceType == AuxDevice);
DPF( DL_TRACE|FA_SYSAUDIO, ("Class = %d", DeviceType) );
//
// Setup a structure to compare with the interfaces that
// we want to find the number of.
//
switch (DeviceType) { case WaveOutDevice: RequestedInterface.Set = KSINTERFACESETID_Media; RequestedInterface.Id = KSINTERFACE_MEDIA_WAVE_QUEUED; RequestedInterface.Flags = 0; RequestedDataFlow = KSPIN_DATAFLOW_IN; RequestedMajorFormat = KSDATAFORMAT_TYPE_AUDIO; RequestedSubFormat = KSDATAFORMAT_TYPE_WILDCARD; break;
case WaveInDevice: RequestedInterface.Set = KSINTERFACESETID_Standard; RequestedInterface.Id = KSINTERFACE_STANDARD_STREAMING; RequestedInterface.Flags = 0; RequestedDataFlow = KSPIN_DATAFLOW_OUT; RequestedMajorFormat = KSDATAFORMAT_TYPE_AUDIO; RequestedSubFormat = KSDATAFORMAT_TYPE_WILDCARD; break;
case MidiOutDevice: RequestedInterface.Set = KSINTERFACESETID_Standard; RequestedInterface.Id = KSINTERFACE_STANDARD_STREAMING; RequestedInterface.Flags = 0; RequestedDataFlow = KSPIN_DATAFLOW_IN; RequestedMajorFormat = KSDATAFORMAT_TYPE_MUSIC; RequestedSubFormat = KSDATAFORMAT_SUBTYPE_MIDI; break;
case MidiInDevice: RequestedInterface.Set = KSINTERFACESETID_Standard; RequestedInterface.Id = KSINTERFACE_STANDARD_STREAMING; RequestedInterface.Flags = 0; RequestedDataFlow = KSPIN_DATAFLOW_OUT; RequestedMajorFormat = KSDATAFORMAT_TYPE_MUSIC; RequestedSubFormat = KSDATAFORMAT_SUBTYPE_MIDI; break;
case MixerDevice: Status = InitializeMixerGetNumDevs( pWdmaContext, DeviceInterface ); fDeviceAdded = NT_SUCCESS(Status); goto exit;
case AuxDevice: Status = InitializeAuxGetNumDevs( pWdmaContext, DeviceInterface ); fDeviceAdded = NT_SUCCESS(Status); goto exit; } //
// Get a handle to sysaudio
//
Status = OpenSysAudio(&hDevice, &pFileObjectDevice);
if(!NT_SUCCESS(Status)) { goto exit; } //
// for every pin on every device see if the interface matches
// the DeviceType requested from user mode
//
Status = GetSysAudioProperty(pFileObjectDevice, KSPROPERTY_SYSAUDIO_DEVICE_COUNT, 0, // not used
sizeof(TotalDevices), &TotalDevices); if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("GetSysAudioProperty failed Status=%X",Status) ); goto exit; }
for (Device = 0; Device < TotalDevices; Device++) { //
// Set the default renderer
//
Status = SetSysAudioProperty(pFileObjectDevice, KSPROPERTY_SYSAUDIO_DEVICE_INSTANCE, sizeof(Device), &Device); if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("GetSysAudioProperty failed Status=%X",Status) ); goto exit; }
//
// Verify that this device matches the DevNode
// being enumerated
//
if (!IsPinForDevNode(pFileObjectDevice,Device,DeviceInterface)) { continue; }
//
// Get the number of pins on the default renderer
//
Status = GetPinProperty(pFileObjectDevice, KSPROPERTY_PIN_CTYPES, 0, sizeof(cPins), &cPins);
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("GetPinProperty failed Status=%X",Status) ); goto exit; }
for(PinId = cPins; PinId > 0; PinId--) { KSPIN_DATAFLOW DataFlow; KSPIN_COMMUNICATION CommunicationType; PKSDATARANGE pDataRange; PWSTR pwstrName = NULL; PKSCOMPONENTID ComponentId = NULL; BOOL fInterfaceFound; BOOL fUsePreferred; ULONG index; ULONG d;
//
// Check the dataflow
//
Status = GetPinProperty(pFileObjectDevice, KSPROPERTY_PIN_DATAFLOW, PinId-1, sizeof(KSPIN_DATAFLOW), &DataFlow);
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("GetPinProperty failed Status=%X",Status) ); goto exit; }
if(RequestedDataFlow != DataFlow) { continue; }
//
// Check the communication type
//
Status = GetPinProperty(pFileObjectDevice, KSPROPERTY_PIN_COMMUNICATION, PinId-1, sizeof(KSPIN_COMMUNICATION), &CommunicationType);
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("GetPinProperty failed Status=%X",Status) ); goto exit; }
if(KSPIN_COMMUNICATION_SINK != CommunicationType && KSPIN_COMMUNICATION_BOTH != CommunicationType) { continue; }
//
// Allocates memory on my behalf. Free later!!!
//
Status = GetPinPropertyEx(pFileObjectDevice, KSPROPERTY_PIN_INTERFACES, PinId-1, &pPinInterfaces); //
// GetPinPropertyEx can return STATUS_PROPSET_NOT_FOUND which we
// expect. Thus, if we get this error, we need to keep looking rather
// then fail. If it returns STATUS_PROPSET_NOT_FOUND pPinInterfaces
// will be NULL thus we must not touch it.
//
// Thus, if not successful AND not a successful error -> error out.
//
if(!NT_SUCCESS(Status) && Status != STATUS_PROPSET_NOT_FOUND ) { DPF(DL_WARNING|FA_SYSAUDIO,("GetPinPropertyEx failed Status=%X",Status) ); goto exit; }
if( pPinInterfaces ) { //
// Find an interface that matches
//
fInterfaceFound = FALSE; for(index = 0; index < pPinInterfaces->Count; index++) { if (IsEqualInterface(&RequestedInterface, &pPinInterfaces->aIdentifiers[index])) { fInterfaceFound = TRUE; break; } }
//
// We're done with the memory, so free
//
AudioFreeMemory_Unknown(&pPinInterfaces);
if (!fInterfaceFound) { continue; } }
//
// If the device exposes a component Id, get it and cache it in AddDevice
//
Status = AudioAllocateMemory_Paged(sizeof(*ComponentId), TAG_Audp_NAME, ZERO_FILL_MEMORY, &ComponentId); if(NT_SUCCESS(Status)) { Status = GetSysAudioProperty(pFileObjectDevice, KSPROPERTY_SYSAUDIO_COMPONENT_ID, Device, sizeof(*ComponentId), ComponentId); //
// WorkItem: It is highly likely that GetSysAudioProperty will
// return STATUS_INVALID_DEVICE_REQUEST for this call. Why?
//
if (!NT_SUCCESS(Status)) { // Not a failure
AudioFreeMemory_Unknown(&ComponentId); ComponentId = NULL; } }
fUsePreferred = FALSE; pwstrName = NULL;
// Get the friendly name for this device.
// - First see if it the category is KSCATEGORY_WDMAUD_USE_PIN_NAME because
// SWMIDI uses this and there should only be one instance of SWMIDI
// in the system.
//
// - Next check to see if the pins provide names, without using the
// KSCATEGORY_WDMAUD_USE_PIN_NAME category. If so, use the name provided
// by the pin.
//
// - Lastly, use the friendly name for the device if it exists.
//
// If all attempts to get a name fail, then this pin is not used by WDMAUD.
Status = GetPinProperty(pFileObjectDevice, KSPROPERTY_PIN_CATEGORY, PinId-1, sizeof(GUID), &guidCategory); //
// WorkItem: GetPinProperty returns code c0000225 - STATUS_INVALID_DEVICE_REQUEST
// for this call. Why?
//
if(NT_SUCCESS(Status)) { if(IsEqualGUID(&KSCATEGORY_WDMAUD_USE_PIN_NAME, &guidCategory)) { Status = GetPinPropertyEx(pFileObjectDevice, KSPROPERTY_PIN_NAME, PinId-1, &pwstrName); //
// GetPinPropertyEx can return STATUS_PROPSET_NOT_FOUND which we
// expect. Thus, if we get this error, we need to keep looking rather
// then fail. If it returns STATUS_PROPSET_NOT_FOUND pwstrName
// will be NULL thus we must not touch it.
//
// Thus, if successful or it's the successful error code -> success
//
if(NT_SUCCESS(Status) || Status == STATUS_PROPSET_NOT_FOUND) { fUsePreferred = TRUE; } else { ASSERT(pwstrName == NULL); } } }
// As long as this is not SWMIDI, first try reading the name from the component ID
if ((fUsePreferred == FALSE) && (ComponentId != NULL)) { ReadProductNameFromMediaCategories(&ComponentId->Name, &pwstrName); }
// If that didn't work, take the regular old friendly name
if(pwstrName == NULL) { Status = GetSysAudioProperty( pFileObjectDevice, KSPROPERTY_SYSAUDIO_DEVICE_FRIENDLY_NAME, Device, sizeof(ulSize), &ulSize);
if(NT_SUCCESS(Status)) { Status = AudioAllocateMemory_Paged(ulSize, TAG_Audp_NAME, ZERO_FILL_MEMORY, &pwstrName); if(!NT_SUCCESS(Status)) { goto exit; }
Status = GetSysAudioProperty( pFileObjectDevice, KSPROPERTY_SYSAUDIO_DEVICE_FRIENDLY_NAME, Device, ulSize, pwstrName);
if (!NT_SUCCESS(Status)) { AudioFreeMemory_Unknown(&pwstrName); } }
//
// Last chance...don't use devices without names
//
if (pwstrName == NULL) { AudioFreeMemory_Unknown(&ComponentId); continue; } }
//
// Allocates memory on my behalf. Store these
// dataranges in the structure of the device if
// we find a match that is good.
//
Status = GetPinPropertyEx(pFileObjectDevice, KSPROPERTY_PIN_DATARANGES, PinId-1, &pDataRanges); //
// GetPinPropertyEx can return STATUS_PROPSET_NOT_FOUND which we
// expect. Thus, if we get this error, we need to keep looking rather
// then fail. If it returns STATUS_PROPSET_NOT_FOUND pDataRanges
// will be NULL thus we must not touch it.
//
// Thus, if not successful AND not a successful error -> error out.
//
if (!NT_SUCCESS(Status) && Status != STATUS_PROPSET_NOT_FOUND ) { DPF(DL_WARNING|FA_SYSAUDIO,("GetPinPropertyEx failed Status=%X",Status) ); goto exit; }
if( pDataRanges ) { //
// See if we have a majorformat and subformat that
// we want
//
pDataRange = &pDataRanges->aDataRanges[0];
for(d = 0; d < pDataRanges->Count; d++) { if (IsEqualGUID(&RequestedMajorFormat, &pDataRange->MajorFormat) && (IsEqualGUID(&RequestedSubFormat, &KSDATAFORMAT_TYPE_WILDCARD) || IsEqualGUID(&RequestedSubFormat, &pDataRange->SubFormat) ) ) {
DPF( DL_TRACE|FA_SYSAUDIO, ("Found device!!!") );
//
// Store so that we can retrieve later on
// an open or getcaps call
//
Status = AddDevice(pWdmaContext, Device, DeviceType, DeviceInterface, PinId-1, pwstrName, fUsePreferred, pDataRanges, ComponentId);
if (NT_SUCCESS(Status)) { fDeviceAdded = TRUE;
//
// Mark these NULL so that it doesn't get freed
// at the end of the loop.
//
// This memory will get freed when the devnode
// is removed and the device entry gets cleaned
// up in RemoveDevNode.
//
pwstrName = NULL; pDataRanges = NULL; ComponentId = NULL; }
break; // Don't need to check anymore dataranges
}
// Get the pointer to the next data range
(PUCHAR)pDataRange += ((pDataRange->FormatSize + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT);
} }
//
// We're done with the memory, so free
//
AudioFreeMemory_Unknown(&pDataRanges); AudioFreeMemory_Unknown(&pwstrName); AudioFreeMemory_Unknown(&ComponentId);
} // pin enumeration
} // device enumeration
exit: //
// Close down sysaudio for now
//
AudioFreeMemory_Unknown(&pPinInterfaces); AudioFreeMemory_Unknown(&pDataRanges);
if(pFileObjectDevice != NULL) { ObDereferenceObject(pFileObjectDevice); } if(hDevice != NULL) { NtClose(hDevice); }
if(fDeviceAdded) { PCOMMONDEVICE *ppCommonDevice; ULONG cRealDevs;
ppCommonDevice = &pWdmaContext->apCommonDevice[DeviceType][0]; for (cRealDevs = cDevs = i = 0; i < MAXNUMDEVS; i++) { if (ppCommonDevice[i]->Device == UNUSED_DEVICE || MyWcsicmp(ppCommonDevice[i]->DeviceInterface, DeviceInterface)) { continue; } ++cRealDevs; if (ppCommonDevice[i]->PreferredDevice == MAXULONG || ppCommonDevice[i]->PreferredDevice == i) { ++cDevs; } } if(cDevs == 0 && cRealDevs > 0) { *lpNumberOfDevices = MAXULONG; } else { *lpNumberOfDevices = cDevs; } } else { *lpNumberOfDevices = 0; }
RETURN( Status ); }
NTSTATUS wdmaudGetNumDevs ( PWDMACONTEXT pContext, DWORD DeviceType, PCWSTR DeviceInterfaceIn, LPDWORD lpNumberOfDevices ) { PDEVNODE_LIST_ITEM pDevNodeListItem; NTSTATUS Status = STATUS_SUCCESS; LARGE_INTEGER li = {0, 0}; PLIST_ENTRY ple;
PAGED_CODE(); ASSERT(DeviceType == WaveOutDevice || DeviceType == WaveInDevice || DeviceType == MidiOutDevice || DeviceType == MidiInDevice || DeviceType == MixerDevice || DeviceType == AuxDevice);
*lpNumberOfDevices = 0;
//
// Can't use WdmaGrabMutex/WdmaReleaseMutex here
//
ASSERT(Status == STATUS_SUCCESS);
for(ple = pContext->DevNodeListHead.Flink; ple != &pContext->DevNodeListHead; ple = ple->Flink) { pDevNodeListItem = CONTAINING_RECORD(ple, DEVNODE_LIST_ITEM, Next);
if(!MyWcsicmp(pDevNodeListItem->DeviceInterface, DeviceInterfaceIn)) {
if(pDevNodeListItem->cDevices[DeviceType] == MAXULONG) {
DPF( DL_TRACE|FA_SYSAUDIO, ("MAXULONG: %ls[%d]", DeviceInterfaceIn, DeviceType));
//
// This status code there are still some pending add or
// remove devices so the actual number of devices can't
// be returned.
//
Status = STATUS_DEVICE_OFF_LINE; } else { *lpNumberOfDevices = pDevNodeListItem->cDevices[DeviceType]; ASSERT(Status == STATUS_SUCCESS); } goto exit; } } //
// This status code there are still some pending add or
// remove devices so the actual number of devices can't
// be returned.
//
Status = STATUS_DEVICE_OFF_LINE; exit: if(NT_SUCCESS(Status)) { DPF( DL_TRACE|FA_SYSAUDIO, ("SUCCESS %ls[%d] %d", DeviceInterfaceIn, DeviceType, *lpNumberOfDevices)); }
RETURN(Status); }
NTSTATUS PinProperty ( PFILE_OBJECT pFileObject, const GUID *pPropertySet, ULONG ulPropertyId, ULONG ulFlags, ULONG cbProperty, PVOID pProperty ) { KSPROPERTY Property; ULONG BytesReturned; NTSTATUS Status = STATUS_INVALID_PARAMETER;
PAGED_CODE(); if (pFileObject) { Property.Set = *pPropertySet; Property.Id = ulPropertyId; Property.Flags = ulFlags;
ASSERT( pFileObject || !"PinProperty called with invalid pFileObject");
if (ulPropertyId == KSPROPERTY_CONNECTION_STATE) { DPF( DL_TRACE|FA_SYSAUDIO, ("State=%d",*(PKSSTATE)pProperty)); }
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Id=%X",ulPropertyId) );
Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_PROPERTY, &Property, sizeof(Property), pProperty, cbProperty, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, pProperty=%X,cbProperty=%X,BytesRet=%d", Status,pProperty,cbProperty,BytesReturned) );
}
if(!NT_SUCCESS(Status)) { DPF(DL_TRACE|FA_SYSAUDIO, ("FAILED SetState = %d",*(PKSSTATE)pProperty)); goto exit; } exit: RETURN(Status); }
NTSTATUS PinMethod ( PFILE_OBJECT pFileObject, const GUID *pMethodSet, ULONG ulMethodId, ULONG ulFlags, ULONG cbMethod, PVOID pMethod ) { KSMETHOD Method; ULONG BytesReturned; NTSTATUS Status = STATUS_INVALID_PARAMETER;
PAGED_CODE(); if (pFileObject) { Method.Set = *pMethodSet; Method.Id = ulMethodId; Method.Flags = ulFlags;
ASSERT( pFileObject || !"PinMethod called with invalid pFileObject");
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Id=%X",ulMethodId) );
Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_METHOD, &Method, sizeof(Method), pMethod, cbMethod, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X, pMethod=%X,cbMethod=%X,BytesRet=%d", Status,pMethod,cbMethod,BytesReturned) ); }
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Status=%X",Status) ); goto exit; } exit: RETURN(Status); }
NTSTATUS AttachVirtualSource( PFILE_OBJECT pFileObject, ULONG ulPinId ) { SYSAUDIO_ATTACH_VIRTUAL_SOURCE AttachVirtualSource; NTSTATUS Status = STATUS_INVALID_PARAMETER; ULONG BytesReturned;
PAGED_CODE(); if (pFileObject) { if(ulPinId == MAXULONG) { DPF(DL_WARNING|FA_SYSAUDIO,("Invalid ulPinId=%X",ulPinId) ); Status = STATUS_INVALID_DEVICE_REQUEST; goto exit; } AttachVirtualSource.Property.Set = KSPROPSETID_Sysaudio_Pin; AttachVirtualSource.Property.Id = KSPROPERTY_SYSAUDIO_ATTACH_VIRTUAL_SOURCE; AttachVirtualSource.Property.Flags = KSPROPERTY_TYPE_SET; AttachVirtualSource.MixerPinId = ulPinId;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Id=%X", AttachVirtualSource.Property.Id) );
Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_PROPERTY, &AttachVirtualSource, sizeof(AttachVirtualSource), NULL, 0, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,BytesRet=%d", Status,BytesReturned) ); }
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Status=%X",Status) ); goto exit; } exit: RETURN(Status); }
NTSTATUS SysAudioPnPNotification( IN PVOID NotificationStructure, IN PVOID _Context ) { PWDMACONTEXT pContext = (PWDMACONTEXT)_Context; PDEVICE_INTERFACE_CHANGE_NOTIFICATION pNotification; NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE(); ASSERT(pContext);
DPF( DL_TRACE|FA_SYSAUDIO,("pWdmaContext=%08Xh", pContext) );
pNotification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStructure;
// The notification sends null terminated unicode strings
if(IsEqualGUID(&pNotification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL)) { Status = QueueWorkList(pContext, InitializeSysaudio, pContext, 0); if (!NT_SUCCESS(Status)) { // At this point pContext->fInitializeSysaudio will still be false because we never
// ran the work item. If we don't signal this event, IOCTL_WDMAUD_INIT will deadlock.
ASSERT(pContext->fInitializeSysaudio == FALSE);
KeSetEvent(&pContext->InitializedSysaudioEvent, 0, FALSE); } } return(Status); }
NTSTATUS InitializeSysaudio( PVOID Reference1, PVOID Reference2 ) { PWDMACONTEXT pWdmaContext = (PWDMACONTEXT)Reference1; SYSAUDIO_CREATE_VIRTUAL_SOURCE CreateVirtualSource; NTSTATUS Status = STATUS_SUCCESS; ULONG BytesReturned; KSEVENT Event;
PAGED_CODE(); ASSERT(pWdmaContext);
DPF( DL_TRACE|FA_SYSAUDIO,("pWdmaContext=%08Xh", pWdmaContext) );
if(pWdmaContext->SysaudioWorkerObject == NULL) { goto exit; }
if( pWdmaContext->pFileObjectSysaudio == NULL ) { pWdmaContext->pFileObjectSysaudio = kmxlOpenSysAudio(); if( pWdmaContext->pFileObjectSysaudio == NULL ) { DPF(DL_WARNING|FA_SYSAUDIO,("NULL pFileObjectSysaudio, pWdmaContext=%08X",pWdmaContext) ); goto exit; } } //
// Initialize the wave and synth virtual source lines
//
CreateVirtualSource.Property.Set = KSPROPSETID_Sysaudio; CreateVirtualSource.Property.Flags = KSPROPERTY_TYPE_GET;
if(pWdmaContext->VirtualWavePinId == MAXULONG) { CreateVirtualSource.Property.Id = KSPROPERTY_SYSAUDIO_CREATE_VIRTUAL_SOURCE_ONLY; CreateVirtualSource.PinCategory = KSNODETYPE_LEGACY_AUDIO_CONNECTOR; CreateVirtualSource.PinName = KSNODETYPE_LEGACY_AUDIO_CONNECTOR;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY %X",CreateVirtualSource) );
Status = KsSynchronousIoControlDevice( pWdmaContext->pFileObjectSysaudio, KernelMode, IOCTL_KS_PROPERTY, &CreateVirtualSource, sizeof(CreateVirtualSource), &pWdmaContext->VirtualWavePinId, sizeof(pWdmaContext->VirtualWavePinId), &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,BytesRet=%d", Status,BytesReturned) );
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Property query Status=%X",Status) ); goto exit; } ASSERT(BytesReturned == sizeof(pWdmaContext->VirtualWavePinId)); } if(pWdmaContext->VirtualMidiPinId == MAXULONG) { CreateVirtualSource.Property.Id = KSPROPERTY_SYSAUDIO_CREATE_VIRTUAL_SOURCE; CreateVirtualSource.PinCategory = KSNODETYPE_SYNTHESIZER; CreateVirtualSource.PinName = KSNODETYPE_SWSYNTH;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY %X",CreateVirtualSource) );
Status = KsSynchronousIoControlDevice( pWdmaContext->pFileObjectSysaudio, KernelMode, IOCTL_KS_PROPERTY, &CreateVirtualSource, sizeof(CreateVirtualSource), &pWdmaContext->VirtualMidiPinId, sizeof(pWdmaContext->VirtualMidiPinId), &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,BytesRet=%d", Status,BytesReturned) );
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Property query Status=%X",Status) ); goto exit; } ASSERT(BytesReturned == sizeof(pWdmaContext->VirtualMidiPinId)); } if(pWdmaContext->VirtualCDPinId == MAXULONG) { CreateVirtualSource.Property.Id = KSPROPERTY_SYSAUDIO_CREATE_VIRTUAL_SOURCE; CreateVirtualSource.PinCategory = KSNODETYPE_CD_PLAYER; CreateVirtualSource.PinName = KSNODETYPE_CD_PLAYER;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY %X",CreateVirtualSource) );
Status = KsSynchronousIoControlDevice( pWdmaContext->pFileObjectSysaudio, KernelMode, IOCTL_KS_PROPERTY, &CreateVirtualSource, sizeof(CreateVirtualSource), &pWdmaContext->VirtualCDPinId, sizeof(pWdmaContext->VirtualCDPinId), &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,BytesRet=%d", Status,BytesReturned) );
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Property query Status=%X",Status) ); goto exit; } ASSERT(BytesReturned == sizeof(pWdmaContext->VirtualCDPinId)); }
//
// Initialize the device add/remove ks event
//
if(!pWdmaContext->fInitializeSysaudio) {
Event.Set = KSEVENTSETID_Sysaudio; Event.Id = KSEVENT_SYSAUDIO_ADDREMOVE_DEVICE; Event.Flags = KSEVENT_TYPE_ENABLE;
pWdmaContext->EventData.NotificationType = KSEVENTF_KSWORKITEM; pWdmaContext->EventData.KsWorkItem.WorkQueueItem = &pWdmaContext->SysaudioWorkItem; pWdmaContext->EventData.KsWorkItem.KsWorkerObject = pWdmaContext->SysaudioWorkerObject; pWdmaContext->EventData.KsWorkItem.Reserved = 0;
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY Event=%X",Event) );
Status = KsSynchronousIoControlDevice( pWdmaContext->pFileObjectSysaudio, KernelMode, IOCTL_KS_ENABLE_EVENT, &Event, sizeof(Event), &pWdmaContext->EventData, sizeof(pWdmaContext->EventData), &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,BytesRet=%d", Status,BytesReturned) );
if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_SYSAUDIO,("Failed Property query Status=%X",Status) ); goto exit; } pWdmaContext->fInitializeSysaudio = TRUE; } exit: KeSetEvent(&pWdmaContext->InitializedSysaudioEvent, 0, FALSE); RETURN(Status); }
VOID UninitializeSysaudio( PWDMACONTEXT pWdmaContext ) { NTSTATUS Status = STATUS_INVALID_PARAMETER; ULONG BytesReturned;
PAGED_CODE(); DPF( DL_TRACE|FA_SYSAUDIO, ("Entering") ); if(pWdmaContext->pFileObjectSysaudio != NULL) { if(pWdmaContext->fInitializeSysaudio) { DPF( DL_TRACE|FA_SYSAUDIO,("KS_DISABLE_EVENT EventData=%X", pWdmaContext->EventData) );
Status = KsSynchronousIoControlDevice( pWdmaContext->pFileObjectSysaudio, KernelMode, IOCTL_KS_DISABLE_EVENT, &pWdmaContext->EventData, sizeof(pWdmaContext->EventData), NULL, 0, &BytesReturned);
DPF( DL_TRACE|FA_SYSAUDIO,("KS_PROPERTY results Status=%X,BytesRet=%d", Status,BytesReturned) );
pWdmaContext->VirtualWavePinId = MAXULONG; pWdmaContext->VirtualMidiPinId = MAXULONG; pWdmaContext->VirtualCDPinId = MAXULONG; pWdmaContext->fInitializeSysaudio = FALSE;
DPF( DL_TRACE|FA_SYSAUDIO,("Exiting %08x", Status)); } } }
NTSTATUS AddDevNode( PWDMACONTEXT pContext, PCWSTR DeviceInterfaceIn, UINT DeviceType ) { NTSTATUS Status=STATUS_SUCCESS; PDEVNODE_LIST_ITEM pDevNodeListItem = NULL; PLIST_ENTRY ple; ULONG t;
PAGED_CODE(); DPF( DL_TRACE|FA_SYSAUDIO,("%08x [%ls] %d", pContext, DeviceInterfaceIn, DeviceType));
for(ple = pContext->DevNodeListHead.Flink; ple != &pContext->DevNodeListHead; ple = ple->Flink) { pDevNodeListItem = CONTAINING_RECORD(ple, DEVNODE_LIST_ITEM, Next); if(!MyWcsicmp(pDevNodeListItem->DeviceInterface, DeviceInterfaceIn)) { ++pDevNodeListItem->cReference; DPF( DL_TRACE|FA_SYSAUDIO, ("cReference is now %d", pDevNodeListItem->cReference)); goto exit; } }
// Limit the number of devnodes that can be added
if (pContext->DevNodeListCount > MAXDEVNODES) { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit; }
pDevNodeListItem = NULL; Status = AudioAllocateMemory_Paged(sizeof(DEVNODE_LIST_ITEM), TAG_AudN_NODE, ZERO_FILL_MEMORY, &pDevNodeListItem); if(!NT_SUCCESS(Status)) { goto exit; } DPF( DL_TRACE|FA_SYSAUDIO, ("New pDevNodeListItem (%08x)", pDevNodeListItem));
Status = AudioAllocateMemory_Paged((wcslen(DeviceInterfaceIn)+1)*sizeof(WCHAR), TAG_AudD_DEVICEINFO, ZERO_FILL_MEMORY, &pDevNodeListItem->DeviceInterface); if (!NT_SUCCESS(Status)) { AudioFreeMemory(sizeof(DEVNODE_LIST_ITEM),&pDevNodeListItem); goto exit; }
wcscpy(pDevNodeListItem->DeviceInterface, DeviceInterfaceIn); pDevNodeListItem->cReference = 1; DPF( DL_TRACE|FA_SYSAUDIO, ("cReference is now 1"));
for(t = 0; t < MAX_DEVICE_CLASS; t++) { pDevNodeListItem->cDevices[t] = MAXULONG; pDevNodeListItem->fAdded[t] = FALSE; } InsertTailList(&pContext->DevNodeListHead, &pDevNodeListItem->Next); pContext->DevNodeListCount++; exit: if (pDevNodeListItem) { pDevNodeListItem->fAdded[DeviceType] = TRUE; Status=ProcessDevNodeListItem(pContext, pDevNodeListItem, DeviceType); }
RETURN( Status ); }
VOID RemoveDevNode( PWDMACONTEXT pWdmaContext, PCWSTR DeviceInterfaceIn, UINT DeviceType ) { PDEVNODE_LIST_ITEM pDevNodeListItem; PLIST_ENTRY ple, pleNext; PCOMMONDEVICE *papCommonDevice; ULONG d, j;
PAGED_CODE(); DPF( DL_TRACE|FA_SYSAUDIO, ("%08x %ls %d", pWdmaContext, DeviceInterfaceIn, DeviceType));
papCommonDevice = &pWdmaContext->apCommonDevice[DeviceType][0];
for(ple = pWdmaContext->DevNodeListHead.Flink; ple != &pWdmaContext->DevNodeListHead; ple = pleNext) { pleNext = ple->Flink; pDevNodeListItem = CONTAINING_RECORD(ple, DEVNODE_LIST_ITEM, Next); if(!MyWcsicmp(pDevNodeListItem->DeviceInterface, DeviceInterfaceIn)) {
for (d = 0; d < MAXNUMDEVS; d++) {
if(papCommonDevice[d]->Device == UNUSED_DEVICE || MyWcsicmp(papCommonDevice[d]->DeviceInterface, DeviceInterfaceIn)) { continue; }
if(papCommonDevice[d]->PreferredDevice == d) { ULONG p = MAXULONG;
for(j = 0; j < MAXNUMDEVS; j++) {
if(j == d) continue;
if(papCommonDevice[j]->Device == UNUSED_DEVICE) continue;
if(papCommonDevice[j]->PreferredDevice != d) continue;
if(p == MAXULONG) { p = j; } papCommonDevice[j]->PreferredDevice = p; } }
switch(DeviceType) { case WaveOutDevice: if ( pWdmaContext->WaveOutDevs[d].pTimer != NULL ) KeCancelTimer(pWdmaContext->WaveOutDevs[d].pTimer);
CleanupWavePins(&pWdmaContext->WaveOutDevs[d]);
AudioFreeMemory_Unknown(&pWdmaContext->WaveOutDevs[d].AudioDataRanges); AudioFreeMemory_Unknown(&pWdmaContext->WaveOutDevs[d].pTimer); AudioFreeMemory_Unknown(&pWdmaContext->WaveOutDevs[d].pDpc); break;
case WaveInDevice: CleanupWavePins(&pWdmaContext->WaveInDevs[d]);
AudioFreeMemory_Unknown(&pWdmaContext->WaveInDevs[d].AudioDataRanges); break;
case MidiOutDevice: CloseMidiDevicePin(&pWdmaContext->MidiOutDevs[d]); AudioFreeMemory_Unknown(&pWdmaContext->MidiOutDevs[d].MusicDataRanges); break;
case MidiInDevice: CloseMidiDevicePin(&pWdmaContext->MidiInDevs[d]); AudioFreeMemory_Unknown(&pWdmaContext->MidiInDevs[d].MusicDataRanges); break;
case MixerDevice: kmxlDeInit(&pWdmaContext->MixerDevs[d]);
break; }
AudioFreeMemory_Unknown(&papCommonDevice[d]->pwstrName); AudioFreeMemory_Unknown(&papCommonDevice[d]->DeviceInterface); AudioFreeMemory_Unknown(&papCommonDevice[d]->ComponentId);
papCommonDevice[d]->pwstrName = NULL; papCommonDevice[d]->DeviceInterface = NULL; papCommonDevice[d]->Device = UNUSED_DEVICE; } pDevNodeListItem->cDevices[DeviceType] = MAXULONG; pDevNodeListItem->fAdded[DeviceType] = FALSE; ASSERT(pDevNodeListItem->cReference > 0);
if(--pDevNodeListItem->cReference > 0) { DPF( DL_TRACE|FA_SYSAUDIO, ("cReference is now %d", pDevNodeListItem->cReference)); break; }
DPF( DL_TRACE|FA_SYSAUDIO, ("Freeing %08x", pDevNodeListItem)); RemoveEntryList(&pDevNodeListItem->Next); pWdmaContext->DevNodeListCount--; AudioFreeMemory_Unknown(&pDevNodeListItem->DeviceInterface); AudioFreeMemory_Unknown(&pDevNodeListItem); break; } } }
VOID SysaudioAddRemove( PWDMACONTEXT pContext ) { PDEVNODE_LIST_ITEM pDevNodeListItem; PLIST_ENTRY ple; int t;
PAGED_CODE(); DPF( DL_TRACE|FA_SYSAUDIO, ("Entering")); WdmaGrabMutex(pContext);
DPFASSERT(IsValidWdmaContext(pContext));
if(pContext->SysaudioWorkerObject != NULL) {
for(ple = pContext->DevNodeListHead.Flink; ple != &pContext->DevNodeListHead; ple = ple->Flink) {
pDevNodeListItem = CONTAINING_RECORD(ple, DEVNODE_LIST_ITEM, Next);
for(t = 0; t < MAX_DEVICE_CLASS; t++) { ProcessDevNodeListItem(pContext, pDevNodeListItem, t); } } } // Need this for to get more KS events
pContext->SysaudioWorkItem.List.Blink = NULL;
WdmaReleaseMutex(pContext); DPF(DL_TRACE|FA_SYSAUDIO, ("Exiting")); }
NTSTATUS ProcessDevNodeListItem ( PWDMACONTEXT pWdmaContext, PDEVNODE_LIST_ITEM pDevNodeListItem, ULONG DeviceType ) { NTSTATUS Status=STATUS_SUCCESS;
PAGED_CODE(); if(!pWdmaContext->fInitializeSysaudio) { RETURN( STATUS_UNSUCCESSFUL ); } if(!pDevNodeListItem->fAdded[DeviceType]) { ASSERT(pDevNodeListItem->cDevices[DeviceType] == MAXULONG); RETURN( Status ); } DPF( DL_TRACE|FA_SYSAUDIO, ("%ls[%d]", pDevNodeListItem->DeviceInterface, DeviceType));
Status=InitializeGetNumDevs( pWdmaContext, DeviceType, pDevNodeListItem->DeviceInterface, &pDevNodeListItem->cDevices[DeviceType]);
if (!NT_SUCCESS(Status)) { RETURN( Status ); }
if(DeviceType == MixerDevice && (pDevNodeListItem->fAdded[WaveOutDevice] || pDevNodeListItem->fAdded[WaveInDevice])) {
Status = kmxlInitializeMixer( pWdmaContext, pDevNodeListItem->DeviceInterface, pDevNodeListItem->cDevices[MixerDevice] );
if(NT_SUCCESS(Status) && pDevNodeListItem->cDevices[MixerDevice]) { if(pDevNodeListItem->fAdded[WaveOutDevice]) { FindVolumeControl(pWdmaContext, pDevNodeListItem->DeviceInterface, WaveOutDevice); } if(pDevNodeListItem->fAdded[MidiOutDevice]) { FindVolumeControl(pWdmaContext, pDevNodeListItem->DeviceInterface, MidiOutDevice); } } }
RETURN( Status ); }
#pragma LOCKED_CODE
NTSTATUS QueueWorkList ( PWDMACONTEXT pContext, VOID (*Function)( PVOID Reference1, PVOID Reference2 ), PVOID Reference1, PVOID Reference2 ) { NTSTATUS Status = STATUS_SUCCESS; PWORK_LIST_ITEM pWorkListItem = NULL;
if(pContext->WorkListWorkerObject == NULL) { ASSERT(NT_SUCCESS(Status)); goto exit; }
Status = AudioAllocateMemory_Fixed(sizeof(WORK_LIST_ITEM), TAG_AudE_EVENT, ZERO_FILL_MEMORY, &pWorkListItem); if(!NT_SUCCESS(Status)) { DPF( DL_TRACE|FA_SYSAUDIO, ("Failing QueueWorkList: %08x", Status)); goto exit; }
pWorkListItem->Reference1 = Reference1; pWorkListItem->Reference2 = Reference2; pWorkListItem->Function = Function;
ExInterlockedInsertTailList(&pContext->WorkListHead, &pWorkListItem->Next, &pContext->WorkListSpinLock);
if(InterlockedIncrement(&pContext->cPendingWorkList) == 1) { KsQueueWorkItem(pContext->WorkListWorkerObject, &pContext->WorkListWorkItem); } exit: RETURN( Status ); }
VOID WorkListWorker( PVOID pReference ) { PWDMACONTEXT pContext = (PWDMACONTEXT)pReference; PWORK_LIST_ITEM pWorkListItem; PLIST_ENTRY ple;
ASSERT(pContext);
WdmaGrabMutex(pContext);
while((ple = ExInterlockedRemoveHeadList( &pContext->WorkListHead, &pContext->WorkListSpinLock)) != NULL) {
pWorkListItem = CONTAINING_RECORD(ple, WORK_LIST_ITEM, Next);
(*pWorkListItem->Function)(pWorkListItem->Reference1,pWorkListItem->Reference2);
AudioFreeMemory(sizeof(sizeof(WORK_LIST_ITEM)),&pWorkListItem);
if(InterlockedDecrement(&pContext->cPendingWorkList) == 0) { break; } }
WdmaReleaseMutex(pContext); }
VOID WdmaGrabMutex( PWDMACONTEXT pWdmaContext ) { // KeWaitForMutexObject(&pWdmaContext->wdmaContextMutex, Executive, KernelMode, FALSE, NULL);
//
// Turn off the APCDisable flag in the thread structure before going for our
// mutex. This will prevent us from getting suspeneded while holding this
// mutex.
//
KeEnterCriticalRegion(); KeWaitForMutexObject(&wdmaMutex, Executive, KernelMode, FALSE, NULL); }
VOID WdmaReleaseMutex( PWDMACONTEXT pWdmaContext ) { // KeReleaseMutex(&pWdmaContext->wdmaContextMutex, FALSE);
KeReleaseMutex(&wdmaMutex, FALSE); KeLeaveCriticalRegion(); }
VOID WdmaContextCleanup(PWDMACONTEXT pWdmaContext) { LONG DeviceType; LONG DeviceNumber; PDEVNODE_LIST_ITEM pDevNodeListItem = NULL; PLIST_ENTRY ple;
DPF( DL_TRACE|FA_SYSAUDIO, ("%08x", pWdmaContext));
for (DeviceType = 0; DeviceType < MAX_DEVICE_CLASS; DeviceType++) { for (DeviceNumber = 0; DeviceNumber < MAXNUMDEVS; DeviceNumber++) { PCOMMONDEVICE pDevice;
pDevice = pWdmaContext->apCommonDevice[DeviceType][DeviceNumber];
ASSERT(pDevice); if (UNUSED_DEVICE != pDevice->Device) { LPWSTR DeviceInterface = NULL; NTSTATUS Status;
ASSERT(pDevice->DeviceInterface);
if (pDevice->DeviceInterface) { Status = AudioAllocateMemory_Paged((wcslen(pDevice->DeviceInterface)+1)*sizeof(WCHAR), TAG_AudD_DEVICEINFO, DEFAULT_MEMORY, &DeviceInterface); if (NT_SUCCESS(Status)) { wcscpy( DeviceInterface, pDevice->DeviceInterface ); RemoveDevNode(pWdmaContext, DeviceInterface, DeviceType); AudioFreeMemory_Unknown(&DeviceInterface); } } } } }
//
// Cleanup any remaining devnode list items
//
while (!IsListEmpty(&pWdmaContext->DevNodeListHead)) { ple = pWdmaContext->DevNodeListHead.Flink; pDevNodeListItem = CONTAINING_RECORD(ple, DEVNODE_LIST_ITEM, Next); DPF( DL_TRACE|FA_SYSAUDIO, ("Stray devnode list item = %08x", pDevNodeListItem)); RemoveHeadList(&pWdmaContext->DevNodeListHead); pWdmaContext->DevNodeListCount--; AudioFreeMemory_Unknown(&pDevNodeListItem->DeviceInterface); AudioFreeMemory_Unknown(&pDevNodeListItem); }
return; }
|