windows-nt/Source/XPSP1/NT/drivers/wdm/audio/legacy/wdmaud.drv/wdmaud.c

617 lines
17 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************
*
* 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 );
}