/**************************************************************************** * * wdmaud32.c * * 32-bit specific interfaces for WDMAUD * * Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved. * * History * 5-12-97 - Noel Cross (NoelC) * ***************************************************************************/ #include "wdmdrv.h" HANDLE ghCallbacks = NULL; PCALLBACKS gpCallbacks = NULL; HANDLE ghDevice = NULL; HANDLE ghMemoryDeviceInterfacePath = NULL; HANDLE ghMemorySize = NULL; DWORD gdwSize = 0; LPWSTR gpszDeviceInterfacePath = NULL; BOOL wdmaudCritSecInit; CRITICAL_SECTION wdmaudCritSec; static TCHAR gszMemoryPathSize[] = TEXT("WDMAUD_Path_Size"); static TCHAR gszMemoryInterfacePath[] = TEXT("WDMAUD_Device_Interface_Path"); static TCHAR gszCallbacks[] = TEXT("Global\\WDMAUD_Callbacks"); extern HANDLE mixercallbackevent; extern HANDLE mixerhardwarecallbackevent; extern HANDLE mixercallbackthread; DWORD waveThread(LPDEVICEINFO DeviceInfo); DWORD midThread(LPDEVICEINFO DeviceInfo); DWORD sndTranslateStatus(); typedef struct _SETUPAPIDLINFO { HINSTANCE hInstSetupAPI; BOOL (WINAPI *pfnDestroyDeviceInfoList)(HDEVINFO); BOOL (WINAPI *pfnGetDeviceInterfaceDetailW)(HDEVINFO, PSP_DEVICE_INTERFACE_DATA, PSP_DEVICE_INTERFACE_DETAIL_DATA_W, DWORD, PDWORD, PSP_DEVINFO_DATA); BOOL (WINAPI *pfnEnumDeviceInterfaces)(HDEVINFO, PSP_DEVINFO_DATA, CONST GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA); HDEVINFO (WINAPI *pfnGetClassDevsW)(CONST GUID*, PCWSTR, HWND, DWORD); } SETUPAPIDLINFO; SETUPAPIDLINFO saInfo = {NULL, NULL, NULL, NULL}; /**************************************************************************** Dynalinking setupapi ****************************************************************************/ BOOL Init_SetupAPI(); BOOL End_SetupAPI(); BOOL dl_SetupDiDestroyDeviceInfoList ( HDEVINFO DeviceInfoSet ); BOOL dl_SetupDiGetDeviceInterfaceDetail ( HDEVINFO DeviceInfoSet, PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData, DWORD DeviceInterfaceDetailDataSize, PDWORD RequiredSize, PSP_DEVINFO_DATA DeviceInfoData ); BOOL dl_SetupDiEnumDeviceInterfaces ( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, CONST GUID *InterfaceClassGuid, DWORD MemberIndex, PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData ); HDEVINFO dl_SetupDiGetClassDevs ( CONST GUID *ClassGuid, PCWSTR Enumerator, HWND hwndParent, DWORD Flags ); /**************************************************************************** Setting up SetupAPI Dynalink... ****************************************************************************/ BOOL Init_SetupAPI ( void ) { if (NULL != saInfo.hInstSetupAPI) { return TRUE; } DPF(DL_TRACE|FA_SETUP, ("Loading SetupAPI!!!") ); saInfo.hInstSetupAPI = LoadLibrary(TEXT("setupapi.dll")); if (NULL == saInfo.hInstSetupAPI) { return FALSE; } saInfo.pfnDestroyDeviceInfoList = (LPVOID)GetProcAddress(saInfo.hInstSetupAPI, "SetupDiDestroyDeviceInfoList"); saInfo.pfnGetDeviceInterfaceDetailW = (LPVOID)GetProcAddress(saInfo.hInstSetupAPI, "SetupDiGetDeviceInterfaceDetailW"); saInfo.pfnEnumDeviceInterfaces = (LPVOID)GetProcAddress(saInfo.hInstSetupAPI, "SetupDiEnumDeviceInterfaces"); saInfo.pfnGetClassDevsW = (LPVOID)GetProcAddress(saInfo.hInstSetupAPI, "SetupDiGetClassDevsW"); if ((NULL == saInfo.pfnDestroyDeviceInfoList) || (NULL == saInfo.pfnGetDeviceInterfaceDetailW) || (NULL == saInfo.pfnEnumDeviceInterfaces) || (NULL == saInfo.pfnGetClassDevsW)) { FreeLibrary(saInfo.hInstSetupAPI); ZeroMemory(&saInfo, sizeof(saInfo)); return FALSE; } return TRUE; } BOOL End_SetupAPI ( void ) { HINSTANCE hInst; hInst = saInfo.hInstSetupAPI; if (NULL == hInst) { DPF(DL_WARNING|FA_SETUP, ("SetupAPI not dynalinked") ); return FALSE; } ZeroMemory(&saInfo, sizeof(saInfo)); FreeLibrary(hInst); return TRUE; } BOOL dl_SetupDiDestroyDeviceInfoList ( HDEVINFO DeviceInfoSet ) { if (NULL == saInfo.hInstSetupAPI) { DPF(DL_WARNING|FA_SETUP, ("SetupAPI not dynalinked") ); return FALSE; } return (saInfo.pfnDestroyDeviceInfoList)(DeviceInfoSet); } BOOL dl_SetupDiGetDeviceInterfaceDetail ( HDEVINFO DeviceInfoSet, PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData, DWORD DeviceInterfaceDetailDataSize, PDWORD RequiredSize, PSP_DEVINFO_DATA DeviceInfoData ) { if (NULL == saInfo.hInstSetupAPI) { DPF(DL_WARNING|FA_SETUP, ("SetupAPI not dynalinked") ); return FALSE; } return (saInfo.pfnGetDeviceInterfaceDetailW)( DeviceInfoSet, DeviceInterfaceData, DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData); } BOOL dl_SetupDiEnumDeviceInterfaces ( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, CONST GUID *InterfaceClassGuid, DWORD MemberIndex, PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData ) { if (NULL == saInfo.hInstSetupAPI) { DPF(DL_WARNING|FA_SETUP, ("SetupAPI not dynalinked") ); return FALSE; } return (saInfo.pfnEnumDeviceInterfaces)( DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, MemberIndex, DeviceInterfaceData); } HDEVINFO dl_SetupDiGetClassDevs ( CONST GUID *ClassGuid, PCWSTR Enumerator, HWND hwndParent, DWORD Flags ) { if (NULL == saInfo.hInstSetupAPI) { DPF(DL_WARNING|FA_SETUP, ("SetupAPI not dynalinked") ); return FALSE; } return (saInfo.pfnGetClassDevsW)( ClassGuid, Enumerator, hwndParent, Flags); } PSECURITY_DESCRIPTOR BuildSecurityDescriptor(DWORD AccessMask) { PSECURITY_DESCRIPTOR pSd; PSID pSidSystem; PSID pSidEveryone; PACL pDacl; ULONG cbDacl; BOOL fSuccess; BOOL f; SID_IDENTIFIER_AUTHORITY AuthorityNt = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY AuthorityWorld = SECURITY_WORLD_SID_AUTHORITY; fSuccess = FALSE; pSd = HeapAlloc(GetProcessHeap(), 0, SECURITY_DESCRIPTOR_MIN_LENGTH); if (pSd) { if (InitializeSecurityDescriptor(pSd, SECURITY_DESCRIPTOR_REVISION)) { if (AllocateAndInitializeSid(&AuthorityNt, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSidSystem)) { DPFASSERT(IsValidSid(pSidSystem)); if (AllocateAndInitializeSid(&AuthorityWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSidEveryone)) { DPFASSERT(IsValidSid(pSidEveryone)); cbDacl = sizeof(ACL) + 2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetLengthSid(pSidSystem) + GetLengthSid(pSidEveryone); pDacl = HeapAlloc(GetProcessHeap(), 0, cbDacl); if (pDacl) { if (InitializeAcl(pDacl, cbDacl, ACL_REVISION)) { if (AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_ALL, pSidSystem)) { if (AddAccessAllowedAce(pDacl, ACL_REVISION, AccessMask, pSidEveryone)) { if (SetSecurityDescriptorDacl(pSd, TRUE, pDacl, FALSE)) { fSuccess = TRUE; } else { DPF(DL_WARNING|FA_SETUP, ("BuildSD: SetSecurityDescriptorDacl failed")); } } else { DPF(DL_WARNING|FA_SETUP, ("BuildSD: AddAccessAlloweAce for Everyone failed")); } } else { DPF(DL_WARNING|FA_SETUP, ("BuildSD: AddAccessAllowedAce for System failed")); } } else { DPF(DL_WARNING|FA_SETUP, ("BuildSD: InitializeAcl failed")); } if (!fSuccess) { f = HeapFree(GetProcessHeap(), 0, pDacl); DPFASSERT(f); } } FreeSid(pSidEveryone); } else { DPF(DL_WARNING|FA_SETUP, ("BuildSD: AllocateAndInitizeSid failed for Everyone")); } FreeSid(pSidSystem); } else { DPF(DL_WARNING|FA_SETUP, ("BuildSD: AllocateAndInitizeSid failed for System")); } } else { DPF(DL_WARNING|FA_SETUP, ("BuildSD: InitializeSecurityDescriptor failed")); } if (!fSuccess) { f = HeapFree(GetProcessHeap(), 0, pSd); DPFASSERT(f); } } return fSuccess ? pSd : NULL; } void DestroySecurityDescriptor(PSECURITY_DESCRIPTOR pSd) { PACL pDacl; BOOL fDaclPresent; BOOL fDaclDefaulted; BOOL f; if (GetSecurityDescriptorDacl(pSd, &fDaclPresent, &pDacl, &fDaclDefaulted)) { if (fDaclPresent) { f = HeapFree(GetProcessHeap(), 0, pDacl); DPFASSERT(f); } } else { DPF(DL_WARNING|FA_SETUP, ("DestroySD: GetSecurityDescriptorDacl failed")); } f = HeapFree(GetProcessHeap(), 0, pSd); DPFASSERT(f); return; } /************************************************************************** @doc EXTERNAL @api BOOL | DllEntryPoint | This procedure is called whenever a process attaches or detaches from the DLL. @rdesc The return value is TRUE if the initialisation completed ok, FALSE if not. **************************************************************************/ BOOL WINAPI DllEntryPoint ( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { BOOL bRet; if (fdwReason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hinstDLL); bRet = LibMain((HANDLE)hinstDLL, 0, NULL); } else { if (fdwReason == DLL_PROCESS_DETACH) { DPF(DL_TRACE|FA_ALL, ("Ending") ); DrvEnd(); } bRet = TRUE; } return bRet; } /************************************************************************** @doc EXTERNAL @api BOOL | DrvInit | Driver initialization takes place here. @rdesc The return value is TRUE if the initialisation completed ok, FALSE if not. **************************************************************************/ BOOL DrvInit() { if (NULL == ghDevice) { ghDevice = wdmaOpenKernelDevice(); if (ghDevice == INVALID_HANDLE_VALUE) { ghDevice = NULL; return 0L; } } if (gpCallbacks == NULL) { if ((gpCallbacks = wdmaGetCallbacks()) == NULL) { gpCallbacks = wdmaCreateCallbacks(); } } try { wdmaudCritSecInit = FALSE; InitializeCriticalSection(&wdmaudCritSec); } except(EXCEPTION_EXECUTE_HANDLER) { return 0L; } wdmaudCritSecInit = TRUE; return ( 1L ) ; } PCALLBACKS wdmaGetCallbacks ( ) { PCALLBACKS pCallbacks = NULL; if (gpCallbacks == NULL) { ghCallbacks = OpenFileMapping( FILE_MAP_READ|FILE_MAP_WRITE, FALSE, gszCallbacks); if(ghCallbacks != NULL) { pCallbacks = MapViewOfFile( ghCallbacks, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(CALLBACKS)); if (pCallbacks == NULL) { CloseHandle(ghCallbacks); ghCallbacks = NULL; } } } return (pCallbacks); } PCALLBACKS wdmaCreateCallbacks ( ) { SECURITY_ATTRIBUTES saCallbacks; PSECURITY_DESCRIPTOR pSdCallbacks; PCALLBACKS pCallbacks = NULL; pSdCallbacks = BuildSecurityDescriptor(FILE_MAP_READ|FILE_MAP_WRITE); if (pSdCallbacks == NULL) { return (NULL); } saCallbacks.nLength = sizeof(SECURITY_ATTRIBUTES); saCallbacks.lpSecurityDescriptor = pSdCallbacks; saCallbacks.bInheritHandle = FALSE; ghCallbacks = CreateFileMapping( GetCurrentProcess(), &saCallbacks, PAGE_READWRITE, 0, sizeof(CALLBACKS), gszCallbacks); DestroySecurityDescriptor(pSdCallbacks); if (ghCallbacks == NULL) { return (NULL); } pCallbacks = (PCALLBACKS) MapViewOfFile( ghCallbacks, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(CALLBACKS)); if (pCallbacks == NULL) { CloseHandle(ghCallbacks); ghCallbacks = NULL; return (NULL); } pCallbacks->GlobalIndex = 0; return (pCallbacks); } BOOL wdmaSetGlobalDeviceInterfaceSizeandPath ( LPWSTR pszInterfacePath, DWORD dwSize ) { SECURITY_ATTRIBUTES saPath; SECURITY_ATTRIBUTES saSize; PSECURITY_DESCRIPTOR pSdPath; PSECURITY_DESCRIPTOR pSdSize; LPWSTR pszMappedDeviceInterfacePath = NULL; PDWORD pdwMappedSize = NULL; if (!pszInterfacePath || (dwSize == 0) ) { return FALSE; } pSdPath = BuildSecurityDescriptor(FILE_MAP_READ); if (NULL == pSdPath) { return FALSE; } saPath.nLength = sizeof(SECURITY_ATTRIBUTES); saPath.lpSecurityDescriptor = pSdPath; saPath.bInheritHandle = FALSE; // // Do the path name first and then do the size // ghMemoryDeviceInterfacePath = CreateFileMapping( GetCurrentProcess(), &saPath, PAGE_READWRITE, 0, dwSize, gszMemoryInterfacePath); DestroySecurityDescriptor(pSdPath); if(NULL == ghMemoryDeviceInterfacePath) { // Hmm... for whatever reason we can't create this global memory // object. Return of size of 0. return FALSE; } pszMappedDeviceInterfacePath = MapViewOfFile( ghMemoryDeviceInterfacePath, FILE_MAP_WRITE, 0, 0, dwSize); if(NULL == pszMappedDeviceInterfacePath) { CloseHandle(ghMemoryDeviceInterfacePath); ghMemoryDeviceInterfacePath = NULL; return FALSE; } // This is the data we need stored lstrcpyW(pszMappedDeviceInterfacePath, pszInterfacePath); // Close just the view of the file. We still have the file mapping handle which will // will be freed in DrvEnd UnmapViewOfFile(pszMappedDeviceInterfacePath); // // Now lets store the size // pSdSize = BuildSecurityDescriptor(FILE_MAP_READ); if (NULL == pSdSize) { CloseHandle(ghMemorySize); return FALSE; } saSize.nLength = sizeof(SECURITY_ATTRIBUTES); saSize.lpSecurityDescriptor = pSdSize; saSize.bInheritHandle = FALSE; ghMemorySize = CreateFileMapping( GetCurrentProcess(), &saSize, PAGE_READWRITE, 0, sizeof(DWORD), gszMemoryPathSize); DestroySecurityDescriptor(pSdSize); if(NULL == ghMemorySize) { // Hmm... for whatever reason we can't create this global memory // object. Return of size of 0. CloseHandle(ghMemoryDeviceInterfacePath); ghMemoryDeviceInterfacePath = NULL; DPF(DL_WARNING|FA_SETUP, ("Failed to create FileMapping for Path Size")); return FALSE; } pdwMappedSize = MapViewOfFile( ghMemorySize, FILE_MAP_WRITE, 0, 0, sizeof(DWORD)); if(NULL == pdwMappedSize) { CloseHandle(ghMemorySize); ghMemorySize = NULL; CloseHandle(ghMemoryDeviceInterfacePath); ghMemoryDeviceInterfacePath = NULL; DPF(DL_WARNING|FA_SETUP, ("Failed to create MappedView for Path Size")); return FALSE; } // This is the data we need stored *pdwMappedSize = dwSize; // Close just the view of the file. We still have the file mapping handle which will // will be freed in DrvEnd UnmapViewOfFile(pdwMappedSize); return TRUE; } // wdmaGetGlobalDeviceInterfacePath() LPWSTR wdmaGetGlobalDeviceInterfaceViaSetupAPI() { LPWSTR pszInterfacePath = NULL; HDEVINFO hDeviceInfoSet = NULL; SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceInterfaceDetailData = NULL; BOOL fResult; DWORD dwSize; GUID guidWDMAUD = KSCATEGORY_WDMAUD; // // Because setupapi is such a pig, we must dynaload it in order to keep it from slowing // down all processes. // if (!Init_SetupAPI()) return NULL; // // Open the device information set // hDeviceInfoSet = dl_SetupDiGetClassDevs ( &guidWDMAUD, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE ); if((!hDeviceInfoSet) || (INVALID_HANDLE_VALUE == hDeviceInfoSet)) { DPF(DL_WARNING|FA_SETUP, ("Can't open device info set (%lu)", GetLastError()) ); fResult = FALSE; } else { fResult = TRUE; } if (fResult) { // // Get the first interface in the set // DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData); fResult = dl_SetupDiEnumDeviceInterfaces ( hDeviceInfoSet, NULL, &guidWDMAUD, 0, &DeviceInterfaceData ); if(!fResult) { DPF(DL_WARNING|FA_SETUP, ("No interfaces matching KSCATEGORY_WDMAUD exist") ); } } // // Get the interface's path // if (fResult) { fResult = dl_SetupDiGetDeviceInterfaceDetail ( hDeviceInfoSet, &DeviceInterfaceData, NULL, 0, &dwSize, NULL ); // // because SetupApi reverses their logic here // if(fResult || ERROR_INSUFFICIENT_BUFFER != GetLastError()) { DPF(DL_WARNING|FA_SETUP, ("Can't get interface detail size (%lu)", GetLastError())); fResult = FALSE; } else { fResult = TRUE; } if (fResult) { pDeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) GlobalAllocPtr( GPTR, dwSize ); if (NULL == pDeviceInterfaceDetailData) { fResult = FALSE; } } if (fResult) { pDeviceInterfaceDetailData->cbSize = sizeof(*pDeviceInterfaceDetailData); fResult = dl_SetupDiGetDeviceInterfaceDetail ( hDeviceInfoSet, &DeviceInterfaceData, pDeviceInterfaceDetailData, dwSize, NULL, NULL ); if (!fResult) { GlobalFreePtr(pDeviceInterfaceDetailData); DPF(DL_WARNING|FA_SETUP, ("Can't get device interface detail (%lu)", GetLastError()) ); } } // // If we get the pathname, return it but also store it in a file mapping so that // other processes can get this information without having to load setupapi. // if (fResult) { DPFASSERT(gpszDeviceInterfacePath == NULL); gpszDeviceInterfacePath = (LPWSTR) GlobalAllocPtr( GPTR, sizeof(WCHAR)*(lstrlenW(pDeviceInterfaceDetailData->DevicePath) + 1)); if (NULL == gpszDeviceInterfacePath) { fResult = FALSE; } else { lstrcpyW(gpszDeviceInterfacePath, pDeviceInterfaceDetailData->DevicePath); } fResult = wdmaSetGlobalDeviceInterfaceSizeandPath(pDeviceInterfaceDetailData->DevicePath, sizeof(WCHAR)*(lstrlenW(pDeviceInterfaceDetailData->DevicePath) + 1)); GlobalFreePtr(pDeviceInterfaceDetailData); } } if((hDeviceInfoSet) && (INVALID_HANDLE_VALUE != hDeviceInfoSet)) { dl_SetupDiDestroyDeviceInfoList(hDeviceInfoSet); } End_SetupAPI(); return gpszDeviceInterfacePath; } LPWSTR wdmaGetGlobalDeviceInterfacePath ( DWORD dwSize ) { LPWSTR pszMappedDeviceInterfacePath = NULL; HANDLE hMemory; if (NULL == gpszDeviceInterfacePath) { hMemory = OpenFileMapping( FILE_MAP_READ, FALSE, gszMemoryInterfacePath); if(NULL == hMemory) { // Hmm... for whatever reason we can't create this global memory // object. Return of size of 0. DPF(DL_WARNING|FA_SETUP, ("Failed to OpenFileMapping for Interface Path")); return NULL; } pszMappedDeviceInterfacePath = MapViewOfFile( hMemory, FILE_MAP_READ, 0, 0, dwSize); if(NULL == pszMappedDeviceInterfacePath) { CloseHandle(hMemory); DPF(DL_WARNING|FA_SETUP, ("Failed to MapViewOfFile for Interface Path")); return NULL; } gpszDeviceInterfacePath = (LPWSTR) GlobalAllocPtr( GPTR, dwSize ); if (gpszDeviceInterfacePath) { // This is the data we needed lstrcpyW(gpszDeviceInterfacePath, pszMappedDeviceInterfacePath); } // Close everything down now UnmapViewOfFile(pszMappedDeviceInterfacePath); CloseHandle(hMemory); } DPF(DL_TRACE|FA_SETUP, ("Path is: %ls",gpszDeviceInterfacePath)); return (gpszDeviceInterfacePath); } // wdmaGetGlobalDeviceInterfacePath() DWORD wdmaGetGlobalDeviceInterfaceSize ( void ) { PDWORD pdwMappedSize; HANDLE hMemory; if (0 == gdwSize) { hMemory = OpenFileMapping( FILE_MAP_READ, FALSE, gszMemoryPathSize); if(NULL == hMemory) { // Hmm... for whatever reason we can't create this global memory // object. Return of size of 0. DPF(DL_WARNING|FA_SETUP, ("Failed to OpenFileMapping for Path Size")); return 0; } pdwMappedSize = MapViewOfFile( hMemory, FILE_MAP_READ, 0, 0, sizeof(DWORD)); if(NULL == pdwMappedSize) { DPF(DL_WARNING|FA_SETUP, ("Failed to MapViewOfFile for Path Size")); return 0; } // This is the data we needed gdwSize = *pdwMappedSize; DPF(DL_TRACE|FA_SETUP, ("Path Size is: %d",gdwSize)); // Unmap the view but close the global handle in DrvEnd UnmapViewOfFile(pdwMappedSize); } return (gdwSize); } // wdmaGetGlobalDeviceInterfaceSize() LPWSTR wdmaGetGlobalDeviceInterface() { LPWSTR pszInterfacePath = NULL; DWORD dwSize; dwSize = wdmaGetGlobalDeviceInterfaceSize(); if (dwSize) { pszInterfacePath = wdmaGetGlobalDeviceInterfacePath(dwSize); } return pszInterfacePath; } LPWSTR wdmaGetDeviceInterface() { LPWSTR pszInterfacePath = NULL; // // Need to check if this already exists // pszInterfacePath = wdmaGetGlobalDeviceInterface(); if (!pszInterfacePath) { // // We haven't found it yet so get ready to call setupapi. // pszInterfacePath = wdmaGetGlobalDeviceInterfaceViaSetupAPI(); } return pszInterfacePath; } HANDLE wdmaOpenKernelDevice() { HANDLE hDevice = INVALID_HANDLE_VALUE; LPWSTR pszInterfacePath = NULL; pszInterfacePath = wdmaGetDeviceInterface(); if (pszInterfacePath) { // Open the interface hDevice = CreateFile ( pszInterfacePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL ); if((!hDevice) || (INVALID_HANDLE_VALUE == hDevice)) { DPF(DL_WARNING|FA_SETUP, ("CreateFile failed to open %S with error %lu", pszInterfacePath, GetLastError()) ); } } else { DPF(DL_WARNING|FA_SETUP, ("wdmaOpenKernelDevice failed with NULL pathname" )); } return hDevice; } /************************************************************************** @doc EXTERNAL @api void | DrvEnd | Driver cleanup takes place here. @rdesc The return value is TRUE if the initialisation completed ok, FALSE if not. **************************************************************************/ VOID DrvEnd() { if (gpszDeviceInterfacePath) { GlobalFreePtr(gpszDeviceInterfacePath); gpszDeviceInterfacePath = NULL; } if (NULL != ghDevice) { CloseHandle(ghDevice); ghDevice = NULL; } if (NULL != ghMemoryDeviceInterfacePath) { CloseHandle(ghMemoryDeviceInterfacePath); ghMemoryDeviceInterfacePath = NULL; } if (NULL != ghMemorySize) { CloseHandle(ghMemorySize); ghMemorySize = NULL; } if (NULL != gpCallbacks) { UnmapViewOfFile(gpCallbacks); gpCallbacks = NULL; } if (NULL != ghCallbacks) { CloseHandle(ghCallbacks); ghCallbacks = NULL; } if (wdmaudCritSecInit) { wdmaudCritSecInit=FALSE; DeleteCriticalSection(&wdmaudCritSec); } if( NULL != mixercallbackevent ) { DPF(DL_WARNING|FA_ALL,("freeing mixercallbackevent") ); CloseHandle(mixercallbackevent); mixercallbackevent=NULL; } if( NULL != mixerhardwarecallbackevent ) { DPF(DL_WARNING|FA_ALL,("freeing mixerhardwarecallbackevent") ); CloseHandle(mixerhardwarecallbackevent); mixerhardwarecallbackevent=NULL; } if( NULL != mixercallbackthread ) { DPF(DL_WARNING|FA_ALL,("freeing mixercallbackthread") ); CloseHandle(mixercallbackthread); mixercallbackthread=NULL; } return; } /**************************************************************************** * @doc INTERNAL * * @api DWORD | wdmaudGetDevCaps | This function returns the device capabilities * of a WDM driver. * * @parm DWORD | id | Device id * * @parm UINT | DeviceType | type of device * * @parm LPBYTE | lpCaps | Far pointer to a WAVEOUTCAPS structure to * receive the information. * * @parm DWORD | dwSize | Size of the WAVEOUTCAPS structure. * * @rdesc MMSYS.. return code ***************************************************************************/ MMRESULT FAR wdmaudGetDevCaps ( LPDEVICEINFO DeviceInfo, MDEVICECAPSEX FAR* pdc ) { if (pdc->cbSize == 0) return MMSYSERR_NOERROR; // // Make sure that we don't take the critical section // in wdmaudIoControl // DeviceInfo->OpenDone = 0; // // Inject a tag into the devcaps to signify that it is // Unicode // ((LPWAVEOUTCAPS)pdc->pCaps)->wMid = UNICODE_TAG; return wdmaudIoControl(DeviceInfo, pdc->cbSize, pdc->pCaps, IOCTL_WDMAUD_GET_CAPABILITIES); } /************************************************************************** @doc EXTERNAL @api void | wdmaudIoControl | Proxies requests for information to and from wdmaud.sys. This routine is synchronous. @rdesc The return value is TRUE if the initialisation completed ok, FALSE if not. **************************************************************************/ /* Note: wdmaudIoControl calls wdmaud.sys through the DeviceIoControl routine. Take note that if wdmaud.sys returns an error, like STATUS_INVALID_PARAMETER or STATUS_INSUFFICIENT_RESOURCES the output buffer will not get filled! DeviceIoControl will only fill that buffer on STATUS_SUCCESS. Why is this important to know? Well, wdmaud.sys takes advantage of this in order to return specific error codes. In other words, in order for wdmaud.sys to return MIXERR_INVALCONTROL it returns STATUS_SUCCESS with the mmr value of the DeviceInfo structure set to MIXERR_INVALCONTROL. */ MMRESULT FAR wdmaudIoControl ( LPDEVICEINFO DeviceInfo, DWORD dwSize, PVOID pData, ULONG IoCode ) { BOOL fResult; MMRESULT mmr; OVERLAPPED ov; ULONG cbDeviceInfo; ULONG cbReturned; if (NULL == ghDevice) { MMRRETURN( MMSYSERR_NOTENABLED ); } RtlZeroMemory( &ov, sizeof( OVERLAPPED ) ); if (NULL == (ov.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ))) MMRRETURN( MMSYSERR_NOMEM ); // // Only take the critical section if there is an open // wave handle. This is to ensure that the lpWaveQueue // is not modified during the ioctl. Without this // protection the copy at the end of the DeviceIoControl // could copy an old DeviceInfo that is not in sync // with the current DeviceInfo. // if ( (DeviceInfo->DeviceType != MixerDevice) && (DeviceInfo->DeviceType != AuxDevice) && (DeviceInfo->OpenDone == 1) ) CRITENTER ; // // Wrap the data buffer around the device context. // DeviceInfo->DataBuffer = pData; DeviceInfo->DataBufferSize = dwSize; // // Since we are not letting the OS do the user-to-kernel // space mapping, we will have to do the mapping ourselves // for writes to data buffers in wdmaud.sys. // cbDeviceInfo = sizeof(*DeviceInfo) + (lstrlenW(DeviceInfo->wstrDeviceInterface) * sizeof(WCHAR)); fResult = DeviceIoControl( ghDevice, IoCode, DeviceInfo, cbDeviceInfo, DeviceInfo, sizeof(*DeviceInfo), &cbReturned, &ov ); if (!fResult) { if (ERROR_IO_PENDING == GetLastError()) { WaitForSingleObject( ov.hEvent, INFINITE ); } mmr = sndTranslateStatus(); } else { mmr = MMSYSERR_NOERROR; } if ( (DeviceInfo->DeviceType != MixerDevice) && (DeviceInfo->DeviceType != AuxDevice) && (DeviceInfo->OpenDone == 1) ) CRITLEAVE ; CloseHandle( ov.hEvent ); MMRRETURN( mmr ); } /**************************************************************************** * @doc INTERNAL * * @api void | sndTranslateStatus | This function translates an NT status * code into a multimedia error code as far as possible. * * @parm NTSTATUS | Status | The NT base operating system return status. * * @rdesc The multimedia error code. ***************************************************************************/ DWORD sndTranslateStatus() { #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: case ERROR_NO_SYSTEM_RESOURCES: return MMSYSERR_NOMEM; case ERROR_ACCESS_DENIED: return MMSYSERR_BADDEVICEID; case ERROR_INSUFFICIENT_BUFFER: case ERROR_INVALID_PARAMETER: case ERROR_INVALID_USER_BUFFER: return MMSYSERR_INVALPARAM; case ERROR_NOT_READY: case ERROR_GEN_FAILURE: return MMSYSERR_ERROR; case ERROR_FILE_NOT_FOUND: return MMSYSERR_NODRIVER; default: DPF(DL_WARNING|FA_DEVICEIO, ("sndTranslateStatus: LastError = %d", n)); return MMSYSERR_ERROR; } } /**************************************************************************** * @doc INTERNAL * * @api MMRESULT | wdmaudSubmitWaveHeader | Pass a new buffer to the Auxiliary * thread for a wave device. * * @parm LPWAVEALLOC | DeviceInfo | The data associated with the logical wave * device. * * @parm LPWAVEHDR | pHdr | Pointer to a wave buffer * * @rdesc A MMSYS... type return code for the application. * * @comm The buffer flags are set and the buffer is passed to the auxiliary * device task for processing. ***************************************************************************/ MMRESULT wdmaudSubmitWaveHeader ( LPDEVICEINFO DeviceInfo, LPWAVEHDR pHdr ) { LPDEVICEINFO WaveHeaderDeviceInfo; PWAVEPREPAREDATA pWavePrepareData; ULONG cbRead; ULONG cbWritten; ULONG cbDeviceInfo; BOOL fResult; MMRESULT mmr; if (NULL == ghDevice) { MMRRETURN( MMSYSERR_NOTENABLED ); } WaveHeaderDeviceInfo = GlobalAllocDeviceInfo(DeviceInfo->wstrDeviceInterface); if (!WaveHeaderDeviceInfo) { MMRRETURN( MMSYSERR_NOMEM ); } // // Catch the case when an application doesn't prepare headers correctly // if (!pHdr->reserved) { // // This should never happen! wdmaudSubmitWaveHeader is called from // waveWrite which is called from handling the WIDM_ADDBUFFER and // WODM_WRITE messages. On both of these messages, we check that // the header has been prepared! // DPF(DL_ERROR|FA_SYNC,("Unprepared header!") ); GlobalFreeDeviceInfo( WaveHeaderDeviceInfo ); return MMSYSERR_INVALPARAM; } // // Free later in the callback routine // pWavePrepareData = (PWAVEPREPAREDATA)pHdr->reserved; pWavePrepareData->pdi = WaveHeaderDeviceInfo; cbDeviceInfo = sizeof(*WaveHeaderDeviceInfo) + (lstrlenW(WaveHeaderDeviceInfo->wstrDeviceInterface) * sizeof(WCHAR)); // // Fill the wave header's deviceinfo structure // WaveHeaderDeviceInfo->DeviceType = DeviceInfo->DeviceType; WaveHeaderDeviceInfo->DeviceNumber = DeviceInfo->DeviceNumber; WaveHeaderDeviceInfo->DeviceHandle = DeviceInfo->DeviceHandle; WaveHeaderDeviceInfo->DataBuffer = pHdr; WaveHeaderDeviceInfo->DataBufferSize = sizeof( WAVEHDR ); if (WaveInDevice == DeviceInfo->DeviceType) { fResult = DeviceIoControl(ghDevice, IOCTL_WDMAUD_WAVE_IN_READ_PIN, WaveHeaderDeviceInfo, cbDeviceInfo, WaveHeaderDeviceInfo, sizeof(*WaveHeaderDeviceInfo), &cbWritten, pWavePrepareData->pOverlapped); } else // WaveOutDevice { fResult = DeviceIoControl(ghDevice, IOCTL_WDMAUD_WAVE_OUT_WRITE_PIN, WaveHeaderDeviceInfo, cbDeviceInfo, WaveHeaderDeviceInfo, sizeof(*WaveHeaderDeviceInfo), &cbRead, pWavePrepareData->pOverlapped); } mmr = sndTranslateStatus(); if (MMSYSERR_NOERROR == mmr) { mmr = wdmaudCreateCompletionThread ( DeviceInfo ); } return mmr; } /**************************************************************************** * @doc INTERNAL * * @api DWORD | wdmaudSubmitMidiOutHeader | Synchronously process a midi output * buffer. * * @rdesc A MMSYS... type return code for the application. ***************************************************************************/ MMRESULT FAR wdmaudSubmitMidiOutHeader ( LPDEVICEINFO DeviceInfo, LPMIDIHDR pHdr ) { BOOL fResult; MMRESULT mmr; OVERLAPPED ov; ULONG cbReturned; ULONG cbDeviceInfo; if (NULL == ghDevice) { MMRRETURN( MMSYSERR_NOTENABLED ); } RtlZeroMemory( &ov, sizeof( OVERLAPPED ) ); if (NULL == (ov.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ))) return FALSE; cbDeviceInfo = sizeof(*DeviceInfo) + (lstrlenW(DeviceInfo->wstrDeviceInterface) * sizeof(WCHAR)); // // Wrap the data buffer around the device context. // DeviceInfo->DataBuffer = pHdr; DeviceInfo->DataBufferSize = sizeof( MIDIHDR ); // // Since we are not letting the OS do the user-to-kernel // space mapping, we will have to do the mapping ourselves // for writes to data buffers in wdmaud.sys. // fResult = DeviceIoControl( ghDevice, IOCTL_WDMAUD_MIDI_OUT_WRITE_LONGDATA, DeviceInfo, cbDeviceInfo, DeviceInfo, sizeof(*DeviceInfo), &cbReturned, &ov ); if (!fResult) { if (ERROR_IO_PENDING == GetLastError()) { WaitForSingleObject( ov.hEvent, INFINITE ); mmr = MMSYSERR_NOERROR; } else { mmr = sndTranslateStatus(); } } else { mmr = MMSYSERR_NOERROR; } CloseHandle( ov.hEvent ); MMRRETURN( mmr ); } /**************************************************************************** * @doc INTERNAL * * @api MMRESULT | wdmaudGetMidiData | Pass a buffer down to * wdmaud.sys to be filled in with KSMUSICFORMAT data. * * @parm LPDEVICEINFO | DeviceInfo | The data associated with the logical * device. * * @rdesc A MMSYS... type return code for the application. ***************************************************************************/ MMRESULT wdmaudGetMidiData ( LPDEVICEINFO DeviceInfo, LPMIDIDATALISTENTRY pOldMidiDataListEntry ) { LPDEVICEINFO MidiDataDeviceInfo; ULONG cbWritten; ULONG cbDeviceInfo; LPMIDIDATALISTENTRY pMidiDataListEntry; LPMIDIDATALISTENTRY pTemp; MMRESULT mmr; if (NULL == ghDevice) { MMRRETURN( MMSYSERR_NOTENABLED ); } // // Don't need to allocate another buffer and create another // event if we can reuse the old one // if (pOldMidiDataListEntry) { // // Make sure to pull it off the front of the queue // before adding again // CRITENTER ; DeviceInfo->DeviceState->lpMidiDataQueue = DeviceInfo->DeviceState->lpMidiDataQueue->lpNext; CRITLEAVE ; pMidiDataListEntry = pOldMidiDataListEntry; // RtlZeroMemory( &pMidiDataListEntry->MidiData, sizeof(MIDIDATA) ); // ResetEvent( ((LPOVERLAPPED)(pMidiDataListEntry->pOverlapped))->hEvent ); } else { // // Allocate a buffer to receive the music data // pMidiDataListEntry = (LPMIDIDATALISTENTRY) GlobalAllocPtr( GPTR, sizeof(MIDIDATALISTENTRY)); if (NULL == pMidiDataListEntry) { MMRRETURN( MMSYSERR_NOMEM ); } #ifdef DEBUG pMidiDataListEntry->dwSig=MIDIDATALISTENTRY_SIGNATURE; #endif pMidiDataListEntry->MidiDataDeviceInfo = GlobalAllocDeviceInfo(DeviceInfo->wstrDeviceInterface); if (!pMidiDataListEntry->MidiDataDeviceInfo) { GlobalFreePtr(pMidiDataListEntry); MMRRETURN( MMSYSERR_NOMEM ); } // // Initialize music data structure // pMidiDataListEntry->pOverlapped = (LPOVERLAPPED)HeapAlloc( GetProcessHeap(), 0, sizeof( OVERLAPPED )); if (NULL == pMidiDataListEntry->pOverlapped) { GlobalFreePtr(pMidiDataListEntry->MidiDataDeviceInfo ); GlobalFreePtr(pMidiDataListEntry); MMRRETURN( MMSYSERR_NOMEM ); } RtlZeroMemory( pMidiDataListEntry->pOverlapped, sizeof( OVERLAPPED ) ); if (NULL == ( ((LPOVERLAPPED)(pMidiDataListEntry->pOverlapped))->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ))) { HeapFree( GetProcessHeap(), 0, pMidiDataListEntry->pOverlapped); GlobalFreePtr(pMidiDataListEntry->MidiDataDeviceInfo ); GlobalFreePtr(pMidiDataListEntry); MMRRETURN( MMSYSERR_NOMEM ); } } // // Cauterize the next pointer for new and old list entries // pMidiDataListEntry->lpNext = NULL; // // Add music data structure to a queue // CRITENTER ; if (!DeviceInfo->DeviceState->lpMidiDataQueue) { DeviceInfo->DeviceState->lpMidiDataQueue = pMidiDataListEntry; pTemp = NULL; #ifdef UNDER_NT if( (DeviceInfo->DeviceState->hevtQueue) && (DeviceInfo->DeviceState->hevtQueue != (HANDLE)FOURTYTHREE) && (DeviceInfo->DeviceState->hevtQueue != (HANDLE)FOURTYTWO) ) { DPF(DL_TRACE|FA_MIDI,("SetEvent on hevtQueue") ); SetEvent( DeviceInfo->DeviceState->hevtQueue ); } #endif } else { for (pTemp = DeviceInfo->DeviceState->lpMidiDataQueue; pTemp->lpNext != NULL; pTemp = pTemp->lpNext); pTemp->lpNext = pMidiDataListEntry; } CRITLEAVE ; DPF(DL_TRACE|FA_MIDI, ("MidiData submitted: pMidiDataListEntry = 0x%08lx", pMidiDataListEntry) ); MidiDataDeviceInfo = pMidiDataListEntry->MidiDataDeviceInfo; cbDeviceInfo = sizeof(*MidiDataDeviceInfo) + (lstrlenW(MidiDataDeviceInfo->wstrDeviceInterface) * sizeof(WCHAR)); // // Wrap the data buffer around the device context. // MidiDataDeviceInfo->DeviceType = DeviceInfo->DeviceType; MidiDataDeviceInfo->DeviceNumber = DeviceInfo->DeviceNumber; MidiDataDeviceInfo->DataBuffer = &pMidiDataListEntry->MidiData; MidiDataDeviceInfo->DataBufferSize = sizeof( MIDIDATA ); // // Send this buffer down to wdmaud.sys to fill in data // DeviceIoControl(ghDevice, IOCTL_WDMAUD_MIDI_IN_READ_PIN, MidiDataDeviceInfo, cbDeviceInfo, MidiDataDeviceInfo, sizeof(*MidiDataDeviceInfo), &cbWritten, pMidiDataListEntry->pOverlapped); mmr = sndTranslateStatus(); // // Make sure that the completion thread is running // if (MMSYSERR_NOERROR == mmr) { mmr = wdmaudCreateCompletionThread ( DeviceInfo ); } else { // Unlink... CloseHandle( ((LPOVERLAPPED)(pMidiDataListEntry->pOverlapped))->hEvent ); HeapFree( GetProcessHeap(), 0, pMidiDataListEntry->pOverlapped); GlobalFreePtr( MidiDataDeviceInfo ); GlobalFreePtr( pMidiDataListEntry ); if (pTemp) { pTemp->lpNext = NULL; } else { DeviceInfo->DeviceState->lpMidiDataQueue = NULL; } } MMRRETURN( mmr ); } /**************************************************************************** * @doc INTERNAL * * @api MMRESULT | wdmaudCreateCompletionThread | * * @rdesc A MMSYS... type return code for the application. * ***************************************************************************/ MMRESULT wdmaudCreateCompletionThread ( LPDEVICEINFO DeviceInfo ) { PTHREAD_START_ROUTINE fpThreadRoutine; DPFASSERT(DeviceInfo->DeviceType == WaveOutDevice || DeviceInfo->DeviceType == WaveInDevice || DeviceInfo->DeviceType == MidiInDevice); // // Thread already created so...forget about it. // if (DeviceInfo->DeviceState->fThreadRunning) { ISVALIDDEVICESTATE(DeviceInfo->DeviceState,TRUE); return MMSYSERR_NOERROR; } // // Pick which thread routine we want to create // if (WaveInDevice == DeviceInfo->DeviceType || WaveOutDevice == DeviceInfo->DeviceType) { fpThreadRoutine = (PTHREAD_START_ROUTINE)waveThread; } else if (MidiInDevice == DeviceInfo->DeviceType) { fpThreadRoutine = (PTHREAD_START_ROUTINE)midThread; } else { MMRRETURN( MMSYSERR_ERROR ); } // // Is there a problem with hThread? Well, here is where it gets set // to a non-zero value. Basically, during this creation process, we // look to see if there is already a work item scheduled on this thread. if // not, we create one and schedule it. // // But, between the point where we check this value and the point where it // gets set // if (NULL == DeviceInfo->DeviceState->hThread) { #ifdef DEBUG if( (DeviceInfo->DeviceState->hevtQueue != NULL) && (DeviceInfo->DeviceState->hevtQueue != (HANDLE)FOURTYTHREE) && (DeviceInfo->DeviceState->hevtQueue != (HANDLE)FOURTYTWO) ) { DPF(DL_ERROR|FA_ALL,("hevtQueue getting overwritten! %08X",DeviceInfo) ); } #endif DeviceInfo->DeviceState->hevtQueue = CreateEvent( NULL, // no security FALSE, // auto reset FALSE, // initially not signalled NULL ); // unnamed #ifdef DEBUG if( (DeviceInfo->DeviceState->hevtExitThread != NULL) && (DeviceInfo->DeviceState->hevtExitThread != (HANDLE)FOURTYEIGHT) ) { DPF(DL_ERROR|FA_ALL,("hevtExitThread getting overwritten %08X",DeviceInfo) ); } #endif DeviceInfo->DeviceState->hevtExitThread = CreateEvent( NULL, // no security FALSE, // auto reset FALSE, // initially not signalled NULL ); // unnamed DPFASSERT(NULL == DeviceInfo->DeviceState->hThread); DPF(DL_TRACE|FA_SYNC,("Creating Completion Thread") ); DeviceInfo->DeviceState->hThread = CreateThread( NULL, // no security 0, // default stack (PTHREAD_START_ROUTINE) fpThreadRoutine, (PVOID) DeviceInfo, // parameter 0, // default create flags &DeviceInfo->DeviceState->dwThreadId ); // container for // thread id // // TODO: I need to wait for the thread to actually start // before I can move on // if (DeviceInfo->DeviceState->hThread) SetThreadPriority(DeviceInfo->DeviceState->hThread, THREAD_PRIORITY_TIME_CRITICAL); } if (NULL == DeviceInfo->DeviceState->hThread) { if (DeviceInfo->DeviceState->hevtQueue) { CloseHandle( DeviceInfo->DeviceState->hevtQueue ); DeviceInfo->DeviceState->hevtQueue = NULL; CloseHandle( DeviceInfo->DeviceState->hevtExitThread ); DeviceInfo->DeviceState->hevtExitThread = NULL; } MMRRETURN( MMSYSERR_ERROR ); } InterlockedExchange( (LPLONG)&DeviceInfo->DeviceState->fThreadRunning, TRUE ); return MMSYSERR_NOERROR; } /**************************************************************************** * @doc INTERNAL * * @api MMRESULT | wdmaudDestroyCompletionThread | * * @rdesc A MMSYS... type return code for the application. * ***************************************************************************/ MMRESULT wdmaudDestroyCompletionThread ( LPDEVICEINFO DeviceInfo ) { MMRESULT mmr; if( (mmr=IsValidDeviceInfo(DeviceInfo)) != MMSYSERR_NOERROR ) { MMRRETURN( mmr ); } CRITENTER; if( DeviceInfo->DeviceState->hThread ) { ISVALIDDEVICESTATE(DeviceInfo->DeviceState,FALSE); InterlockedExchange( (LPLONG)&DeviceInfo->DeviceState->fExit, TRUE ); // // If the thread handling the completion notifications, waveThread and // midThread have completed, then hevtQueue will be invalid. We don't // want to call SetEvent with invalid info. Also, if the thread has // completed, then we know that hevtExitThread will have been signaled and // fThreadRunning will be FALSE. // if( DeviceInfo->DeviceState->fThreadRunning ) { ISVALIDDEVICESTATE(DeviceInfo->DeviceState,TRUE); SetEvent( DeviceInfo->DeviceState->hevtQueue ); } CRITLEAVE; // // Ok, here we're going to wait until that routine below, waveThread // completes and signals us. // DPF(DL_TRACE|FA_SYNC, ("DestroyThread: Waiting for thread to go away") ); WaitForSingleObject( DeviceInfo->DeviceState->hevtExitThread, INFINITE ); DPF(DL_TRACE|FA_SYNC, ("DestroyThread: Done waiting for thread to go away") ); CRITENTER; CloseHandle( DeviceInfo->DeviceState->hThread ); DeviceInfo->DeviceState->hThread = NULL; CloseHandle( DeviceInfo->DeviceState->hevtExitThread ); DeviceInfo->DeviceState->hevtExitThread = (HANDLE)FOURTYEIGHT; //NULL; } InterlockedExchange( (LPLONG)&DeviceInfo->DeviceState->fExit, FALSE ); ISVALIDDEVICEINFO(DeviceInfo); ISVALIDDEVICESTATE(DeviceInfo->DeviceState,FALSE); CRITLEAVE; return MMSYSERR_NOERROR; } /**************************************************************************** * @doc INTERNAL * * @api DWORD | waveThread | * ***************************************************************************/ DWORD waveThread ( LPDEVICEINFO DeviceInfo ) { BOOL fDone; LPWAVEHDR pWaveHdr; MMRESULT mmr; // // Keep looping until all notifications are posted... // fDone = FALSE; while (!fDone ) { fDone = FALSE; CRITENTER ; ISVALIDDEVICEINFO(DeviceInfo); ISVALIDDEVICESTATE(DeviceInfo->DeviceState,TRUE); if(pWaveHdr = DeviceInfo->DeviceState->lpWaveQueue) { PWAVEPREPAREDATA pWavePrepareData; HANDLE hEvent; if( (mmr=IsValidWaveHeader(pWaveHdr)) == MMSYSERR_NOERROR ) { pWavePrepareData = (PWAVEPREPAREDATA)pWaveHdr->reserved; if( (mmr=IsValidPrepareWaveHeader(pWavePrepareData)) == MMSYSERR_NOERROR ) { hEvent = pWavePrepareData->pOverlapped->hEvent; CRITLEAVE ; WaitForSingleObject( hEvent, INFINITE ); CRITENTER ; // // Validate that our data is still intact // if( ( (mmr=IsValidDeviceInfo(DeviceInfo)) ==MMSYSERR_NOERROR ) && ( (mmr=IsValidDeviceState(DeviceInfo->DeviceState,TRUE)) == MMSYSERR_NOERROR ) ) { DPF(DL_TRACE|FA_WAVE, ("Calling waveCompleteHeader") ); waveCompleteHeader(DeviceInfo); } else { // // Problem: Major structures have changed. How can we complete // this header? The only thing I can think of here is to // terminate the thread. // goto Terminate_waveThread; } } else { // // Problem: reserved field that contains the Prepare data info // is corrupt, thus we will not have a valid hEvent to wait on. // remove this header and go on to the next. // DeviceInfo->DeviceState->lpWaveQueue = DeviceInfo->DeviceState->lpWaveQueue->lpNext; } } else { // // Problem: Our header is corrupt. We can't possibly wait on this // because we'll never get signaled! thus we will not have a valid // hEvent to wait on. Remove this header and go on to the next. // DeviceInfo->DeviceState->lpWaveQueue = DeviceInfo->DeviceState->lpWaveQueue->lpNext; } CRITLEAVE ; } else { // fDone = TRUE; if (DeviceInfo->DeviceState->fRunning) { wdmaudIoControl(DeviceInfo, 0, NULL, DeviceInfo->DeviceType == WaveOutDevice ? IOCTL_WDMAUD_WAVE_OUT_PAUSE : IOCTL_WDMAUD_WAVE_IN_STOP); InterlockedExchange( (LPLONG)&DeviceInfo->DeviceState->fRunning, FALSE ); } CRITLEAVE ; WaitForSingleObject( DeviceInfo->DeviceState->hevtQueue, INFINITE ); // // We could have been here for two reasons 1) the thread got starved // ie. the header list went empty or 2) we're done with the headers. // Only when we're done do we really want to exit this thread. // if( DeviceInfo->DeviceState->fExit ) { fDone = TRUE; } } } CRITENTER; ISVALIDDEVICEINFO(DeviceInfo); ISVALIDDEVICESTATE(DeviceInfo->DeviceState,TRUE); CloseHandle( DeviceInfo->DeviceState->hevtQueue ); DeviceInfo->DeviceState->hevtQueue = (HANDLE)FOURTYTWO; // WAS NULL InterlockedExchange( (LPLONG)&DeviceInfo->DeviceState->fThreadRunning, FALSE ); SetEvent( DeviceInfo->DeviceState->hevtExitThread ); DPF(DL_TRACE|FA_WAVE, ("waveThread: Closing") ); Terminate_waveThread: CRITLEAVE; return 0; } /**************************************************************************** * @doc INTERNAL * * @api DWORD | midThread | * ***************************************************************************/ DWORD midThread ( LPDEVICEINFO DeviceInfo ) { BOOL fDone; LPMIDIDATALISTENTRY pMidiDataListEntry; int i; MMRESULT mmr; DPF(DL_TRACE|FA_MIDI, ("Entering") ); // // Keep looping until all notifications are posted... // fDone = FALSE; while (!fDone) { CRITENTER ; ISVALIDDEVICEINFO(DeviceInfo); ISVALIDDEVICESTATE(DeviceInfo->DeviceState,TRUE); if (pMidiDataListEntry = DeviceInfo->DeviceState->lpMidiDataQueue) { HANDLE hEvent; if( (mmr=IsValidMidiDataListEntry(pMidiDataListEntry)) == MMSYSERR_NOERROR) { hEvent = ((LPOVERLAPPED)(pMidiDataListEntry->pOverlapped))->hEvent; DPF(DL_TRACE|FA_MIDI, ("Waiting on pMidiDataListEntry = 0x%08lx", pMidiDataListEntry) ); CRITLEAVE ; WaitForSingleObject( hEvent, INFINITE ); CRITENTER ; DPF(DL_TRACE|FA_MIDI, ("Completed pMidiDataListEntry = 0x%08lx", pMidiDataListEntry) ); if( ((mmr=IsValidDeviceInfo(DeviceInfo)) == MMSYSERR_NOERROR) && ((mmr=IsValidDeviceState(DeviceInfo->DeviceState,TRUE)) == MMSYSERR_NOERROR ) ) { // // Parse and callback clients // wdmaudParseMidiData(DeviceInfo, pMidiDataListEntry); if (DeviceInfo->DeviceState->fExit || !DeviceInfo->DeviceState->fRunning) { // // Unlink from queue and free memory // wdmaudFreeMidiData(DeviceInfo, pMidiDataListEntry); } else { // // Reuse this buffer to read Midi data // wdmaudGetMidiData(DeviceInfo, pMidiDataListEntry); } } else { // // Problem: Our major structure is bad. There is nothing that // we can do, exit and hope for the best. // goto Terminate_midThread; } } else { // // Problem: the pMidiDataListEntry is invalid. We can't use it // so we simply move on to the next one and hope for the best. // DeviceInfo->DeviceState->lpMidiDataQueue = DeviceInfo->DeviceState->lpMidiDataQueue->lpNext; } CRITLEAVE ; } else { fDone = TRUE; CRITLEAVE ; DPF(DL_TRACE|FA_MIDI, ("Waiting for signal to kill thread") ); WaitForSingleObject( DeviceInfo->DeviceState->hevtQueue, INFINITE ); DPF(DL_TRACE|FA_MIDI, ("Done waiting for signal to kill thread") ); } } CRITENTER; ISVALIDDEVICEINFO(DeviceInfo); ISVALIDDEVICESTATE(DeviceInfo->DeviceState,TRUE); CloseHandle( DeviceInfo->DeviceState->hevtQueue ); DeviceInfo->DeviceState->hevtQueue = (HANDLE)FOURTYTHREE; //NULL; InterlockedExchange( (LPLONG)&DeviceInfo->DeviceState->fThreadRunning, FALSE ); SetEvent( DeviceInfo->DeviceState->hevtExitThread ); DPF(DL_TRACE|FA_MIDI, ("Closing") ); Terminate_midThread: CRITLEAVE; return 0; } BOOL IsMidiDataDiscontinuous ( PKSSTREAM_HEADER pHeader ) { DPFASSERT(pHeader); // // Check the OptionFlags for the end of the midi stream // return (pHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY); } ULONG GetStreamHeaderSize ( PKSSTREAM_HEADER pHeader ) { DPFASSERT(pHeader); // // Check the OptionFlags for the end of the midi stream // return (pHeader->DataUsed); } BOOL IsSysExData ( LPBYTE MusicData ) { DPFASSERT(MusicData); return ( IS_SYSEX(*MusicData) || IS_EOX(*MusicData) || IS_DATA_BYTE(*MusicData) ); } BOOL IsEndofSysEx ( LPBYTE MusicData ) { DPFASSERT(MusicData); return IS_EOX(*(MusicData)); } void wdmaudParseSysExData ( LPDEVICEINFO DeviceInfo, LPMIDIDATA pMidiData, BOOL MidiDataDiscontinuous ) { BOOL fCompleteSysEx = FALSE; LPMIDIHDR pMidiInHdr; PKSMUSICFORMAT MusicFormat; ULONG MusicDataLeft; LPBYTE MusicData; ULONG RunningTimeMs; ULONG DataCopySize; ULONG HeaderFreeSpace; ULONG MusicFormatDataLeft; ULONG MusicFormatDataPosition = 0; // // Easier to use locals // MusicFormat = (PKSMUSICFORMAT)&pMidiData->MusicFormat; MusicData = (LPBYTE)pMidiData->MusicData; MusicDataLeft = pMidiData->StreamHeader.DataUsed; RunningTimeMs = 0; if ( MidiDataDiscontinuous || IsEndofSysEx(MusicData + MusicFormat->ByteCount - 1) ) { fCompleteSysEx = TRUE; } while (MusicDataLeft || MidiDataDiscontinuous) { // // update the running time for this Music Format header // if (MusicFormat->ByteCount == 0) { RunningTimeMs = DeviceInfo->DeviceState->LastTimeMs; } else { RunningTimeMs += MusicFormat->TimeDeltaMs; DeviceInfo->DeviceState->LastTimeMs = RunningTimeMs; } // // Get the next header from the queue // pMidiInHdr = DeviceInfo->DeviceState->lpMidiInQueue; while (pMidiInHdr && MusicFormatDataPosition <= MusicFormat->ByteCount) { HeaderFreeSpace = pMidiInHdr->dwBufferLength - pMidiInHdr->dwBytesRecorded; MusicFormatDataLeft = MusicFormat->ByteCount - MusicFormatDataPosition; // // Compute the size of the copy // DataCopySize = min(HeaderFreeSpace,MusicFormatDataLeft); // // Fill this, baby // if (DataCopySize) { RtlCopyMemory(pMidiInHdr->lpData + pMidiInHdr->dwBytesRecorded, MusicData + MusicFormatDataPosition, DataCopySize); } // // update the number of bytes recorded // pMidiInHdr->dwBytesRecorded += DataCopySize; MusicFormatDataPosition += DataCopySize; DPF(DL_TRACE|FA_RECORD, ("Record SysEx: %d(%d) Data=0x%08lx", DataCopySize, pMidiInHdr->dwBytesRecorded, *MusicData) ); // // If the buffer is full or end-of-sysex byte is received, // the buffer is marked as 'done' and it's owner is called back. // if ( (fCompleteSysEx && pMidiInHdr->dwBytesRecorded && (MusicFormatDataPosition == MusicFormat->ByteCount) ) // copied whole SysEx || (pMidiInHdr->dwBufferLength == pMidiInHdr->dwBytesRecorded) ) // filled entire buffer { if (MidiDataDiscontinuous) { midiInCompleteHeader(DeviceInfo, RunningTimeMs, MIM_LONGERROR); } else { midiInCompleteHeader(DeviceInfo, RunningTimeMs, MIM_LONGDATA); } // // Grab the next header to fill, if it exists // pMidiInHdr = DeviceInfo->DeviceState->lpMidiInQueue; } // // Break out of loop when all of the data is copied // if (MusicFormatDataPosition == MusicFormat->ByteCount) { break; } // // in the middle of a sysex and we still // have room left in the header // } // while we have more headers and data to copy // // don't continue messin' with this irp // if (MidiDataDiscontinuous) { break; } MusicDataLeft -= sizeof(KSMUSICFORMAT) + ((MusicFormat->ByteCount + 3) & ~3); MusicFormat = (PKSMUSICFORMAT)(MusicData + ((MusicFormat->ByteCount + 3) & ~3)); MusicData = (LPBYTE)(MusicFormat + 1); } // while IrpDataLeft return; } void wdmaudParseShortMidiData ( LPDEVICEINFO DeviceInfo, LPMIDIDATA pMidiData, BOOL MidiDataDiscontinuous ) { BOOL fCompleteSysEx = FALSE; LPMIDIHDR pMidiInHdr; PKSMUSICFORMAT MusicFormat; ULONG MusicDataLeft; LPBYTE MusicData; ULONG RunningTimeMs; ULONG DataCopySize; ULONG HeaderFreeSpace; ULONG MusicFormatDataLeft; ULONG MusicFormatDataPosition = 0; // // Easier to use locals // MusicFormat = (PKSMUSICFORMAT)&pMidiData->MusicFormat; MusicData = (LPBYTE)pMidiData->MusicData; MusicDataLeft = pMidiData->StreamHeader.DataUsed; RunningTimeMs = 0; while (MusicDataLeft || MidiDataDiscontinuous) { // // update the running time for this Music Format header // if (MusicFormat->ByteCount == 0) { RunningTimeMs = DeviceInfo->DeviceState->LastTimeMs; } else { RunningTimeMs += MusicFormat->TimeDeltaMs; DeviceInfo->DeviceState->LastTimeMs = RunningTimeMs; } // // Non-used bytes should be zero'ed out // midiCallback(DeviceInfo, MIM_DATA, *((LPDWORD)MusicData), RunningTimeMs); // // don't continue messin' with this irp // if (MidiDataDiscontinuous) { break; } MusicDataLeft -= sizeof(KSMUSICFORMAT) + ((MusicFormat->ByteCount + 3) & ~3); MusicFormat = (PKSMUSICFORMAT)(MusicData + ((MusicFormat->ByteCount + 3) & ~3)); MusicData = (LPBYTE)(MusicFormat + 1); } // while IrpDataLeft return; } /**************************************************************************** * @doc INTERNAL * * @api VOID | wdmaudParseMidiData | This routine takes the MIDI data retrieved * from kernel mode and calls back the application with the long or short * messages packed in the buffer. * * @parm LPDEVICEINFO | DeviceInfo | The data associated with the logical midi * device. * * @comm The buffer flags are set and the buffer is passed to the auxiliary * device task for processing. ****************************************************************************/ void wdmaudParseMidiData ( LPDEVICEINFO DeviceInfo, LPMIDIDATALISTENTRY pMidiDataListEntry ) { BOOL MidiDataDiscontinuous; ULONG DataRemaining; ULONG BytesUsed; MMRESULT mmr; if( (mmr=IsValidMidiDataListEntry(pMidiDataListEntry)) == MMSYSERR_NOERROR ) { DataRemaining = GetStreamHeaderSize(&pMidiDataListEntry->MidiData.StreamHeader); MidiDataDiscontinuous = IsMidiDataDiscontinuous(&pMidiDataListEntry->MidiData.StreamHeader); if ( IsSysExData((LPBYTE)pMidiDataListEntry->MidiData.MusicData) ) { wdmaudParseSysExData(DeviceInfo, &pMidiDataListEntry->MidiData, MidiDataDiscontinuous); } else { // Must be short messages wdmaudParseShortMidiData(DeviceInfo, &pMidiDataListEntry->MidiData, MidiDataDiscontinuous); } } } /**************************************************************************** * @doc INTERNAL * * @api VOID | wdmaudFreeMidiData | This routine unlinks and free the MIDI * data structure pointed to on input. * * @parm LPDEVICEINFO | DeviceInfo | The data associated with the logical midi * device. * * @parm LPMIDIDATA | pMidiData | The data buffer to be cleaned up * ****************************************************************************/ void wdmaudFreeMidiData ( LPDEVICEINFO DeviceInfo, LPMIDIDATALISTENTRY pMidiDataListEntry ) { // // Advance the head of the queue // DeviceInfo->DeviceState->lpMidiDataQueue = DeviceInfo->DeviceState->lpMidiDataQueue->lpNext; // // Free all associated data members // CloseHandle( ((LPOVERLAPPED)(pMidiDataListEntry->pOverlapped))->hEvent ); HeapFree( GetProcessHeap(), 0, pMidiDataListEntry->pOverlapped ); GlobalFreeDeviceInfo( pMidiDataListEntry->MidiDataDeviceInfo ); GlobalFreePtr( pMidiDataListEntry ); } /**************************************************************************** * @doc INTERNAL * * @api MMRESULT | wdmaudFreeMidiQ | * ***************************************************************************/ MMRESULT wdmaudFreeMidiQ ( LPDEVICEINFO DeviceInfo ) { LPMIDIHDR pHdr; LPMIDIHDR pTemp; DPF(DL_TRACE|FA_MIDI, ("entering") ); CRITENTER ; // // Grab the head of the MIDI In queue and iterate through // completing the headers // pHdr = DeviceInfo->DeviceState->lpMidiInQueue; DeviceInfo->DeviceState->lpMidiInQueue = NULL ; // mark the queue as empty while (pHdr) { pTemp = pHdr->lpNext; pHdr->dwFlags &= ~MHDR_INQUEUE ; pHdr->dwFlags |= MHDR_DONE ; pHdr->dwBytesRecorded = 0; // // Invoke the callback function // midiCallback(DeviceInfo, MIM_LONGDATA, (DWORD_PTR)pHdr, DeviceInfo->DeviceState->LastTimeMs); // NOTE: This is not precise, but there is no way to // know what the kernel time is without defining // a new interface just for this. pHdr = pTemp; } CRITLEAVE ; return MMSYSERR_NOERROR; }