windows-nt/Source/XPSP1/NT/multimedia/media/mmdrv/drvutil.c
2020-09-26 16:20:57 +08:00

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;
}