/**************************************************************************** * * drvutil.c * * Multimedia kernel driver support component (mmdrv) * * Copyright (c) Microsoft Corporation 1992 - 1995. All rights reserved. * * Support functions common to multiple multi-media drivers : * * -- Open a device * -- Translate a kernel IO return code to a multi-media return code * -- Count the number of devices of a given type * -- Set and Get data synchronously to a kernel device * * History * 01-Feb-1992 - Robin Speed (RobinSp) wrote it * 04-Feb-1992 - Reviewed by SteveDav * ***************************************************************************/ #include "mmdrv.h" #include #include #include /**************************************************************************** * @doc INTERNAL * * @api MMRESULT | sndOpenDev | 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 * * @parm PHANDLE | phDev | Where to return the kernel device handle * * @parm ACCESS_MASK | Access | The desired access * * @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 sndOpenDev(UINT DeviceType, DWORD dwId, PHANDLE phDev, DWORD Access) { WCHAR cDev[SOUND_MAX_DEVICE_NAME]; WinAssert(DeviceType == WaveOutDevice || DeviceType == WaveInDevice || DeviceType == MidiOutDevice || DeviceType == MidiInDevice || DeviceType == AuxDevice); *phDev = INVALID_HANDLE_VALUE; // Always initialise the return value // // Check it's not out of range // if (dwId > SOUND_MAX_DEVICES) { return MMSYSERR_BADDEVICEID; } // // Create the device name and open it - remove '\Device' // wsprintf(cDev, L"\\\\.%ls%d", (DeviceType == WaveOutDevice ? DD_WAVE_OUT_DEVICE_NAME_U : DeviceType == WaveInDevice ? DD_WAVE_IN_DEVICE_NAME_U : DeviceType == MidiOutDevice ? DD_MIDI_OUT_DEVICE_NAME_U : DeviceType == MidiInDevice ? DD_MIDI_IN_DEVICE_NAME_U : DD_AUX_DEVICE_NAME_U) + strlen("\\Device"), dwId); *phDev = CreateFile(cDev, Access, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, Access != GENERIC_READ ? FILE_FLAG_OVERLAPPED : 0, NULL); // // Check up on the driver for refusing to open // multiply for read // WinAssert(!(GetLastError() == ERROR_ACCESS_DENIED && Access == GENERIC_READ)); // // Return status to caller // return *phDev != INVALID_HANDLE_VALUE ? MMSYSERR_NOERROR : sndTranslateStatus(); } /**************************************************************************** * @doc INTERNAL * * @api void | sndTranslateStatus | This function translates an NT status * code into a multi-media error code as far as possible. * * @parm NTSTATUS | Status | The NT base operating system return status. * * * @rdesc The multi-media error code. ***************************************************************************/ DWORD sndTranslateStatus(void) { #if DBG UINT n; switch (n=GetLastError()) { #else switch (GetLastError()) { #endif case NO_ERROR: case ERROR_IO_PENDING: return MMSYSERR_NOERROR; case ERROR_BUSY: return MMSYSERR_ALLOCATED; case ERROR_NOT_SUPPORTED: case ERROR_INVALID_FUNCTION: return MMSYSERR_NOTSUPPORTED; case ERROR_NOT_ENOUGH_MEMORY: return MMSYSERR_NOMEM; case ERROR_ACCESS_DENIED: return MMSYSERR_BADDEVICEID; case ERROR_INSUFFICIENT_BUFFER: return MMSYSERR_INVALPARAM; default: dprintf2(("sndTranslateStatus: LastError = %d", n)); return MMSYSERR_ERROR; } } /**************************************************************************** * @doc INTERNAL * * @api DWORD | sndGetNumDevs | This function returns the number of (kernel) * * @parm UINT | DeviceType | The Device type * * @rdesc The number of devices. ***************************************************************************/ DWORD sndGetNumDevs(UINT DeviceType) { // // Look for devices until we don't find one // int i; HANDLE h; for (i=0; sndOpenDev(DeviceType, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) { // // BUGBUG try something to make sure ! // CloseHandle(h); } // // We incremented i each time we found a good device // return i; } /**************************************************************************** * @doc INTERNAL * * @api DWORD | sndSetData | This function sets the volume given a device id * and could be used for other soft value setting * when only read access is required to the device * * @parm UINT | DeviceType | The Device type * * @parm UINT | DeviceId | The device id * * @parm UINT | Length | Length of data to set * * @parm PBYTE | Data | Data to set * * @parm ULONG | Ioctl | The Ioctl to use * * @rdesc MM... return code. ***************************************************************************/ MMRESULT sndSetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data, ULONG Ioctl) { HANDLE hDev; MMRESULT mRet; DWORD BytesReturned; // // Open the wave output device // mRet = sndOpenDev(DeviceType, DeviceId, &hDev, GENERIC_READ); if (mRet != MMSYSERR_NOERROR) { return mRet; } // // Set our data. // // Setting the overlapped parameter (last) to null means we // wait until the operation completes. // mRet = DeviceIoControl(hDev, Ioctl, Data, Length, NULL, 0, &BytesReturned, NULL) ? MMSYSERR_NOERROR : sndTranslateStatus(); // // Close our file and return // CloseHandle(hDev); return mRet; } /**************************************************************************** * @doc INTERNAL * * @api DWORD | sndGetData | This function gets data from a given device * specified by device id when read-only access is * sufficient * * @parm UINT | DeviceType | The Device type * * @parm UINT | DeviceId | The device id * * @parm UINT | Length | Length of data to set * * @parm PBYTE | Data | Data to set * * @parm ULONG | Ioctl | The Ioctl to use * * @rdesc MM... return code. ***************************************************************************/ MMRESULT sndGetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data, ULONG Ioctl) { HANDLE hDev; MMRESULT mRet; DWORD BytesReturned; // // Open the wave output device // mRet = sndOpenDev(DeviceType, DeviceId, &hDev, GENERIC_READ); if (mRet != MMSYSERR_NOERROR) { return mRet; } // // Set our data. // // Setting the overlapped parameter (last) to null means we // wait until the operation completes. // mRet = DeviceIoControl(hDev, Ioctl, NULL, 0, (LPVOID)Data, Length, &BytesReturned, NULL) ? MMSYSERR_NOERROR : sndTranslateStatus(); // // Close our file and return // CloseHandle(hDev); return mRet; } /**************************************************************************** * @doc INTERNAL * * @api MMRESULT | sndGetHandleData | Get data from a device using its handle * * @parm PWAVEALLOC | pClient | Client handle. * * @parm DWORD | dwSize | Size of the data * * @parm PVOID | pData | Where to put the data. * * @parm ULONG | Function | The Ioctl to use * * @rdesc MMSYS... return value. ***************************************************************************/ MMRESULT sndGetHandleData(HANDLE hDev, DWORD dwSize, PVOID pData, ULONG Ioctl, HANDLE hEvent) { OVERLAPPED Overlap; DWORD BytesReturned; WinAssert(hDev != NULL); memset(&Overlap, 0, sizeof(Overlap)); Overlap.hEvent = hEvent; // // Issue the IO control. We must wait with our own event because // setting the overlapped object to null will complete if other // IOs complete. // if (!DeviceIoControl(hDev, Ioctl, NULL, 0, pData, dwSize, &BytesReturned, &Overlap)) { DWORD cbTransfer; // // Wait for completion if necessary // if (GetLastError() == ERROR_IO_PENDING) { if (!GetOverlappedResult(hDev, &Overlap, &cbTransfer, TRUE)) { return sndTranslateStatus(); } } else { return sndTranslateStatus(); } } // // We'd better peek aleratbly to flush out any IO // completions so that things like RESET only // return when all buffers have been completed // // This relies on the fact that SleepEx will return // WAIT_IO_COMPLETION in preference to OK // while (SetEvent(hEvent) && WaitForSingleObjectEx(hEvent, 0, TRUE) == WAIT_IO_COMPLETION) {} return MMSYSERR_NOERROR; } /**************************************************************************** * @doc INTERNAL * * @api MMRESULT | sndSetHandleData | Pass data to a device using its handle * * @parm PWAVEALLOC | pClient | Client handle. * * @parm DWORD | dwSize | Size of the data * * @parm PVOID | pData | Data to send. * * @parm ULONG | Function | The Ioctl to use * * @rdesc MMSYS... return value. ***************************************************************************/ MMRESULT sndSetHandleData(HANDLE hDev, DWORD dwSize, PVOID pData, ULONG Ioctl, HANDLE hEvent) { OVERLAPPED Overlap; DWORD BytesReturned; WinAssert(hDev != NULL); memset((PVOID)&Overlap, 0, sizeof(Overlap)); Overlap.hEvent = hEvent; // // Issue the IO control. We must wait with our own event because // setting the overlapped object to null will complete if other // IOs complete. // if (!DeviceIoControl(hDev, Ioctl, pData, dwSize, NULL, 0, &BytesReturned, &Overlap)) { DWORD cbTransfer; // // Wait for completion if necessary // if (GetLastError() == ERROR_IO_PENDING) { if (!GetOverlappedResult(hDev, &Overlap, &cbTransfer, TRUE)) { return sndTranslateStatus(); } } else { return sndTranslateStatus(); } } // // We'd better peek alertably to flush out any IO // completions so that things like RESET only // return when all buffers have been completed // // This relies on the fact that SleepEx will return // WAIT_IO_COMPLETION in preference to OK // while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {} return MMSYSERR_NOERROR; }