//--------------------------------------------------------------------------- // // 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 (indexfirstcallbackindex) { 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; jControlCallbackCount; 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; jControlCallbackCount; 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; } }