mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
616 lines
17 KiB
616 lines
17 KiB
/****************************************************************************
|
|
*
|
|
* wdmaud.c
|
|
*
|
|
* WDM Audio mapper
|
|
*
|
|
* Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
|
|
*
|
|
* History
|
|
* 5-12-97 - Noel Cross (NoelC)
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include <stdarg.h>
|
|
#include "wdmdrv.h"
|
|
|
|
LPDEVICEINFO pWaveDeviceList = NULL;
|
|
LPDEVICEINFO pMidiDeviceList = NULL;
|
|
|
|
#ifdef DEBUG
|
|
INT giAllocs=0;
|
|
INT giFrees=0;
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------
|
|
// LPDEVICEINFO GlobalAllocDeviceInfo
|
|
//
|
|
// Note: when allocating DeviceInfo structure, we know that the structure's
|
|
// definition includes one character for the DeviceInterface, so we only need
|
|
// to allocate additional length for the string but not its NULL terminator
|
|
//--------------------------------------------------------------------------
|
|
LPDEVICEINFO GlobalAllocDeviceInfo(LPCWSTR DeviceInterface)
|
|
{
|
|
LPDEVICEINFO DeviceInfo;
|
|
|
|
IsValidDeviceInterface(DeviceInterface);
|
|
|
|
DeviceInfo = GlobalAllocPtr(GPTR, sizeof(*DeviceInfo)+(sizeof(WCHAR)*lstrlenW(DeviceInterface)));
|
|
if (DeviceInfo) {
|
|
lstrcpyW(DeviceInfo->wstrDeviceInterface, DeviceInterface);
|
|
#ifdef DEBUG
|
|
DeviceInfo->dwSig=DEVICEINFO_SIGNATURE;
|
|
#endif
|
|
}
|
|
DPF(DL_TRACE|FA_ALL,("Allocated DI=%08X, giAllocs=%d, giFrees=%d",
|
|
DeviceInfo,++giAllocs,giFrees) );
|
|
|
|
return DeviceInfo;
|
|
}
|
|
|
|
VOID GlobalFreeDeviceInfo(LPDEVICEINFO lpdi)
|
|
{
|
|
//
|
|
// Now free the deviceinfo structure.
|
|
//
|
|
if( lpdi )
|
|
{
|
|
#ifdef DEBUG
|
|
giFrees++;
|
|
// remove the signature from the block.
|
|
lpdi->dwSig=0;
|
|
#endif
|
|
GlobalFreePtr( lpdi );
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | wdmaudOpenDev | Open the kernel driver device corresponding
|
|
* to a logical wave device id
|
|
*
|
|
* @parm UINT | DeviceType | The type of device
|
|
*
|
|
* @parm DWORD | dwId | The device id
|
|
*
|
|
* @comm For our sound devices the only relevant access are read and
|
|
* read/write. Device should ALWAYS allow opens for read unless some
|
|
* resource or access-rights restriction occurs.
|
|
***************************************************************************/
|
|
MMRESULT wdmaudOpenDev
|
|
(
|
|
LPDEVICEINFO DeviceInfo,
|
|
LPWAVEFORMATEX lpWaveFormat
|
|
)
|
|
{
|
|
MMRESULT mmr;
|
|
UINT cbSize;
|
|
|
|
DPFASSERT(DeviceInfo->DeviceType == WaveOutDevice ||
|
|
DeviceInfo->DeviceType == WaveInDevice ||
|
|
DeviceInfo->DeviceType == MidiOutDevice ||
|
|
DeviceInfo->DeviceType == MidiInDevice ||
|
|
DeviceInfo->DeviceType == MixerDevice);
|
|
|
|
//
|
|
// Check it's not out of range
|
|
//
|
|
if (DeviceInfo->DeviceNumber > WDMAUD_MAX_DEVICES)
|
|
{
|
|
MMRRETURN( MMSYSERR_BADDEVICEID );
|
|
}
|
|
|
|
|
|
if (NULL != lpWaveFormat)
|
|
{
|
|
if (WAVE_FORMAT_PCM == lpWaveFormat->wFormatTag)
|
|
{
|
|
cbSize = sizeof(PCMWAVEFORMAT);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// because MMSYSTEM does not (currently) validate for the extended
|
|
// format information, we validate this pointer
|
|
//
|
|
cbSize = sizeof(WAVEFORMATEX) + lpWaveFormat->cbSize;
|
|
if (IsBadReadPtr(lpWaveFormat, cbSize))
|
|
{
|
|
MMRRETURN( MMSYSERR_INVALPARAM );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store this for positional information
|
|
//
|
|
DeviceInfo->DeviceState->cSampleBits = lpWaveFormat->nChannels * lpWaveFormat->wBitsPerSample;
|
|
|
|
}
|
|
else
|
|
{
|
|
cbSize = 0L;
|
|
}
|
|
|
|
mmr = wdmaudIoControl(DeviceInfo,
|
|
cbSize,
|
|
lpWaveFormat,
|
|
IOCTL_WDMAUD_OPEN_PIN);
|
|
|
|
//
|
|
// Return status to caller
|
|
//
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | wdmaudCloseDev | Close the kernel driver device corresponding
|
|
* to a logical device id
|
|
*
|
|
* @parm UINT | DeviceType | The type of device
|
|
*
|
|
* @parm DWORD | dwId | The device id
|
|
*
|
|
* @comm For our sound devices the only relevant access are read and
|
|
* read/write. Device should ALWAYS allow opens for read unless some
|
|
* resource or access-rights restriction occurs.
|
|
***************************************************************************/
|
|
MMRESULT FAR wdmaudCloseDev
|
|
(
|
|
LPDEVICEINFO DeviceInfo
|
|
)
|
|
{
|
|
MMRESULT mmr;
|
|
|
|
DPFASSERT(DeviceInfo->DeviceType == WaveOutDevice ||
|
|
DeviceInfo->DeviceType == WaveInDevice ||
|
|
DeviceInfo->DeviceType == MidiOutDevice ||
|
|
DeviceInfo->DeviceType == MidiInDevice ||
|
|
DeviceInfo->DeviceType == MixerDevice);
|
|
|
|
//
|
|
// Check it's not out of range
|
|
//
|
|
if (DeviceInfo->DeviceNumber > WDMAUD_MAX_DEVICES)
|
|
{
|
|
MMRRETURN( MMSYSERR_BADDEVICEID );
|
|
}
|
|
|
|
if (WaveOutDevice == DeviceInfo->DeviceType ||
|
|
WaveInDevice == DeviceInfo->DeviceType)
|
|
{
|
|
if (DeviceInfo->DeviceState->lpWaveQueue)
|
|
{
|
|
return WAVERR_STILLPLAYING;
|
|
}
|
|
//
|
|
// Wait for the thread to be destroyed.
|
|
//
|
|
mmr = wdmaudDestroyCompletionThread(DeviceInfo);
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
}
|
|
else if (MidiInDevice == DeviceInfo->DeviceType)
|
|
{
|
|
if (DeviceInfo->DeviceState->lpMidiInQueue)
|
|
{
|
|
DPF(DL_WARNING|FA_MIDI,("Error closing midi device") );
|
|
return MIDIERR_STILLPLAYING;
|
|
}
|
|
|
|
InterlockedExchange( (LPLONG)&DeviceInfo->DeviceState->fExit, TRUE );
|
|
}
|
|
mmr = wdmaudIoControl(DeviceInfo,
|
|
0,
|
|
NULL,
|
|
IOCTL_WDMAUD_CLOSE_PIN);
|
|
|
|
//
|
|
// Return status to caller
|
|
//
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api DWORD | wdmaudGetNumDevs | This function returns the number of (kernel)
|
|
*
|
|
* @parm UINT | DeviceType | The Device type
|
|
*
|
|
* @parm LPCWSTR | DeviceInterface | Pointer to a buffer containing the
|
|
* device interface name of the SysAudio device for which we should
|
|
* obtain the count of device of the type DeviceType
|
|
*
|
|
* @rdesc The number of devices.
|
|
***************************************************************************/
|
|
|
|
DWORD FAR wdmaudGetNumDevs
|
|
(
|
|
UINT DeviceType,
|
|
LPCWSTR DeviceInterface
|
|
)
|
|
{
|
|
LPDEVICEINFO DeviceInfo;
|
|
DWORD NumDevs;
|
|
MMRESULT mmr;
|
|
|
|
DPFASSERT(DeviceType == WaveOutDevice ||
|
|
DeviceType == WaveInDevice ||
|
|
DeviceType == MidiOutDevice ||
|
|
DeviceType == MidiInDevice ||
|
|
DeviceType == MixerDevice ||
|
|
DeviceType == AuxDevice);
|
|
|
|
DeviceInfo = GlobalAllocDeviceInfo(DeviceInterface);
|
|
if (NULL == DeviceInfo)
|
|
{
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
//
|
|
// Call wdmaud.sys to get the number of devices for each
|
|
// type of function.
|
|
//
|
|
DeviceInfo->DeviceType = DeviceType;
|
|
|
|
//
|
|
// Make sure that we don't take the critical section
|
|
// in wdmaudIoControl (NT only)
|
|
//
|
|
DeviceInfo->OpenDone = 0;
|
|
|
|
mmr = wdmaudIoControl(DeviceInfo,
|
|
0L,
|
|
NULL,
|
|
IOCTL_WDMAUD_GET_NUM_DEVS);
|
|
#ifdef DEBUG
|
|
if( mmr != MMSYSERR_NOERROR)
|
|
DPF(DL_WARNING|FA_DEVICEIO, (szReturningErrorStr,mmr,MsgToAscii(mmr)) );
|
|
#endif
|
|
NumDevs = DeviceInfo->DeviceNumber;
|
|
GlobalFreeDeviceInfo( DeviceInfo );
|
|
|
|
//
|
|
// DeviceNumber is overloaded so we don't have to map
|
|
// an address into kernel mode
|
|
//
|
|
|
|
return MAKELONG(NumDevs, mmr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api DWORD | wdmaudDrvExit | This function indicates DevNode removal
|
|
*
|
|
* @parm UINT | DeviceType | The Device type
|
|
*
|
|
* @parm LPCWSTR | DeviceInterface | Pointer to a buffer containing the
|
|
* device interface name of the SysAudio device that we are adding
|
|
* or removing
|
|
*
|
|
* @rdesc The number of devices.
|
|
***************************************************************************/
|
|
|
|
DWORD FAR wdmaudAddRemoveDevNode
|
|
(
|
|
UINT DeviceType,
|
|
LPCWSTR DeviceInterface,
|
|
BOOL fAdd
|
|
)
|
|
{
|
|
LPDEVICEINFO DeviceInfo;
|
|
MMRESULT mmr;
|
|
|
|
DPFASSERT(DeviceType == WaveOutDevice ||
|
|
DeviceType == WaveInDevice ||
|
|
DeviceType == MidiOutDevice ||
|
|
DeviceType == MidiInDevice ||
|
|
DeviceType == MixerDevice ||
|
|
DeviceType == AuxDevice);
|
|
|
|
DeviceInfo = GlobalAllocDeviceInfo(DeviceInterface);
|
|
if (NULL == DeviceInfo)
|
|
{
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
//
|
|
// Call wdmaud.sys to get the number of devices for each
|
|
// type of function.
|
|
//
|
|
DeviceInfo->DeviceType = DeviceType;
|
|
mmr = wdmaudIoControl(DeviceInfo,
|
|
0L,
|
|
NULL,
|
|
fAdd ?
|
|
IOCTL_WDMAUD_ADD_DEVNODE :
|
|
IOCTL_WDMAUD_REMOVE_DEVNODE);
|
|
|
|
GlobalFreeDeviceInfo( DeviceInfo );
|
|
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api DWORD | wdmaudSetPreferredDevice | sets the preferred evice
|
|
*
|
|
* @parm UINT | DeviceType | The Device type
|
|
*
|
|
* @parm LPCWSTR | DeviceInterface | Pointer to a buffer containing the
|
|
* device interface name of the SysAudio device that we are adding
|
|
* or removing
|
|
*
|
|
* @rdesc The number of devices.
|
|
***************************************************************************/
|
|
|
|
DWORD FAR wdmaudSetPreferredDevice
|
|
(
|
|
UINT DeviceType,
|
|
UINT DeviceNumber,
|
|
DWORD_PTR dwParam1,
|
|
DWORD_PTR dwParam2
|
|
)
|
|
{
|
|
LPDEVICEINFO DeviceInfo;
|
|
MMRESULT mmr;
|
|
|
|
DPFASSERT(DeviceType == WaveOutDevice ||
|
|
DeviceType == WaveInDevice ||
|
|
DeviceType == MidiOutDevice ||
|
|
DeviceType == MidiInDevice ||
|
|
DeviceType == MixerDevice ||
|
|
DeviceType == AuxDevice);
|
|
|
|
DeviceInfo = GlobalAllocDeviceInfo((LPCWSTR)dwParam2);
|
|
if (NULL == DeviceInfo)
|
|
{
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
DeviceInfo->DeviceType = DeviceType;
|
|
DeviceInfo->DeviceNumber = DeviceNumber;
|
|
DeviceInfo->dwFlags = (DWORD) dwParam1;
|
|
|
|
mmr = wdmaudIoControl(DeviceInfo,
|
|
0L,
|
|
NULL,
|
|
IOCTL_WDMAUD_SET_PREFERRED_DEVICE);
|
|
GlobalFreeDeviceInfo( DeviceInfo );
|
|
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | wdmaudSetDeviceState |
|
|
*
|
|
* @parm DWORD | DeviceType | The Device type
|
|
*
|
|
* @parm ULONG | State | The state to set the device to
|
|
*
|
|
* @rdesc MMSYS.. return code
|
|
***************************************************************************/
|
|
MMRESULT wdmaudSetDeviceState
|
|
(
|
|
LPDEVICEINFO DeviceInfo,
|
|
ULONG State
|
|
)
|
|
{
|
|
MMRESULT mmr;
|
|
ULONG BufferCount;
|
|
|
|
if( ( (mmr=IsValidDeviceInfo(DeviceInfo)) != MMSYSERR_NOERROR ) ||
|
|
( (mmr=IsValidDeviceState(DeviceInfo->DeviceState,FALSE)) != MMSYSERR_NOERROR ) )
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
if (IOCTL_WDMAUD_WAVE_OUT_PLAY == State ||
|
|
IOCTL_WDMAUD_WAVE_IN_RECORD == State ||
|
|
IOCTL_WDMAUD_MIDI_IN_RECORD == State )
|
|
{
|
|
//
|
|
// We need to create a thread here on NT because we need
|
|
// to get notified when our IO requests complete. This
|
|
// requires another thread of execution to be able to
|
|
// process the completed IO.
|
|
//
|
|
mmr = wdmaudCreateCompletionThread ( DeviceInfo );
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
DeviceInfo->DeviceState->fRunning = TRUE;
|
|
|
|
IsValidDeviceState(DeviceInfo->DeviceState,TRUE);
|
|
}
|
|
|
|
if (IOCTL_WDMAUD_MIDI_IN_RESET == State ||
|
|
IOCTL_WDMAUD_MIDI_IN_STOP == State)
|
|
{
|
|
CRITENTER;
|
|
if (DeviceInfo->DeviceState->fRunning)
|
|
{
|
|
DeviceInfo->DeviceState->fRunning = FALSE;
|
|
CRITLEAVE;
|
|
}
|
|
else
|
|
{
|
|
CRITLEAVE;
|
|
if (IOCTL_WDMAUD_MIDI_IN_RESET == State)
|
|
{
|
|
return( wdmaudFreeMidiQ( DeviceInfo ) );
|
|
}
|
|
else
|
|
{
|
|
MMRRETURN( MMSYSERR_NOERROR );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the device to set the state. Note that some calls will wait in
|
|
// kernel mode for events to complete. Thus, this thread may be pre-empted
|
|
// and the waveThread or midThread routines will completely finish and unload
|
|
// by the time we come back. Thus, the calls to wdmaudDestroyCompletionThread
|
|
// will be no-ops.
|
|
//
|
|
DPF(DL_TRACE|FA_SYNC,("Setting state=%08X",State) );
|
|
mmr = wdmaudIoControl(DeviceInfo,
|
|
0,
|
|
NULL,
|
|
State);
|
|
DPF(DL_TRACE|FA_SYNC,("Done Setting state mmr=%08X",mmr) );
|
|
|
|
if (MMSYSERR_NOERROR == mmr)
|
|
{
|
|
if ((IOCTL_WDMAUD_WAVE_OUT_PAUSE == State) ||
|
|
(IOCTL_WDMAUD_WAVE_IN_STOP == State) ||
|
|
(IOCTL_WDMAUD_WAVE_IN_RESET == State) )
|
|
{
|
|
DeviceInfo->DeviceState->fPaused = TRUE;
|
|
}
|
|
|
|
if ((IOCTL_WDMAUD_WAVE_OUT_PLAY == State) ||
|
|
(IOCTL_WDMAUD_WAVE_OUT_RESET == State) ||
|
|
(IOCTL_WDMAUD_WAVE_IN_RECORD == State) )
|
|
{
|
|
DeviceInfo->DeviceState->fPaused = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPF(DL_WARNING|FA_ALL,("Error Setting State: mmr = %d", mmr ) );
|
|
}
|
|
|
|
if (IOCTL_WDMAUD_WAVE_OUT_RESET == State ||
|
|
IOCTL_WDMAUD_WAVE_IN_RESET == State)
|
|
{
|
|
DeviceInfo->DeviceState->fRunning = FALSE;
|
|
|
|
//
|
|
// Wait for all of the pending IO to come back from the
|
|
// reset operation.
|
|
//
|
|
mmr = wdmaudDestroyCompletionThread ( DeviceInfo );
|
|
}
|
|
|
|
if (IOCTL_WDMAUD_MIDI_IN_RESET == State)
|
|
{
|
|
mmr = wdmaudDestroyCompletionThread ( DeviceInfo );
|
|
if (MMSYSERR_NOERROR == mmr)
|
|
{
|
|
mmr = wdmaudFreeMidiQ( DeviceInfo );
|
|
|
|
for (BufferCount = 0; BufferCount < STREAM_BUFFERS; BufferCount++)
|
|
{
|
|
wdmaudGetMidiData( DeviceInfo, NULL );
|
|
}
|
|
}
|
|
}
|
|
else if (IOCTL_WDMAUD_MIDI_IN_STOP == State)
|
|
{
|
|
mmr = wdmaudDestroyCompletionThread ( DeviceInfo );
|
|
if (MMSYSERR_NOERROR == mmr)
|
|
{
|
|
for (BufferCount = 0; BufferCount < STREAM_BUFFERS; BufferCount++)
|
|
{
|
|
wdmaudGetMidiData( DeviceInfo, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | wdmaudGetPos |
|
|
*
|
|
* @parm DWORD | DeviceInfo | The Device instance structure
|
|
*
|
|
* @parm ULONG | | The state to set the device to
|
|
*
|
|
* @parm ULONG | State | The state to set the device to
|
|
*
|
|
* @rdesc MMSYS.. return code
|
|
***************************************************************************/
|
|
MMRESULT wdmaudGetPos
|
|
(
|
|
LPDEVICEINFO pClient,
|
|
LPMMTIME lpmmt,
|
|
DWORD dwSize,
|
|
UINT DeviceType
|
|
)
|
|
{
|
|
DWORD dwPos;
|
|
MMRESULT mmr;
|
|
LPDEVICEINFO DeviceInfo;
|
|
|
|
if (dwSize < sizeof(MMTIME))
|
|
MMRRETURN( MMSYSERR_ERROR );
|
|
|
|
DeviceInfo = GlobalAllocDeviceInfo(pClient->wstrDeviceInterface);
|
|
if (NULL == DeviceInfo)
|
|
{
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
//
|
|
// Call wdmaud.sys to get the number of devices for each
|
|
// type of function.
|
|
//
|
|
DeviceInfo->DeviceType = pClient->DeviceType;
|
|
DeviceInfo->DeviceNumber = pClient->DeviceNumber;
|
|
DeviceInfo->DeviceHandle = pClient->DeviceHandle;
|
|
DeviceInfo->OpenDone = 0;
|
|
|
|
//
|
|
// Get the current position from the driver
|
|
//
|
|
mmr = wdmaudIoControl(DeviceInfo,
|
|
sizeof(DWORD),
|
|
(LPBYTE)&dwPos,
|
|
DeviceType == WaveOutDevice ?
|
|
IOCTL_WDMAUD_WAVE_OUT_GET_POS :
|
|
IOCTL_WDMAUD_WAVE_IN_GET_POS);
|
|
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
//
|
|
// dwPos is in bytes
|
|
//
|
|
if (lpmmt->wType == TIME_BYTES)
|
|
{
|
|
lpmmt->u.cb = dwPos;
|
|
}
|
|
else
|
|
{
|
|
lpmmt->wType = TIME_SAMPLES;
|
|
if (pClient->DeviceState->cSampleBits != 0)
|
|
{
|
|
lpmmt->u.sample = (dwPos * 8) / pClient->DeviceState->cSampleBits;
|
|
}
|
|
else
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
GlobalFreeDeviceInfo( DeviceInfo );
|
|
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
|