2407 lines
70 KiB
C
2407 lines
70 KiB
C
/****************************************************************************
|
|
*
|
|
* 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;
|
|
}
|
|
|