989 lines
30 KiB
C
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;
|
|
}
|
|
}
|
|
|