449 lines
12 KiB
C
449 lines
12 KiB
C
|
/****************************************************************************
|
||
|
*
|
||
|
* drvutil.c
|
||
|
*
|
||
|
* Multimedia kernel driver support component (mmdrv)
|
||
|
*
|
||
|
* Copyright (c) 1992-1995 Microsoft Corporation
|
||
|
*
|
||
|
* 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 <ntddwave.h>
|
||
|
#include <ntddmidi.h>
|
||
|
#include <ntddaux.h>
|
||
|
|
||
|
/****************************************************************************
|
||
|
* @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++) {
|
||
|
|
||
|
//
|
||
|
// Possible future work - try calling an actual function to make sure
|
||
|
// this worked.
|
||
|
//
|
||
|
|
||
|
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;
|
||
|
}
|