windows-nt/Source/XPSP1/NT/drivers/wdm/audio/legacy/wdmaud.drv/mixer.c
2020-09-26 16:20:57 +08:00

989 lines
30 KiB
C

//---------------------------------------------------------------------------
//
// Module: mixer.c
//
// Description:
// Contains the kernel mode portion of the mixer line driver.
//
//
//@@BEGIN_MSINTERNAL
// Development Team:
// D. Baumberger
//
// History: Date Author Comment
//
//@@END_MSINTERNAL
//
//---------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
//
//---------------------------------------------------------------------------
#include "wdmdrv.h"
#include "mixer.h"
#ifndef UNDER_NT
extern volatile BYTE cPendingOpens;
extern volatile BYTE fExiting;
#else
HANDLE serializemixerapi=NULL;
SECURITY_ATTRIBUTES mutexsecurity;
SECURITY_DESCRIPTOR mutexdescriptor;
#endif
LPMIXERINSTANCE pMixerDeviceList = NULL;
#ifdef UNDER_NT
#define MXDM_GETHARDWAREEVENTDATA 0xffffffff
HANDLE mixercallbackevent=NULL;
HANDLE mixerhardwarecallbackevent=NULL;
HANDLE mixercallbackthread=NULL;
DWORD mixerthreadid=0;
ULONG localindex=0;
extern PCALLBACKS gpCallbacks;
extern DWORD sndTranslateStatus();
#pragma data_seg()
#define StoreCallback(Type,Id) {\
if (gpCallbacks) {\
gpCallbacks->Callbacks[gpCallbacks->GlobalIndex%CALLBACKARRAYSIZE].dwCallbackType = Type;\
gpCallbacks->Callbacks[gpCallbacks->GlobalIndex%CALLBACKARRAYSIZE].dwID = Id;\
gpCallbacks->GlobalIndex++;\
}\
};
ULONG GetGlobalCallbackIndex()
{
if (gpCallbacks != NULL) {
return(gpCallbacks->GlobalIndex);
}
return (0);
}
BOOLEAN
CallbacksExist
(
ULONG localindex,
DWORD *Type,
DWORD *Id
)
{
if (gpCallbacks == NULL) {
return (FALSE);
}
if (localindex < gpCallbacks->GlobalIndex) {
*Type = gpCallbacks->Callbacks[localindex%CALLBACKARRAYSIZE].dwCallbackType;
*Id = gpCallbacks->Callbacks[localindex%CALLBACKARRAYSIZE].dwID;
return (TRUE);
}
else {
return (FALSE);
}
}
///////////////////////////////////////////////////////////////////////
//
// MIXERCOMPLETE
//
// Receives the callbacks from the kernel and calls all the clients.
//
//
#define MIXER_CONTROL_CALLBACK 0x01
#define MIXER_LINE_CALLBACK 0x02
VOID MIXERCOMPLETE
(
ULONG index,
DWORD dwID,
DWORD dwCallbackType
)
{
LPMIXERINSTANCE lpInstance;
LPMIXERINSTANCE deletelpInstance=NULL;
// We have to syncronize the access to the instance list with the mixer
// close code that removes elements from this list, and with the mixer
// open code that adds elements to this list. The easiest way to do
// this is to use our global mixer api serialization mutex.
WaitForSingleObject(serializemixerapi,INFINITE);
// DPF( (2, "<----" ) );
for (lpInstance = pMixerDeviceList;
lpInstance != NULL;
lpInstance = lpInstance->Next
) {
ISVALIDMIXERINSTANCE(lpInstance);
if (deletelpInstance!=NULL) {
#ifdef DEBUG
deletelpInstance->dwSig=0;
#endif
GlobalFreePtr( deletelpInstance );
deletelpInstance=NULL;
}
// Wait until the index is at the first callback data
// for this instance before allowing any callbacks. If
// we are at the first callback data then allow all
// future callbacks.
if (lpInstance->firstcallbackindex) {
if (index<lpInstance->firstcallbackindex) {
continue;
}
else {
lpInstance->firstcallbackindex=0;
}
}
InterlockedIncrement(&lpInstance->referencecount);
if( dwCallbackType == MIXER_CONTROL_CALLBACK ) {
/*
DPF( (2, "MIXER_CONTROL_CALLBACK(%lX,%lX,%lX,%lX)",
dwID,
lpInstance->OpenDesc_dwCallback,
lpInstance->OpenDesc_hmx,
lpInstance->OpenDesc_dwInstance
)
);
*/
ReleaseMutex(serializemixerapi);
DriverCallback( lpInstance->OpenDesc_dwCallback,
DCB_FUNCTION,
lpInstance->OpenDesc_hmx,
MM_MIXM_CONTROL_CHANGE,
lpInstance->OpenDesc_dwInstance,
dwID,
0L
);
WaitForSingleObject(serializemixerapi,INFINITE);
} else if( dwCallbackType == MIXER_LINE_CALLBACK ) {
/*
DPF( (2, "MIXER_LINE_CALLBACK(%lX,%lX,%lX,%lX)",
dwID,
lpInstance->OpenDesc_dwCallback,
lpInstance->OpenDesc_hmx,
lpInstance->OpenDesc_dwInstance
)
);
*/
ReleaseMutex(serializemixerapi);
DriverCallback( lpInstance->OpenDesc_dwCallback,
DCB_FUNCTION,
lpInstance->OpenDesc_hmx,
MM_MIXM_LINE_CHANGE,
lpInstance->OpenDesc_dwInstance,
dwID,
0L
);
WaitForSingleObject(serializemixerapi,INFINITE);
} else {
//
// The callback wasn't one that is recognized. Just
// return and abort the loop
//
// DPF( (2, "Invalid Mixer Callback -- %d!", dwCallbackType) );
if (InterlockedDecrement(&lpInstance->referencecount)<0) {
deletelpInstance=lpInstance;
}
DPFASSERT(0);
break;
}
if (InterlockedDecrement(&lpInstance->referencecount)<0) {
deletelpInstance=lpInstance;
}
}
// DPF( (2, "---->" ) );
if (deletelpInstance!=NULL) {
#ifdef DEBUG
deletelpInstance->dwSig=0;
#endif
GlobalFreePtr( deletelpInstance );
deletelpInstance=NULL;
}
ReleaseMutex(serializemixerapi);
}
DWORD
WINAPI
MixerCallbackThread(
LPVOID lpParamNotUsed
)
{
MMRESULT status=MMSYSERR_NOERROR;
DWORD dwID;
WORD wCallbackType;
HANDLE callbackevents[2];
DWORD index;
DWORD Type, Id;
// Setup the handles array.
callbackevents[0]=mixerhardwarecallbackevent;
callbackevents[1]=mixercallbackevent;
if (!SetThreadPriority(mixercallbackthread,THREAD_PRIORITY_TIME_CRITICAL)) {
status=GetLastError();
}
while (1) {
// Block until a callback may be needed.
index=WaitForMultipleObjects(2,callbackevents,FALSE,INFINITE);
// Did hardware event happen? If so get the new data.
if (index==0) {
mxdMessage(0,MXDM_GETHARDWAREEVENTDATA,0,0,0);
}
// Scan through all new callbacks - looking for any that
// we need to make for this process.
while (CallbacksExist(localindex,&Type, &Id)) {
DPF(DL_TRACE|FA_EVENT, ("Thrd id %d, lindex %d, gindex %d, dwid %d, cbtype %d ",
mixerthreadid,
localindex,
gpCallbacks->GlobalIndex,
Id,
Type
));
MIXERCOMPLETE(localindex, Id, Type);
localindex++;
}
}
return ERROR_SUCCESS;
}
MMRESULT SetupMixerCallbacks(VOID)
{
MMRESULT status=MMSYSERR_NOERROR;
// First get a handle to a named global callback event so that
// the callback threads can block.
if (NULL==mixercallbackevent) {
// First assume event exists and try to open it.
// This will succeed in all cases except the very first
// time it is run.
mixercallbackevent=OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, TRUE, L"Global\\mixercallback");
if (NULL==mixercallbackevent) {
// Didn't exist, so now create it.
SECURITY_ATTRIBUTES SecurityAttributes;
PSECURITY_DESCRIPTOR pSecurityDescriptor;
// First build the required security descriptor.
pSecurityDescriptor=BuildSecurityDescriptor(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
if(pSecurityDescriptor==NULL) {
status= sndTranslateStatus();
return status;
}
//
// Create an event such that all processes have access to it.
//
SecurityAttributes.nLength = sizeof(SecurityAttributes);
SecurityAttributes.lpSecurityDescriptor = pSecurityDescriptor;
SecurityAttributes.bInheritHandle = FALSE;
mixercallbackevent=CreateEvent(&SecurityAttributes, TRUE, FALSE, L"Global\\mixercallback");
// Now free the security descriptor memory we allocated.
DestroySecurityDescriptor(pSecurityDescriptor);
if (NULL==mixercallbackevent) {
// Handle the race condition that exists when 2
// threads both try to Create and only the first succeeds.
mixercallbackevent=OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, TRUE, L"Global\\mixercallback");
if (NULL==mixercallbackevent) {
status= sndTranslateStatus();
return status;
}
}
}
}
// Now get a handle to the global hardware callback event.
if (NULL==mixerhardwarecallbackevent) {
// First assume event exists and try to open it.
// This will succeed in all cases except the very first
// time it is run.
mixerhardwarecallbackevent=OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, TRUE, L"Global\\hardwaremixercallback");
if (NULL==mixerhardwarecallbackevent) {
// Didn't exist, so now create it.
SECURITY_ATTRIBUTES SecurityAttributes;
PSECURITY_DESCRIPTOR pSecurityDescriptor;
// First build the required security descriptor.
pSecurityDescriptor=BuildSecurityDescriptor(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
if(pSecurityDescriptor==NULL) {
status= sndTranslateStatus();
return status;
}
//
// Create an event such that all processes have access to it.
//
SecurityAttributes.nLength = sizeof(SecurityAttributes);
SecurityAttributes.lpSecurityDescriptor = pSecurityDescriptor;
SecurityAttributes.bInheritHandle = FALSE;
// Note that this event releases only 1 thread at a time
// whereas the other callback event releases them all!
mixerhardwarecallbackevent=CreateEvent(&SecurityAttributes, FALSE, FALSE, L"Global\\hardwaremixercallback");
// Now free the security descriptor memory we allocated.
DestroySecurityDescriptor(pSecurityDescriptor);
if (NULL==mixerhardwarecallbackevent) {
// Handle the race condition that exists when 2
// threads both try to Create and only the first succeeds.
mixerhardwarecallbackevent=OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, TRUE, L"Global\\hardwaremixercallback");
if (NULL==mixerhardwarecallbackevent) {
status= sndTranslateStatus();
return status;
}
}
}
}
// Now create a thread in this process for making the
// callbacks if not already done.
if ( NULL == mixercallbackthread ) {
mixercallbackthread=CreateThread(NULL, 0, MixerCallbackThread, NULL, CREATE_SUSPENDED, &mixerthreadid);
if ( NULL == mixercallbackthread ) {
status= sndTranslateStatus();
return status;
} else {
// if we successfully created the thread, we can now activate it.
if( ResumeThread(mixercallbackthread) == -1 ) {
status= sndTranslateStatus();
return status;
}
}
}
return status;
}
#endif // UNDER_NT
//--------------------------------------------------------------------------
// LPDEVICEINFO AllocMixerDeviceInfo
//--------------------------------------------------------------------------
LPDEVICEINFO GlobalAllocMixerDeviceInfo(LPWSTR DeviceInterface, UINT id, DWORD_PTR dwKernelInstance)
{
LPDEVICEINFO pDeviceInfo;
pDeviceInfo = GlobalAllocDeviceInfo(DeviceInterface);
if (pDeviceInfo)
{
pDeviceInfo->dwInstance = dwKernelInstance;
pDeviceInfo->DeviceNumber = id;
pDeviceInfo->DeviceType = MixerDevice;
pDeviceInfo->dwCallbackType = 0;
pDeviceInfo->ControlCallbackCount=0;
#ifndef UNDER_NT
pDeviceInfo->dwFormat = ANSI_TAG;
#else
pDeviceInfo->dwFormat = UNICODE_TAG;
#endif // !UNDER_NT
}
return pDeviceInfo;
}
///////////////////////////////////////////////////////////////////////
//
// mxdMessage
//
//
DWORD FAR PASCAL _loadds mxdMessage
(
UINT id, // The device Id the message is for
UINT msg, // The message to perform
DWORD_PTR dwUser, // The instance data.
DWORD_PTR dwParam1, // Message specific parameter 1
DWORD_PTR dwParam2 // Message specific parmaeter 2
)
{
MMRESULT mmr;
ULONG initialcallbackindex;
if (NULL==serializemixerapi)
{
//
// To synchronize between this routine and the MixerCallbackThread we
// want a process specific mutex. Thus, we create one the first time
// through.
//
serializemixerapi=CreateMutex(NULL,FALSE,NULL);
if (NULL==serializemixerapi)
{
MMRRETURN( MMSYSERR_NOMEM );
}
}
WaitForSingleObject(serializemixerapi,INFINITE);
initialcallbackindex=GetGlobalCallbackIndex();
switch (msg)
{
///////////////////////////////////////////////////////////////
case MXDM_INIT:
///////////////////////////////////////////////////////////////
mmr=wdmaudAddRemoveDevNode( MixerDevice, (LPCWSTR)dwParam2, TRUE);
break;
///////////////////////////////////////////////////////////////
case DRVM_EXIT:
///////////////////////////////////////////////////////////////
mmr=wdmaudAddRemoveDevNode( MixerDevice, (LPCWSTR)dwParam2, FALSE);
break;
///////////////////////////////////////////////////////////////
case MXDM_GETNUMDEVS:
///////////////////////////////////////////////////////////////
DPF(DL_TRACE|FA_MIXER, ("MIXM_GETNUMDEVS(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
mmr=wdmaudGetNumDevs(MixerDevice, (LPCWSTR)dwParam1);
break;
///////////////////////////////////////////////////////////////
case MXDM_GETDEVCAPS:
///////////////////////////////////////////////////////////////
{
LPDEVICEINFO pDeviceInfo;
DPF(DL_TRACE|FA_MIXER, ("MIXM_GETDEVCAPS(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
pDeviceInfo = GlobalAllocMixerDeviceInfo((LPWSTR)dwParam2, id, 0);
if (pDeviceInfo) {
mmr = wdmaudGetDevCaps(pDeviceInfo, (MDEVICECAPSEX FAR*)dwParam1);
GlobalFreeDeviceInfo(pDeviceInfo);
}
else {
mmr = MMSYSERR_NOMEM;
}
}
break;
///////////////////////////////////////////////////////////////
case MXDM_OPEN:
///////////////////////////////////////////////////////////////
{
LPMIXEROPENDESC pMixerOpenDesc = (LPMIXEROPENDESC)dwParam1;
LPMIXERINSTANCE pMixerInstance;
DPF(DL_TRACE|FA_MIXER, ("MIXM_OPEN(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
pMixerInstance = (LPMIXERINSTANCE) GlobalAllocPtr(
GPTR | GMEM_SHARE,
sizeof( MIXERINSTANCE ) + (sizeof(WCHAR)*lstrlenW((LPWSTR)pMixerOpenDesc->dnDevNode)));
if( pMixerInstance == NULL ) {
mmr=MMSYSERR_NOMEM;
break;
}
pMixerInstance->referencecount=0;
#ifdef DEBUG
pMixerInstance->dwSig=MIXERINSTANCE_SIGNATURE;
#endif
if (mixercallbackthread==NULL) {
localindex=GetGlobalCallbackIndex();
}
pMixerInstance->firstcallbackindex=GetGlobalCallbackIndex();
if (mixercallbackthread==NULL) {
if ((mmr=SetupMixerCallbacks())!=MMSYSERR_NOERROR) {
GlobalFreePtr( pMixerInstance );
break;
}
}
// We allocate a DEVICEINFO and MIXEROPENDESC with GlobalAlloc so
// that on Win98 it is in system global memory. This is necessary
// because the following IOCTL is async on Win98. This isn't really
// necessary on NT 5, but in the interest of common sources we do it
// this way even on NT 5.
pMixerOpenDesc = GlobalAllocPtr(GPTR, sizeof(*pMixerOpenDesc));
if (pMixerOpenDesc) {
LPDEVICEINFO pDeviceInfo;
// Copy the input MIXEROPENDESC to our globally allocated MIXEROPENDESC
*pMixerOpenDesc = *((LPMIXEROPENDESC)dwParam1);
pDeviceInfo = GlobalAllocMixerDeviceInfo((LPWSTR)pMixerOpenDesc->dnDevNode, id, 0);
if (pDeviceInfo) {
pDeviceInfo->dwFlags = (DWORD)dwParam2;
pDeviceInfo->HardwareCallbackEventHandle=0;
if (mixerhardwarecallbackevent) {
pDeviceInfo->HardwareCallbackEventHandle=mixerhardwarecallbackevent;
}
pDeviceInfo->mmr = MMSYSERR_ERROR;
mmr = wdmaudIoControl(pDeviceInfo,
0,
NULL,
IOCTL_WDMAUD_MIXER_OPEN);
EXTRACTERROR(mmr,pDeviceInfo);
if (MMSYSERR_NOERROR == mmr) {
// Fill in the MixerInstance structure
pMixerInstance->Next = NULL;
pMixerInstance->OpenDesc_hmx = (HDRVR)pMixerOpenDesc->hmx;
pMixerInstance->OpenDesc_dwCallback = pMixerOpenDesc->dwCallback;
pMixerInstance->OpenDesc_dwInstance = pMixerOpenDesc->dwInstance;
pMixerInstance->OpenFlags = (DWORD)dwParam2;
lstrcpyW(pMixerInstance->wstrDeviceInterface, (LPWSTR)pMixerOpenDesc->dnDevNode);
pMixerInstance->dwKernelInstance = pDeviceInfo->dwInstance;
}
GlobalFreeDeviceInfo(pDeviceInfo);
pDeviceInfo = NULL;
}
else {
mmr = MMSYSERR_NOMEM;
}
GlobalFreePtr(pMixerOpenDesc);
pMixerOpenDesc = NULL;
}
else {
mmr = MMSYSERR_NOMEM;
}
if( mmr == MMSYSERR_NOERROR ) {
pMixerInstance->Next = pMixerDeviceList;
pMixerDeviceList = pMixerInstance;
*((PDWORD_PTR) dwUser) = (DWORD_PTR) pMixerInstance;
// Sanity check that we put a valid mixer instance structure in the list.
ISVALIDMIXERINSTANCE(pMixerInstance);
}
else {
#ifdef DEBUG
pMixerInstance->dwSig=0;
#endif
GlobalFreePtr( pMixerInstance );
}
}
break;
///////////////////////////////////////////////////////////////
case MXDM_CLOSE:
///////////////////////////////////////////////////////////////
{
LPMIXERINSTANCE pInstance = (LPMIXERINSTANCE)dwUser;
LPDEVICEINFO pDeviceInfo;
DPF(DL_TRACE|FA_MIXER, ("MIXM_CLOSE(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
if( (mmr=IsValidMixerInstance(pInstance)) != MMSYSERR_NOERROR)
break;
pDeviceInfo = GlobalAllocMixerDeviceInfo(pInstance->wstrDeviceInterface, id, pInstance->dwKernelInstance);
if (pDeviceInfo) {
mxdRemoveClient( pInstance );
pDeviceInfo->mmr = MMSYSERR_ERROR;
mmr = wdmaudIoControl(
pDeviceInfo,
0,
NULL,
IOCTL_WDMAUD_MIXER_CLOSE
);
EXTRACTERROR(mmr,pDeviceInfo);
GlobalFreeDeviceInfo(pDeviceInfo);
}
else {
mmr = MMSYSERR_NOMEM;
}
}
break;
///////////////////////////////////////////////////////////////
case MXDM_GETLINEINFO:
///////////////////////////////////////////////////////////////
{
LPMIXERINSTANCE pInstance = (LPMIXERINSTANCE)dwUser;
LPDEVICEINFO pDeviceInfo;
DPF(DL_TRACE|FA_MIXER, ("MIXM_GETLINEINFO(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
if( (mmr=IsValidMixerInstance(pInstance)) != MMSYSERR_NOERROR)
break;
pDeviceInfo = GlobalAllocMixerDeviceInfo(pInstance->wstrDeviceInterface, id, pInstance->dwKernelInstance);
if (pDeviceInfo) {
pDeviceInfo->dwFlags = (DWORD)dwParam2;
pDeviceInfo->mmr = MMSYSERR_ERROR;
mmr = wdmaudIoControl(
pDeviceInfo,
((LPMIXERLINE) dwParam1)->cbStruct,
(LPVOID) dwParam1,
IOCTL_WDMAUD_MIXER_GETLINEINFO
);
EXTRACTERROR(mmr,pDeviceInfo);
GlobalFreeDeviceInfo(pDeviceInfo);
}
else {
mmr = MMSYSERR_NOMEM;
}
}
break;
///////////////////////////////////////////////////////////////
case MXDM_GETLINECONTROLS:
///////////////////////////////////////////////////////////////
{
LPMIXERINSTANCE pInstance = (LPMIXERINSTANCE)dwUser;
LPDEVICEINFO pDeviceInfo;
DPF(DL_TRACE|FA_MIXER, ("MIXM_GETLINECONTROLS(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
if( (mmr=IsValidMixerInstance(pInstance)) != MMSYSERR_NOERROR)
break;
pDeviceInfo = GlobalAllocMixerDeviceInfo(pInstance->wstrDeviceInterface, id, pInstance->dwKernelInstance);
if (pDeviceInfo) {
pDeviceInfo->dwFlags = (DWORD)dwParam2;
pDeviceInfo->mmr = MMSYSERR_ERROR;
mmr = wdmaudIoControl(
pDeviceInfo,
((LPMIXERLINECONTROLS) dwParam1)->cbStruct,
(LPVOID) dwParam1,
IOCTL_WDMAUD_MIXER_GETLINECONTROLS
);
EXTRACTERROR(mmr,pDeviceInfo);
GlobalFreeDeviceInfo(pDeviceInfo);
}
else {
mmr = MMSYSERR_NOMEM;
}
}
break;
///////////////////////////////////////////////////////////////
case MXDM_GETCONTROLDETAILS:
///////////////////////////////////////////////////////////////
{
LPMIXERINSTANCE pInstance = (LPMIXERINSTANCE)dwUser;
LPDEVICEINFO pDeviceInfo;
DPF(DL_TRACE|FA_MIXER, ("MIXM_GETCONTROLDETAILS(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
if( (mmr=IsValidMixerInstance(pInstance)) != MMSYSERR_NOERROR)
break;
pDeviceInfo = GlobalAllocMixerDeviceInfo(pInstance->wstrDeviceInterface, id, pInstance->dwKernelInstance);
if (pDeviceInfo) {
pDeviceInfo->dwFlags = (DWORD)dwParam2;
pDeviceInfo->mmr = MMSYSERR_ERROR;
mmr = wdmaudIoControl(
pDeviceInfo,
((LPMIXERCONTROLDETAILS) dwParam1)->cbStruct,
(LPVOID) dwParam1,
IOCTL_WDMAUD_MIXER_GETCONTROLDETAILS
);
EXTRACTERROR(mmr,pDeviceInfo);
GlobalFreeDeviceInfo(pDeviceInfo);
}
else {
mmr = MMSYSERR_NOMEM;
}
}
break;
///////////////////////////////////////////////////////////////
case MXDM_SETCONTROLDETAILS:
///////////////////////////////////////////////////////////////
{
LPMIXERINSTANCE pInstance = (LPMIXERINSTANCE)dwUser;
LPDEVICEINFO pDeviceInfo;
DPF(DL_TRACE|FA_MIXER, ("MIXM_SETCONTROLDETAILS(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
if( (mmr=IsValidMixerInstance(pInstance)) != MMSYSERR_NOERROR)
break;
pDeviceInfo = GlobalAllocMixerDeviceInfo(pInstance->wstrDeviceInterface, id, pInstance->dwKernelInstance);
if (pDeviceInfo) {
pDeviceInfo->dwFlags = (DWORD)dwParam2;
pDeviceInfo->mmr = MMSYSERR_ERROR;
pDeviceInfo->dwCallbackType=0;
mmr = wdmaudIoControl(
pDeviceInfo,
((LPMIXERCONTROLDETAILS) dwParam1)->cbStruct,
(LPVOID) dwParam1,
IOCTL_WDMAUD_MIXER_SETCONTROLDETAILS
);
EXTRACTERROR(mmr,pDeviceInfo);
if (pDeviceInfo->dwCallbackType&MIXER_CONTROL_CALLBACK) {
LONG j;
for (j=0; j<pDeviceInfo->ControlCallbackCount; j++) {
StoreCallback(MIXER_CONTROL_CALLBACK,
(pDeviceInfo->dwID)[j]);
}
}
if (pDeviceInfo->dwCallbackType&MIXER_LINE_CALLBACK) {
StoreCallback(MIXER_LINE_CALLBACK,
pDeviceInfo->dwLineID);
}
GlobalFreeDeviceInfo(pDeviceInfo);
}
else {
mmr = MMSYSERR_NOMEM;
}
}
// Map invalid error codes to valid ones.
switch (mmr) {
case MMSYSERR_ERROR:
mmr = MMSYSERR_NOMEM;
break;
default:
break;
}
break;
#ifdef UNDER_NT
///////////////////////////////////////////////////////////////
case MXDM_GETHARDWAREEVENTDATA:
///////////////////////////////////////////////////////////////
{
LPMIXERINSTANCE pInstance = (LPMIXERINSTANCE)dwUser;
LPDEVICEINFO pDeviceInfo;
DPF(DL_TRACE|FA_MIXER, ("MXDM_GETHARDWAREEVENTDATA(%d,%lx,%lx,%lx)", id, dwUser, dwParam1, dwParam2) );
DPFASSERT( dwUser==0 && dwParam1==0 && dwParam2==0 );
if (dwUser!=0 || dwParam1!=0 || dwParam2!=0) {
mmr=MMSYSERR_INVALPARAM;
break;
}
pDeviceInfo = GlobalAllocMixerDeviceInfo(L" ", 0, 0);
if (pDeviceInfo) {
pDeviceInfo->dwCallbackType=1;
// WorkItem: Hey, this loop continually calls the driver and doesn't error out!!!!! Shouldn't it?
while(pDeviceInfo->dwCallbackType) {
pDeviceInfo->dwFlags = 0;
pDeviceInfo->mmr = MMSYSERR_ERROR;
pDeviceInfo->dwCallbackType=0;
mmr = wdmaudIoControl(
pDeviceInfo,
0,
NULL,
IOCTL_WDMAUD_MIXER_GETHARDWAREEVENTDATA
);
EXTRACTERROR(mmr,pDeviceInfo);
if (pDeviceInfo->dwCallbackType&MIXER_CONTROL_CALLBACK) {
LONG j;
for (j=0; j<pDeviceInfo->ControlCallbackCount; j++) {
StoreCallback(MIXER_CONTROL_CALLBACK,
(pDeviceInfo->dwID)[j]);
}
}
if (pDeviceInfo->dwCallbackType&MIXER_LINE_CALLBACK) {
StoreCallback(MIXER_LINE_CALLBACK,
pDeviceInfo->dwLineID);
}
}
mmr = pDeviceInfo->mmr; // WorkItem: Why isn't this inside the loop?
GlobalFreeDeviceInfo(pDeviceInfo);
}
else {
mmr = MMSYSERR_NOMEM;
}
}
break;
#endif
///////////////////////////////////////////////////////////////
default:
///////////////////////////////////////////////////////////////
mmr=MMSYSERR_NOTSUPPORTED;
break;
}
//#if 0
#ifdef UNDER_NT
ReleaseMutex(serializemixerapi);
// Now check if any callbacks were logged while we were
// running. If so, make them.
if (GetGlobalCallbackIndex()!=initialcallbackindex) {
if (mixercallbackevent!=NULL) {
PulseEvent(mixercallbackevent);
}
}
#endif
MMRRETURN( mmr );
} // mxdMessage()
///////////////////////////////////////////////////////////////////////
//
// mxdRemoveClient
//
//
VOID
mxdRemoveClient(
LPMIXERINSTANCE lpInstance
)
{
LPMIXERINSTANCE lp, lpPrevious;
lpPrevious = (LPMIXERINSTANCE)&pMixerDeviceList;
for(lp = pMixerDeviceList; lp != NULL; lp = lp->Next) {
ISVALIDMIXERINSTANCE(lp);
if(lp == lpInstance) {
lpPrevious->Next = lp->Next;
#ifdef UNDER_NT
if (InterlockedDecrement(&lpInstance->referencecount)<0) {
// The instance is not in use by a callback. So OK to free it.
#ifdef DEBUG
lpInstance->dwSig=0;
#endif
GlobalFreePtr( lpInstance );
}
// This instance is in use by a callback, so do not free it now.
// We are already setup so the callback will free it, since we have
// changed the referencecount so it will not be zero after the callback
// code decrements it. That will prompt the callback code to release
// the instance memory.
#else
GlobalFreePtr( lpInstance );
#endif
break;
}
lpPrevious = lp;
}
}