4079 lines
123 KiB
C
4079 lines
123 KiB
C
/****************************************************************************
|
|
*
|
|
* 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;
|
|
}
|