windows-nt/Source/XPSP1/NT/drivers/wdm/audio/legacy/wdmaud.drv/wdmaud32.c

2407 lines
70 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************
*
* 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;
}