972 lines
24 KiB
C
972 lines
24 KiB
C
/****************************************************************************
|
|
*
|
|
* drvutil.c
|
|
*
|
|
* Multimedia kernel driver support component (drvlib)
|
|
*
|
|
* Copyright (c) 1993-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 <drvlib.h>
|
|
#include <ntddwave.h>
|
|
#include <ntddmidi.h>
|
|
#include <ntddaux.h>
|
|
#include <registry.h>
|
|
|
|
/*
|
|
** External name of driver
|
|
*/
|
|
|
|
extern TCHAR DriverName[];
|
|
|
|
/*
|
|
** We need an hInstance module handle to get strings from since the kernel
|
|
** driver returns string Ids in its structures
|
|
*/
|
|
|
|
extern HINSTANCE hInstance;
|
|
|
|
/*
|
|
** Internal structures
|
|
*/
|
|
|
|
/*
|
|
** List of cached device descriptions. This is generated when the
|
|
** driver is loaded from the data in the registry so that multiple
|
|
** queries are faster and consistent.
|
|
*/
|
|
|
|
typedef struct _DEVICE_LIST {
|
|
struct _DEVICE_LIST *Next; // Next pointer
|
|
DWORD DeviceType; // Type of device
|
|
ULONG CardIndex; // Card entry registry key index
|
|
PVOID DeviceInstanceData; // Instance data for this specific device
|
|
// if needed.
|
|
ULONG DeviceInstanceDataSize;
|
|
WCHAR Name[1]; // Device name
|
|
} DEVICE_LIST, *PDEVICE_LIST;
|
|
|
|
PDEVICE_LIST DeviceList;
|
|
|
|
/*
|
|
** Internal routines
|
|
*/
|
|
|
|
PDEVICE_LIST sndFindDevice(DWORD DeviceType, DWORD dwId);
|
|
BOOL sndProcessDevices(HKEY DevicesKey, PDEVICE_LIST *DeviceList, DWORD CardIndex);
|
|
BOOL sndAppendToDeviceList(PDEVICE_LIST *pList, DWORD DeviceType, DWORD CardIndex,
|
|
LPWSTR Name);
|
|
VOID sndFreeDeviceList(VOID);
|
|
BOOL sndEnumKey(HKEY Key, DWORD Index, LPTSTR OurKey, HKEY *pSubkey);
|
|
|
|
/****************************************************************************
|
|
* @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)
|
|
{
|
|
PDEVICE_LIST List;
|
|
WCHAR cDev[SOUND_MAX_DEVICE_NAME + 4];
|
|
|
|
List = sndFindDevice(DeviceType, dwId);
|
|
|
|
*phDev = INVALID_HANDLE_VALUE; // Always initialise the return value
|
|
|
|
if (List == NULL) {
|
|
return MMSYSERR_BADDEVICEID;
|
|
}
|
|
|
|
//
|
|
// Create the device name and open it - remove '\Device'
|
|
//
|
|
|
|
wsprintf(cDev, L"\\\\.\\%ls", List->Name);
|
|
|
|
*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)
|
|
{
|
|
//
|
|
// Search the device list for devices of our type
|
|
//
|
|
int i;
|
|
PDEVICE_LIST List;
|
|
|
|
for (List = DeviceList, i= 0; List != NULL; List = List->Next) {
|
|
if (List->DeviceType == DeviceType) {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
HKEY sndOpenParametersKey(VOID)
|
|
{
|
|
return DrvOpenRegKey(DriverName, TEXT(""));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | sndFindDevices | Find all the devices using its handle
|
|
*
|
|
* @parm HKEY | Key| Handle to
|
|
*
|
|
* @parm PDEVICE_LIST * | DeviceList | The data about the devices found is
|
|
* returned here.
|
|
*
|
|
* @rdesc MMSYS... return value.
|
|
*
|
|
* @comm
|
|
* The registry structure is assumed to be (eg)
|
|
*
|
|
* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\sndsys\Parameters..
|
|
*
|
|
* Parameters
|
|
* Device0 One key for each card found
|
|
* DriverParameters
|
|
* Interrupt REG_DWORD
|
|
* Port REG_DWORD
|
|
* DmaChannel REG_DWORD
|
|
* Devices (Volatile key)
|
|
* WSSWave0 REG_BINARY (device data)
|
|
* WSSMix1 REG_BINARY (device data)
|
|
* MixerSettings REG_BINARY
|
|
*
|
|
* The (device data) for each device contains :
|
|
*
|
|
* DWORD DeviceType
|
|
* BYTE DeviceInstanceData[]
|
|
*
|
|
* NOTE - DON'T use the 'standard' names (eg WaveOut0) for your device
|
|
* or mmdrv will try to grab those devices.
|
|
*
|
|
*
|
|
***************************************************************************/
|
|
MMRESULT sndFindDevices(VOID)
|
|
{
|
|
DWORD Index;
|
|
HKEY DriverKey;
|
|
|
|
WinAssert(DeviceList == NULL);
|
|
|
|
/*
|
|
** Open the driver's registry key
|
|
*/
|
|
|
|
DriverKey = sndOpenParametersKey();
|
|
|
|
if (DriverKey == NULL) {
|
|
return sndTranslateStatus();
|
|
}
|
|
|
|
for (Index = 0; ; Index++) {
|
|
|
|
BOOL Rc;
|
|
HKEY Subkey;
|
|
|
|
if (!sndEnumKey(DriverKey, Index, SOUND_DEVICES_SUBKEY, &Subkey)) {
|
|
|
|
sndFreeDeviceList();
|
|
RegCloseKey(DriverKey);
|
|
return sndTranslateStatus();
|
|
}
|
|
|
|
/*
|
|
** Null returned subkey means end of list
|
|
*/
|
|
|
|
if (Subkey == NULL) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
** Process all the data for this device :
|
|
*/
|
|
|
|
Rc = sndProcessDevices(Subkey, &DeviceList, Index);
|
|
|
|
RegCloseKey(Subkey);
|
|
|
|
if (!Rc) {
|
|
sndFreeDeviceList();
|
|
RegCloseKey(DriverKey);
|
|
return sndTranslateStatus();
|
|
}
|
|
}
|
|
|
|
RegCloseKey(DriverKey);
|
|
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
BOOL sndProcessDevices(HKEY DevicesKey, PDEVICE_LIST *DeviceList, DWORD CardIndex)
|
|
{
|
|
BOOL Rc;
|
|
DWORD Index;
|
|
DWORD Length;
|
|
DWORD Type;
|
|
DWORD Value;
|
|
|
|
Rc = TRUE;
|
|
|
|
for (Index = 0; ; Index++) {
|
|
WCHAR DeviceName[SOUND_MAX_DEVICE_NAME];
|
|
DWORD Error;
|
|
DWORD cbSize;
|
|
PBYTE Data;
|
|
|
|
Length = sizeof(DeviceName) / sizeof(DeviceName[0]);
|
|
cbSize = 0;
|
|
|
|
/*
|
|
** Can't get the value at the same time as the key name because
|
|
** bugs in RegEnumValue mean that if the value is too big you
|
|
** won't get any of it!
|
|
*/
|
|
|
|
Error = RegEnumValue(DevicesKey,
|
|
Index,
|
|
DeviceName,
|
|
&Length,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&cbSize);
|
|
|
|
if (Error == ERROR_NO_MORE_ITEMS) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
** Check we got the data we wanted
|
|
*/
|
|
|
|
if (Error != ERROR_SUCCESS) {
|
|
Rc = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (cbSize < sizeof(DWORD)) {
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
Rc = FALSE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
** Get some space for the value
|
|
*/
|
|
|
|
Data = (PBYTE)HeapAlloc(hHeap, 0, cbSize);
|
|
|
|
if (Data == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
Rc = FALSE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
** Read the actual value
|
|
*/
|
|
|
|
Error = RegQueryValueEx(DevicesKey,
|
|
DeviceName,
|
|
NULL,
|
|
&Type,
|
|
Data,
|
|
&cbSize);
|
|
|
|
Value = *(LPDWORD)Data;
|
|
HeapFree(hHeap, 0, Data);
|
|
|
|
/*
|
|
** The kernel driver is supposed to put either a binary or
|
|
** DWORD into the registry
|
|
*/
|
|
|
|
if (Error != ERROR_SUCCESS) {
|
|
|
|
Rc = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (Type != REG_DWORD && Type != REG_BINARY ||
|
|
cbSize < sizeof(DWORD)) {
|
|
dprintf(("Invalid device type!"));
|
|
WinAssert(FALSE);
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
Rc = FALSE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
** Store the device data in the table
|
|
**
|
|
** When we actually open it we're going to need to append
|
|
** \\.\ .. to it
|
|
*/
|
|
|
|
if (!sndAppendToDeviceList(DeviceList, Value, CardIndex, DeviceName)) {
|
|
Rc = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Rc;
|
|
}
|
|
|
|
BOOL sndEnumKey(HKEY Key, DWORD Index, LPTSTR OurKey, HKEY *pSubkey)
|
|
{
|
|
DWORD cchName;
|
|
WCHAR SubKeyName[50];
|
|
HKEY Subkey;
|
|
UINT OurKeyLen;
|
|
|
|
OurKeyLen = lstrlen(OurKey);
|
|
|
|
cchName = sizeof(SubKeyName) / sizeof(SubKeyName[0]) - OurKeyLen;
|
|
|
|
switch (RegEnumKeyEx(Key,
|
|
Index,
|
|
SubKeyName,
|
|
&cchName,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL)) {
|
|
case ERROR_SUCCESS:
|
|
|
|
if (OurKeyLen != 0) {
|
|
wcscat(SubKeyName, TEXT("\\"));
|
|
}
|
|
wcscat(SubKeyName, OurKey);
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(Key,
|
|
SubKeyName,
|
|
0,
|
|
KEY_READ,
|
|
&Subkey)) {
|
|
return FALSE;
|
|
} else {
|
|
*pSubkey = Subkey;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
case ERROR_NO_MORE_ITEMS:
|
|
*pSubkey = NULL;
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL sndAppendToDeviceList(
|
|
PDEVICE_LIST *pList,
|
|
DWORD DeviceType,
|
|
DWORD CardIndex,
|
|
LPWSTR Name)
|
|
{
|
|
PDEVICE_LIST pNew;
|
|
|
|
#if DBG
|
|
PDEVICE_LIST List;
|
|
for (List = *pList; List != NULL; List = List->Next) {
|
|
if (List->DeviceType == DeviceType &&
|
|
lstrcmpi(List->Name, Name) == 0) {
|
|
dprintf(("Duplicate device name found - %ls!!", Name));
|
|
DebugBreak();
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
/*
|
|
** Allocate space for the new entry.
|
|
**
|
|
** Note that the space for the trailing null in the name is accounted
|
|
** for by the length of 1 given to the Name structure entry.
|
|
*/
|
|
|
|
pNew = (PDEVICE_LIST)HeapAlloc(
|
|
hHeap,
|
|
0,
|
|
sizeof(DEVICE_LIST) + lstrlen(Name) * sizeof(WCHAR));
|
|
|
|
if (pNew == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
** Fill in the new entry
|
|
*/
|
|
|
|
pNew->DeviceType = DeviceType;
|
|
pNew->CardIndex = CardIndex;
|
|
lstrcpy(pNew->Name, Name);
|
|
pNew->DeviceInstanceData = NULL;
|
|
|
|
pNew->Next = *pList;
|
|
|
|
*pList = pNew;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID sndFreeDeviceList(VOID)
|
|
{
|
|
PDEVICE_LIST pFree;
|
|
|
|
for (; DeviceList != NULL; ) {
|
|
pFree = DeviceList;
|
|
DeviceList = pFree->Next;
|
|
if (pFree->DeviceInstanceData != NULL) {
|
|
HeapFree(hHeap, 0, (LPVOID)pFree->DeviceInstanceData);
|
|
}
|
|
HeapFree(hHeap, 0, (LPSTR)pFree);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Load the device data into the device list
|
|
*/
|
|
|
|
PVOID sndLoadDeviceData(DWORD DeviceType, DWORD dwId, LPDWORD pSize)
|
|
{
|
|
PDEVICE_LIST List;
|
|
HKEY DriverKey;
|
|
DWORD Index;
|
|
BOOL Rc;
|
|
PVOID DeviceData;
|
|
|
|
DeviceData = NULL;
|
|
|
|
List = sndFindDevice(DeviceType, dwId);
|
|
|
|
WinAssert(List != NULL);
|
|
|
|
/*
|
|
** Search through all the entries trying to open the key with the
|
|
** device name. Note that it's possible for the registry to have
|
|
** totally changed so we can't assume all this is going to work
|
|
** or give the correct result. We can check the device type of
|
|
** the entry we open and of course the name must be correct.
|
|
*/
|
|
|
|
DriverKey = sndOpenParametersKey();
|
|
|
|
for (Index = 0, Rc = FALSE; ; Index++) {
|
|
|
|
HKEY Subkey;
|
|
DWORD Type;
|
|
DWORD Size;
|
|
|
|
if (!sndEnumKey(DriverKey, Index, SOUND_DEVICES_SUBKEY, &Subkey)) {
|
|
|
|
RegCloseKey(DriverKey);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
** Null returned subkey means end of list
|
|
*/
|
|
|
|
if (Subkey == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (Rc) {
|
|
/*
|
|
** Try to get the data for this device
|
|
*/
|
|
|
|
Size = 0;
|
|
Rc = (ERROR_SUCCESS == RegQueryValueEx(Subkey,
|
|
List->Name,
|
|
NULL,
|
|
&Type,
|
|
NULL,
|
|
&Size));
|
|
}
|
|
|
|
if (Rc) {
|
|
|
|
/*
|
|
** Allocate some space and try to load it
|
|
*/
|
|
|
|
DeviceData = (PVOID)HeapAlloc(hHeap, 0, Size);
|
|
|
|
if (DeviceData != NULL) {
|
|
|
|
Rc = (ERROR_SUCCESS == RegQueryValueEx(Subkey,
|
|
List->Name,
|
|
NULL,
|
|
&Type,
|
|
DeviceData,
|
|
&Size));
|
|
|
|
RegCloseKey(Subkey);
|
|
|
|
if (!Rc ||
|
|
*(LPDWORD)DeviceData != DeviceType) {
|
|
|
|
LocalFree((HLOCAL)DeviceData);
|
|
DeviceData = NULL;
|
|
|
|
} else {
|
|
|
|
*pSize = Size;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** This was the one we wanted
|
|
*/
|
|
|
|
break;
|
|
} else {
|
|
RegCloseKey(Subkey);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(DriverKey);
|
|
|
|
return DeviceData;
|
|
}
|
|
|
|
PDEVICE_LIST sndFindDevice(DWORD DeviceType, DWORD dwId)
|
|
{
|
|
PDEVICE_LIST List;
|
|
DWORD CurrentId;
|
|
|
|
for (List = DeviceList, CurrentId = 0; List != NULL; List = List->Next) {
|
|
if (List->DeviceType == DeviceType) {
|
|
if (CurrentId == dwId) {
|
|
break;
|
|
} else {
|
|
CurrentId++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return List;
|
|
}
|
|
|
|
BOOL sndFindDeviceInstanceData(DWORD DeviceType, DWORD dwId,
|
|
PVOID *InstanceData)
|
|
{
|
|
PDEVICE_LIST List;
|
|
|
|
List = sndFindDevice(DeviceType, dwId);
|
|
|
|
if (List == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
*InstanceData = List->DeviceInstanceData;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL sndSetDeviceInstanceData(DWORD DeviceType, DWORD dwId,
|
|
PVOID InstanceData)
|
|
{
|
|
PDEVICE_LIST List;
|
|
|
|
List = sndFindDevice(DeviceType, dwId);
|
|
|
|
WinAssert(List != NULL);
|
|
WinAssert(List->DeviceInstanceData == NULL);
|
|
List->DeviceInstanceData = InstanceData;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** Package up LoadString a bit
|
|
*/
|
|
|
|
void InternalLoadString(UINT StringId, LPTSTR pszString, UINT Length)
|
|
{
|
|
ZeroMemory(pszString, Length * sizeof(pszString[0]));
|
|
|
|
if (StringId != 0) {
|
|
LoadString(hInstance, StringId, pszString, Length);
|
|
}
|
|
}
|
|
|