/**************************************************************************** * * wdmaud.c * * WDM Audio mapper * * Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved. * * History * 5-12-97 - Noel Cross (NoelC) * ***************************************************************************/ #include #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 ); }