summaryrefslogtreecommitdiff
path: root/audio/tests/PinResourceHelpers
diff options
context:
space:
mode:
authorDaniel Rugerio <[email protected]>2021-08-13 15:05:12 -0700
committerGitHub <[email protected]>2021-08-13 15:05:12 -0700
commit7895dd22785ddba5e973662ed942be3b3452b89d (patch)
tree5c4572d5bfd076d3776a88c5757e37f3ed50c0e7 /audio/tests/PinResourceHelpers
parentdf47b2d284558fa9aacd19257153037e4ebba60e (diff)
audio: Updates for Windows 10 22000 (#655)
* Updates for Windows 10 22000. * Change how WIL is consumed.
Diffstat (limited to 'audio/tests/PinResourceHelpers')
-rw-r--r--audio/tests/PinResourceHelpers/HalfApp.cpp2
-rw-r--r--audio/tests/PinResourceHelpers/HalfApp.h233
-rw-r--r--audio/tests/PinResourceHelpers/PreComp.h2
-rw-r--r--audio/tests/PinResourceHelpers/PropertyHelper.cpp486
-rw-r--r--audio/tests/PinResourceHelpers/PropertyHelper.h260
-rw-r--r--audio/tests/PinResourceHelpers/TestResource.cpp9
-rw-r--r--audio/tests/PinResourceHelpers/TestResource.h68
-rw-r--r--audio/tests/PinResourceHelpers/TestResourceBuild.cpp562
-rw-r--r--audio/tests/PinResourceHelpers/TestResourceHelper.h11
9 files changed, 1623 insertions, 10 deletions
diff --git a/audio/tests/PinResourceHelpers/HalfApp.cpp b/audio/tests/PinResourceHelpers/HalfApp.cpp
index 83c9c8f3..154c1e9a 100644
--- a/audio/tests/PinResourceHelpers/HalfApp.cpp
+++ b/audio/tests/PinResourceHelpers/HalfApp.cpp
@@ -1,6 +1,6 @@
// ------------------------------------------------------------------------------
//
-// Copyright (C) Microsoft. All rights reserved.
+// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Module Name:
//
diff --git a/audio/tests/PinResourceHelpers/HalfApp.h b/audio/tests/PinResourceHelpers/HalfApp.h
new file mode 100644
index 00000000..a30bdf50
--- /dev/null
+++ b/audio/tests/PinResourceHelpers/HalfApp.h
@@ -0,0 +1,233 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// File Name:
+//
+// HalfApp.h
+//
+// Abstract:
+//
+// CHalfApp declarations
+//
+// -------------------------------------------------------------------------------
+#pragma once
+
+#define BUF_LEN_IN_MS 1000 // 1 second
+
+// ------------------------------------------------------------------------------
+// Data flow
+enum STACKWISE_DATAFLOW { render, capture };
+
+
+// ------------------------------------------------------------------------------
+// Structs used to hold results of format records
+typedef struct
+{
+ WAVEFORMATEXTENSIBLE wfxEx;
+ UINT32 fundamentalPeriodInFrames;
+ UINT32 defaultPeriodInFrames;
+ UINT32 minPeriodInFrames;
+ UINT32 maxPeriodInFrames;
+ UINT32 maxPeriodInFramesExtended;
+} FORMAT_RECORD, * PFORMAT_RECORD;
+
+struct DeviceDescriptor
+{
+ IMMDevice* pDevice;
+ LPWSTR pwstrAudioEndpointId;
+ LPWSTR pwstrAudioEndpointFriendlyName;
+ STACKWISE_DATAFLOW dataFlow;
+ EndpointConnectorType eConnectorType;
+ UINT uConnectorId;
+ GUID mode;
+ ULONG cModes;
+ GUID* pModes;
+ ULONG cFormatRecords;
+ PFORMAT_RECORD pFormatRecords;
+ PWAVEFORMATEX pPreferredFormat;
+ UINT32 u32DefaultPeriodicityInFrames;
+ UINT32 u32FundamentalPeriodicityInFrames;
+ UINT32 u32MinPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFrames;
+ bool bIsPortCls;
+ bool bIsAVStream;
+ bool bIsBluetooth;
+ bool bIsSideband;
+ bool bIsMVA;
+};
+
+class CHalfApp
+{
+public:
+ // Basic pin infos
+ wil::com_ptr_nothrow<IMMDevice> m_pDevice;
+ wil::unique_cotaskmem_string m_pwstrDeviceId;
+ wil::unique_cotaskmem_string m_pwstrDeviceFriendlyName;
+ STACKWISE_DATAFLOW m_DataFlow;
+ EndpointConnectorType m_ConnectorType;
+ UINT m_uConnectorId;
+ GUID m_Mode;
+ ULONG m_cModes;
+ GUID* m_pModes; // All processing modes
+ bool m_bIsPortCls;
+ bool m_bIsAVStream;
+ bool m_bIsBluetooth;
+ bool m_bIsSideband;
+ bool m_bIsMVA;
+
+ // Formats
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> m_pPreferredFormat;
+ UINT32 m_u32DefaultPeriodicityInFrames;
+ UINT32 m_u32FundamentalPeriodicityInFrames;
+ UINT32 m_u32MinPeriodicityInFrames;
+ UINT32 m_u32MaxPeriodicityInFrames;
+
+ PFORMAT_RECORD m_pFormatRecords;
+ ULONG m_cFormatRecords;
+
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> m_pCurrentFormat;
+ UINT32 m_u32CurrentDefaultPeriodicityInFrames;
+ UINT32 m_u32CurrentFundamentalPeriodicityInFrames;
+ UINT32 m_u32CurrentMinPeriodicityInFrames;
+ UINT32 m_u32CurrentMaxPeriodicityInFrames;
+
+ // Audio endpoint interfaces
+ wil::com_ptr_nothrow<IAudioDeviceEndpoint> m_pAudioDeviceEndpoint;
+ wil::com_ptr_nothrow<IAudioEndpoint> m_pAudioEndpoint;
+ wil::com_ptr_nothrow<IAudioEndpointControl> m_pAudioEndpointControl;
+ wil::com_ptr_nothrow<IAudioEndpointRT> m_pAudioEndpointRT;
+ wil::com_ptr_nothrow<IAudioClock> m_pAudioClock;
+
+ // Data buffer
+ wil::unique_hlocal_ptr<BYTE> m_pbSineToneDataBuffer;
+ DWORD m_dwSineToneDataBufferSize;
+ DWORD m_dwSineToneDataBufferPosition;
+
+ // Stream
+ HNSTIME m_hnsPeriod = 0;
+ FLOAT32 m_f32EndpointFrameRate = 0.F;
+ HNSTIME m_hnsEndpointLatency = 0;
+ bool m_bIsEventCapable = false;
+ bool m_bStreamInitialized = false;
+ volatile bool m_bStreamThreadTerminate = false;
+ bool m_bYieldActive = false;
+ bool m_bIsBackupTimerRequired = true;
+
+ wil::unique_handle m_hStreamThread;
+
+ LPTHREAD_START_ROUTINE m_pStreamRoutine = nullptr;
+ wil::unique_event_nothrow m_hProcessThreadStartedEvent;
+ wil::unique_event_nothrow m_hTerminate;
+ wil::unique_event_nothrow m_hEndpointBufferCompleteEvent;
+ wil::unique_handle m_hTimer;
+
+ wil::critical_section m_CritSec;
+
+ typedef enum
+ {
+ ET_DO_NOTHING = 0x00,
+ ET_TERMINATE = 0x01,
+ ET_OBJECT_BUFFER_COMPLETE = 0x02,
+ ET_CLIENT_RELEASE_BUFFER = 0x04,
+ ET_TIMER = 0x08,
+ ET_PAUSE_PUMP = 0x10,
+ ET_RESUME_PUMP = 0x20,
+ } TEventType;
+
+ CHalfApp(
+ _In_ DeviceDescriptor descriptor
+ )
+ : m_DataFlow(descriptor.dataFlow)
+ , m_ConnectorType(descriptor.eConnectorType)
+ , m_uConnectorId(descriptor.uConnectorId)
+ , m_cModes(descriptor.cModes)
+ , m_cFormatRecords(descriptor.cFormatRecords)
+ , m_u32DefaultPeriodicityInFrames(descriptor.u32DefaultPeriodicityInFrames)
+ , m_u32FundamentalPeriodicityInFrames(descriptor.u32FundamentalPeriodicityInFrames)
+ , m_u32MinPeriodicityInFrames(descriptor.u32MinPeriodicityInFrames)
+ , m_u32MaxPeriodicityInFrames(descriptor.u32MaxPeriodicityInFrames)
+ , m_u32CurrentDefaultPeriodicityInFrames(descriptor.u32DefaultPeriodicityInFrames)
+ , m_u32CurrentFundamentalPeriodicityInFrames(descriptor.u32FundamentalPeriodicityInFrames)
+ , m_u32CurrentMinPeriodicityInFrames(descriptor.u32MinPeriodicityInFrames)
+ , m_u32CurrentMaxPeriodicityInFrames(descriptor.u32MaxPeriodicityInFrames)
+ , m_bIsPortCls(descriptor.bIsPortCls)
+ , m_bIsAVStream(descriptor.bIsAVStream)
+ , m_bIsBluetooth(descriptor.bIsBluetooth)
+ , m_bIsSideband(descriptor.bIsSideband)
+ , m_bIsMVA(descriptor.bIsMVA)
+ {
+ m_pDevice = descriptor.pDevice;
+ m_pAudioDeviceEndpoint = nullptr;
+ m_pAudioEndpointControl = nullptr;
+
+ m_pwstrDeviceId = wil::make_cotaskmem_string_nothrow(descriptor.pwstrAudioEndpointId, wcslen(descriptor.pwstrAudioEndpointId));
+ m_pwstrDeviceFriendlyName = wil::make_cotaskmem_string_nothrow(descriptor.pwstrAudioEndpointFriendlyName, wcslen(descriptor.pwstrAudioEndpointFriendlyName));
+ m_Mode = descriptor.mode;
+
+ m_pModes = new GUID[descriptor.cModes];
+ memcpy(m_pModes, descriptor.pModes, sizeof(GUID)*(descriptor.cModes));
+ m_pFormatRecords = new FORMAT_RECORD[descriptor.cFormatRecords];
+ memcpy(m_pFormatRecords, descriptor.pFormatRecords, sizeof(FORMAT_RECORD)*(descriptor.cFormatRecords));
+
+ CloneWaveFormat(descriptor.pPreferredFormat, wil::out_param(m_pPreferredFormat));
+ CloneWaveFormat(descriptor.pPreferredFormat, wil::out_param(m_pCurrentFormat)); // Use preferred format as current format
+ }
+
+ ~CHalfApp(void)
+ {
+ if (m_pModes)
+ delete[] m_pModes;
+ if (m_pFormatRecords)
+ delete[] m_pFormatRecords;
+ }
+
+ // Audio endpoint interfaces
+ HRESULT InitializeEndpoint();
+ HRESULT ReleaseEndpoint();
+ HRESULT StartEndpoint();
+ HRESULT StopEndpoint();
+ HRESULT ResetEndpoint();
+
+ // Sine tone data buffer
+ HRESULT CreateSineToneDataBuffer(WAVEFORMATEX* pWfx);
+ HRESULT ReleaseSineToneDataBuffer();
+
+
+
+
+ // Stream
+ HRESULT InitializeAndSetBuffer(HNSTIME hnsPeriod, UINT32 u32LatencyCoefficient);
+ HRESULT InitializeStream(HNSTIME requestedPeriodicity, UINT32 u32LatencyCoefficient);
+ HRESULT CleanupStream();
+ HRESULT StartStream();
+ HRESULT StopStream();
+ static DWORD CALLBACK RenderStreamRoutine(PVOID lpParameter);
+ static DWORD CALLBACK CaptureStreamRoutine(PVOID lpParameter);
+ void SignalAndWaitForThread();
+
+
+
+ HRESULT SetTimer(HANDLE timer, HNSTIME timeDuration, bool fireImmediatley);
+ void CancelTimer(HANDLE timer);
+ HRESULT GetPosition(UINT64* pu64Position, UINT64* pu64hnsQPCPosition);
+
+ // Multiple pin instances
+ HRESULT GetCurrentAvailiablePinInstanceCount(UINT32* pAvailablePinInstanceCount);
+ HRESULT GetSecondHalfApp(AUDIO_SIGNALPROCESSINGMODE secondMode, CHalfApp** ppSecondHalfApp);
+
+ // Only for loopback testcases
+ HRESULT GetHostHalfApp(CHalfApp** ppHostHalfApp);
+};
+
+// {571BF784-A9D6-420A-BCA0-C579A8710236}
+DEFINE_GUID(IID_IHalfAppContainer,
+ 0x571bf784, 0xa9d6, 0x420a, 0xbc, 0xa0, 0xc5, 0x79, 0xa8, 071, 0x02, 0x36);
+
+DECLARE_INTERFACE_IID_(
+IHalfAppContainer,
+IUnknown,
+"571BF784-A9D6-420A-BCA0-C579A8710236")
+{
+ STDMETHOD(GetHalfApp)(THIS_ CHalfApp ** ppHalfApp) PURE;
+};
diff --git a/audio/tests/PinResourceHelpers/PreComp.h b/audio/tests/PinResourceHelpers/PreComp.h
index 85fc4f83..d75f0e3f 100644
--- a/audio/tests/PinResourceHelpers/PreComp.h
+++ b/audio/tests/PinResourceHelpers/PreComp.h
@@ -1,6 +1,6 @@
// ------------------------------------------------------------------------------
//
-// Copyright (C) Microsoft. All rights reserved.
+// Copyright (C) Microsoft Corporation. All rights reserved.
//
// File Name:
//
diff --git a/audio/tests/PinResourceHelpers/PropertyHelper.cpp b/audio/tests/PinResourceHelpers/PropertyHelper.cpp
index 6e85e53e..1d5f4548 100644
--- a/audio/tests/PinResourceHelpers/PropertyHelper.cpp
+++ b/audio/tests/PinResourceHelpers/PropertyHelper.cpp
@@ -1,6 +1,6 @@
// ------------------------------------------------------------------------------
//
-// Copyright (C) Microsoft. All rights reserved.
+// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Module Name:
//
@@ -16,6 +16,11 @@
#include <Functiondiscoverykeys_devpkey.h>
#include <devpkey.h>
#include <PropertyHelper.h>
+#include <imagehlp.h>
+
+using namespace WEX::Common;
+using namespace WEX::Logging;
+using namespace WEX::TestExecution;
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
@@ -1099,6 +1104,355 @@ HRESULT GetAvailiablePinInstanceCount
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
+// GetDriverPathViaService
+//
+// Get the full path to the driver .sys file by the service name
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetDriverPathViaService
+(
+ LPWSTR ServiceName,
+ LPWSTR DriverFullPath,
+ UINT cchFullPath
+)
+{
+ HRESULT hr = S_OK;
+ wil::unique_schandle scManager;
+ wil::unique_schandle scService;
+
+ scManager = wil::unique_schandle(OpenSCManagerW(nullptr, nullptr, GENERIC_READ));
+ if (!VERIFY_IS_NOT_NULL(scManager.get()))
+ {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+
+ scService = wil::unique_schandle(OpenServiceW(scManager.get(), ServiceName, GENERIC_READ));
+ if (!VERIFY_IS_NOT_NULL(scService.get()))
+ {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+
+ DWORD BytesRequired;
+ if (!VERIFY_IS_TRUE(!QueryServiceConfigW(scService.get(), nullptr, 0, &BytesRequired) && ERROR_INSUFFICIENT_BUFFER == GetLastError()))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+
+ wil::unique_cotaskmem_ptr<BYTE> pBuff;
+ pBuff.reset((BYTE*)CoTaskMemAlloc(BytesRequired));
+ if (!VERIFY_IS_NOT_NULL(pBuff))
+ {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+
+ LPQUERY_SERVICE_CONFIGW ServiceConfig = (LPQUERY_SERVICE_CONFIGW)pBuff.get();
+ DWORD BytesReturned;
+
+ if (!VERIFY_IS_TRUE(QueryServiceConfigW(scService.get(), ServiceConfig, BytesRequired, &BytesReturned)))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+
+ if (!VERIFY_IS_NOT_NULL(ServiceConfig->lpBinaryPathName))
+ {
+ hr = E_UNEXPECTED;
+ return hr;
+ }
+
+ if (!VERIFY_IS_TRUE('\0' != ServiceConfig->lpBinaryPathName[0]))
+ {
+ hr = E_UNEXPECTED;
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = GetFullPathFromImagePath(ServiceConfig->lpBinaryPathName, DriverFullPath, cchFullPath)))
+ {
+ return hr;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetFullPathFromImagePath
+//
+// Get the full driver path from the image path
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetFullPathFromImagePath
+(
+ LPWSTR ImagePath,
+ LPWSTR DriverFullPath,
+ UINT cchFullPath
+)
+{
+ HRESULT hr = S_OK;
+ //
+ // First, check if the ImagePath uses either of the well-known kernel
+ // DosDevices prefixes. If so, skip over those first since
+ // GetFileAttributes would succeed, leading us to think it is a valid DOS
+ // path, and GetFullPathName handles these incorrectly anyways. Don't
+ // bother handling the user DosDevices formats since it would be invalid to
+ // specify that format for the ImagePath of a kernel module.
+ //
+ LPWSTR pImagePath = (LPWSTR)ImagePath;
+
+ const wchar_t* DosDevicesPath = L"\\DosDevices\\";
+ const wchar_t* QuestionPath = L"\\??\\";
+ if (_wcsnicmp(pImagePath,
+ DosDevicesPath,
+ static_cast<int>(wcslen(DosDevicesPath))) == 0)
+ {
+ pImagePath += wcslen(DosDevicesPath);
+ }
+ else if (_wcsnicmp(pImagePath,
+ QuestionPath,
+ static_cast<int>(wcslen(QuestionPath))) == 0)
+ {
+ pImagePath += wcslen(QuestionPath);
+ }
+
+ //
+ // Check if the ImagePath happens to be a valid full path.
+ //
+ if (GetFileAttributesW(pImagePath) != 0xFFFFFFFF)
+ {
+ if (!VERIFY_IS_TRUE(GetFullPathNameW(pImagePath, cchFullPath, DriverFullPath, nullptr)))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+ return hr;
+ }
+
+ //
+ // If the ImagePath starts with "\SystemRoot" or "%SystemRoot%" then
+ // remove those values.
+ //
+ LPWSTR pRelativePath = (LPWSTR)pImagePath;
+
+ const wchar_t* SystemRootPath = L"\\SystemRoot\\";
+ const wchar_t* SystemRootVariablePath = L"%SystemRoot%\\";
+
+ if (_wcsnicmp(pRelativePath,
+ SystemRootPath,
+ static_cast<int>(wcslen(SystemRootPath))) == 0)
+ {
+ pRelativePath += wcslen(SystemRootPath);
+ }
+ else if (_wcsnicmp(pRelativePath,
+ SystemRootVariablePath,
+ static_cast<int>(wcslen(SystemRootVariablePath))) == 0)
+ {
+ pRelativePath += wcslen(SystemRootVariablePath);
+ }
+
+ //
+ // At this point pRelativePath should point to the image path relative to
+ // the windows directory.
+ //
+ WCHAR WindowsPath[MAX_PATH];
+ if (!VERIFY_IS_TRUE(GetSystemWindowsDirectoryW(WindowsPath, MAX_PATH)))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = StringCchPrintfW(DriverFullPath, cchFullPath, L"%s\\%s", WindowsPath, pRelativePath)))
+ {
+ return hr;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// CheckImports
+//
+// Check if the driver imports a specific module
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT CheckImports
+(
+ LPWSTR DriverPath,
+ LPSTR ModuleNameToCheck,
+ LPSTR MethodNameToCheck,
+ bool* pIsImported
+)
+{
+ HRESULT hr = S_OK;
+
+ *pIsImported = FALSE;
+
+ CHAR szDriverPath[MAX_PATH];
+ size_t bytesConverted = 0;
+ if (!VERIFY_IS_TRUE(0 == wcstombs_s(&bytesConverted, szDriverPath, MAX_PATH, DriverPath, _TRUNCATE)))
+ {
+ hr = E_FAIL;
+ return hr;
+ }
+
+ LOADED_IMAGE image;
+ //Load the image
+ if (!VERIFY_IS_TRUE(MapAndLoad(szDriverPath, NULL, &image, TRUE, TRUE)))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+
+ auto imageCleanup = wil::scope_exit([&]()
+ {
+ (VOID)UnMapAndLoad(&image);
+ });
+
+ //Get the Import Directory
+ ULONG importDescriptorSize;
+ PIMAGE_SECTION_HEADER sectionHeader;
+ PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx(image.MappedAddress,
+ FALSE,
+ IMAGE_DIRECTORY_ENTRY_IMPORT,
+ &importDescriptorSize,
+ &sectionHeader);
+ if (!VERIFY_IS_NOT_NULL(importDescriptor))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+
+ //Iterate through each directory entry
+ while (!(importDescriptor->Characteristics == 0 &&
+ importDescriptor->TimeDateStamp == 0 &&
+ importDescriptor->ForwarderChain == 0 &&
+ importDescriptor->Name == 0 &&
+ importDescriptor->FirstThunk == 0))
+ {
+ CHAR* str = (PCHAR)ImageRvaToVa(ImageNtHeader(image.MappedAddress),
+ image.MappedAddress,
+ importDescriptor->Name,
+ NULL);
+ if (!str || !*str)
+ {
+ continue;
+ }
+
+ if (_stricmp(str, ModuleNameToCheck) == 0)
+ {
+ // If a specific method name is provided, check for the specific method
+ if (MethodNameToCheck)
+ {
+ PIMAGE_THUNK_DATA thunk;
+ //Iterate through the INT(Import Name Table)
+ if (importDescriptor->OriginalFirstThunk == 0)
+ {
+ thunk = nullptr;
+ }
+ else
+ {
+ thunk = (PIMAGE_THUNK_DATA)ImageRvaToVa(ImageNtHeader(image.MappedAddress),
+ image.MappedAddress,
+ importDescriptor->OriginalFirstThunk,
+ NULL);
+ }
+
+ while (thunk && thunk->u1.Ordinal != 0)
+ {
+ if (!IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal))
+ {
+ PIMAGE_IMPORT_BY_NAME importName = (PIMAGE_IMPORT_BY_NAME)ImageRvaToVa(ImageNtHeader(image.MappedAddress),
+ image.MappedAddress,
+ (ULONG)thunk->u1.ForwarderString,
+ NULL);
+ if (importName && _stricmp(importName->Name, MethodNameToCheck) == 0)
+ {
+ *pIsImported = TRUE;
+ }
+ }
+
+ thunk++;
+ }
+ }
+ else
+ {
+ *pIsImported = TRUE;
+ }
+
+ break;
+ }
+
+ importDescriptor++;
+ }
+
+ return hr;
+
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// IsPortCls
+//
+// Check if audio device is PortCls
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT IsPortCls
+(
+ IMMDevice* pDevice,
+ bool* pIsPortCls
+)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IMMDevice> spDevnodeDevice;
+ wil::com_ptr_nothrow<IPropertyStore> spPnpProperties;
+ wil::unique_prop_variant varDeviceService;
+
+ *pIsPortCls = false;
+
+ // Find the associated PnP device
+ if (!VERIFY_SUCCEEDED(hr = GetPnpDevnodeFromMMDevice(pDevice, &spDevnodeDevice))) {
+ return hr;
+ }
+
+ // Open pnp device property store
+ if (!VERIFY_SUCCEEDED(hr = spDevnodeDevice->OpenPropertyStore(STGM_READ, &spPnpProperties))) {
+ return hr;
+ }
+
+ // Read the DEVPKEY_Device_Service
+ if (!VERIFY_SUCCEEDED(hr = spPnpProperties->GetValue((REFPROPERTYKEY)DEVPKEY_Device_Service, &varDeviceService))) {
+ return hr;
+ }
+
+ // If DEVPKEY_Device_Service is empty, it likely means a raw PDO, which means the device is handled by the parent FDO.
+ // For practical purposes, this would most likely mean a non-PortCls pin, which is what we were trying to determine, so it is OK.
+ if (varDeviceService.vt == VT_EMPTY) {
+ return S_OK;
+ }
+
+ if (!VERIFY_IS_TRUE(varDeviceService.vt == VT_LPWSTR && varDeviceService.pwszVal != nullptr)) {
+ hr = E_FAIL;
+ return hr;
+ }
+
+ WCHAR FullPath[MAX_PATH];
+ if (!VERIFY_SUCCEEDED(hr = GetDriverPathViaService(varDeviceService.pwszVal, FullPath, MAX_PATH))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = CheckImports(FullPath, "portcls.sys", "PcNewPort", pIsPortCls))) {
+ return hr;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
// IsAVStream
//
// Check if audio device is AVStream
@@ -1132,13 +1486,24 @@ HRESULT IsAVStream
return hr;
}
+ // If DEVPKEY_Device_Service is empty, it likely means a raw PDO, which means the device is handled by the parent FDO.
+ // For practical purposes, this would most likely mean a non-AVStream pin, which is what we were trying to determine, so it is OK.
+ if (varDeviceService.vt == VT_EMPTY) {
+ return S_OK;
+ }
+
if (!VERIFY_IS_TRUE(varDeviceService.vt == VT_LPWSTR && varDeviceService.pwszVal != nullptr)) {
hr = E_FAIL;
return hr;
}
- if (0 == _wcsicmp(varDeviceService.pwszVal, L"usbaudio") || 0 == _wcsicmp(varDeviceService.pwszVal, L"BthHFAud") || 0 == _wcsicmp(varDeviceService.pwszVal, L"BthA2dp")) {
- *pIsAVStream = true;
+ WCHAR FullPath[MAX_PATH];
+ if (!VERIFY_SUCCEEDED(hr = GetDriverPathViaService(varDeviceService.pwszVal, FullPath, MAX_PATH))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = CheckImports(FullPath, "ks.sys", nullptr, pIsAVStream))) {
+ return hr;
}
return hr;
@@ -1222,6 +1587,78 @@ HRESULT IsSideband
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
+// IsMVA
+//
+// Check if audio device is side band
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT IsMVA
+(
+ EndpointConnectorType eConnectorType,
+ IMMDevice* pDevice,
+ bool* pIsMVA
+)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IMMDevice> adapterDevice;
+ wil::com_ptr_nothrow<IKsControl> ksControl;
+ ULONG ulReturned = 0;
+ ULONG propertySupportFlags = 0;
+ KSSOUNDDETECTORPROPERTY vam2Property = {0};
+
+ if (!VERIFY_IS_TRUE(pDevice != nullptr)) {
+ hr = E_FAIL;
+ return hr;
+ }
+
+ if (!VERIFY_IS_TRUE(pIsMVA != nullptr)) {
+ hr = E_FAIL;
+ return hr;
+ }
+
+ // If it isn't a keyword connector, it's not MVA, we're done.
+ if (eConnectorType != eKeywordDetectorConnector)
+ {
+ *pIsMVA = FALSE;
+ return S_OK;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &adapterDevice)))
+ {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = adapterDevice->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**) &ksControl))) {
+ return hr;
+ }
+
+ // At this point we know that it is a keyword detector connector and we have the required interface to call into the driver
+ // to see if it's MVA. If it's not MVA, it's SVA, so mark it as SVA here.
+ *pIsMVA = false;
+
+ vam2Property.Property.Set = KSPROPSETID_SoundDetector2;
+ vam2Property.Property.Id = KSPROPERTY_SOUNDDETECTOR_SUPPORTEDPATTERNS;
+ vam2Property.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT;
+ vam2Property.EventId = GUID_NULL;
+
+ hr = ksControl->KsProperty(
+ (PKSPROPERTY)&vam2Property,
+ sizeof(vam2Property),
+ &propertySupportFlags,
+ sizeof(propertySupportFlags),
+ &ulReturned);
+ if (SUCCEEDED(hr) &&
+ propertySupportFlags == (KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT))
+ {
+ // Success, we've identified this as a connector which supports MVA, mark it.
+ *pIsMVA = true;
+ }
+
+ return S_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
// GetPnpDevnodeFromMMDeivce
//
// Get pnp devnode from mm device
@@ -1261,3 +1698,46 @@ HRESULT GetPnpDevnodeFromMMDevice
return hr;
}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// VerifyAllEndpointsPluggedIn
+//
+// Verify there is no unplugged endpoint
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+void VerifyAllEndpointsPluggedIn()
+{
+ wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
+ wil::com_ptr_nothrow<IMMDeviceCollection> spEndpoints;
+ UINT cDevices = 0;
+
+ SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
+ DisableVerifyExceptions disable;
+
+ VERIFY_SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED));
+
+ // Create IMMDevice Enumerator
+ VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator));
+
+ // Enumerate all unplugged endpoints
+ VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_UNPLUGGED, &spEndpoints));
+ VERIFY_SUCCEEDED(spEndpoints->GetCount(&cDevices));
+
+ if (!VERIFY_IS_TRUE(cDevices == 0))
+ {
+ Log::Comment(L"Following unplugged audio device(s) found, please plug in unplugged device(s).");
+
+ for (UINT i = 0; i < cDevices; i++) {
+ wil::com_ptr_nothrow<IMMDevice> spEndpoint;
+ wil::unique_cotaskmem_string id;
+ wil::unique_cotaskmem_string friendlyName;
+
+ VERIFY_SUCCEEDED(spEndpoints->Item(i, &spEndpoint));
+ VERIFY_SUCCEEDED(spEndpoint->GetId(&id));
+ VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName));
+
+ Log::Comment(String().Format(L"Device: %s (%s) is unplugged.", friendlyName.get(), id.get()));
+ }
+ }
+}
diff --git a/audio/tests/PinResourceHelpers/PropertyHelper.h b/audio/tests/PinResourceHelpers/PropertyHelper.h
new file mode 100644
index 00000000..5680283d
--- /dev/null
+++ b/audio/tests/PinResourceHelpers/PropertyHelper.h
@@ -0,0 +1,260 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// Module Name:
+//
+// PropertyHelpers.h
+//
+// Abstract:
+//
+// Header for common helpers/defines of property
+//
+// -------------------------------------------------------------------------------
+#pragma once
+
+#include "HalfApp.h"
+
+#define kSystemDefaultPeriod 100000 // 10 milliseconds
+#define kSystemMinPeriodForNonRTCapableEndpoints 20000 // 2 milliseconds
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// ----------------------------------------------------------------------
+HRESULT GetConnectorId
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ bool* hasConnector,
+ UINT* pConnectorId
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetEndpointFriendlyName
+(
+ IMMDevice* pDevice,
+ LPWSTR* ppwszEndpointName
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetAudioFilterAsDevice
+(
+ IMMDevice* pDevice,
+ IMMDevice** ppAudioFilterAsDevice
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetCachedProcessingModes
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ ULONG *pCount,
+ AUDIO_SIGNALPROCESSINGMODE **ppModes
+);
+
+
+
+
+
+
+
+
+
+
+
+
+// ----------------------------------------------------------------------
+HRESULT GetProcessingModes
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ ULONG *pCount,
+ AUDIO_SIGNALPROCESSINGMODE **ppModes
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetCachedSupportedFormatRecords
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ ULONG *pCount,
+ FORMAT_RECORD **ppFormatRecords
+);
+
+
+
+
+
+
+
+
+
+
+
+
+
+// ----------------------------------------------------------------------
+HRESULT GetSupportedFormatRecords
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ STACKWISE_DATAFLOW dataFlow,
+ ULONG *pCount,
+ FORMAT_RECORD **ppFormatRecords
+);
+
+// ----------------------------------------------------------------------
+HRESULT IsFormatSupported
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ WAVEFORMATEX *pWfx,
+ BOOL* pbSupported
+);
+
+// ----------------------------------------------------------------------
+HRESULT DiscoverPeriodicityCharacteristicsForFormat
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ WAVEFORMATEX *pWfx,
+ STACKWISE_DATAFLOW dataFlow,
+ UINT32 *pDefaultPeriodicityInFrames,
+ UINT32 *pFundamentalPeriodicityInFrames,
+ UINT32 *pMinPeriodicityInFrames,
+ UINT32 *pMaxPeriodicityInFrames,
+ UINT32 *pMaxPeriodicityInFramesExtended
+);
+
+// ----------------------------------------------------------------------
+HRESULT CheckConnectorSupportForPeriodicity
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ WAVEFORMATEX *pWfx,
+ STACKWISE_DATAFLOW dataFlow,
+ HNSTIME RequestedPeriodicity,
+ UINT32 *pActualPeriodicityInFrames
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetCachedDefaultFormat
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ WAVEFORMATEX** ppDefaultFormat
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetProposedFormatForProcessingMode
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ WAVEFORMATEX **ppProposedFormat
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetAvailiablePinInstanceCount
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ UINT32* pAvailablePinInstanceCount
+);
+
+// -------------------------------------------------------------------
+HRESULT GetDriverPathViaService
+(
+ LPWSTR ServiceName,
+ LPWSTR DriverFullPath,
+ UINT cchFullPath
+);
+
+// -------------------------------------------------------------------
+HRESULT GetFullPathFromImagePath
+(
+ LPWSTR ImagePath,
+ LPWSTR DriverFullPath,
+ UINT cchFullPath
+);
+
+// -------------------------------------------------------------------
+HRESULT CheckImports
+(
+ LPWSTR DriverPath,
+ LPSTR ModuleNameToCheck,
+ LPSTR MethodNameToCheck,
+ bool* pIsImported
+);
+
+// ----------------------------------------------------------------------
+HRESULT IsPortCls
+(
+ IMMDevice* pDevice,
+ bool* pIsPortCls
+);
+
+// ----------------------------------------------------------------------
+HRESULT IsAVStream
+(
+ IMMDevice* pDevice,
+ bool* pIsAVStream
+);
+
+// ----------------------------------------------------------------------
+HRESULT IsBluetooth
+(
+ IMMDevice* pDevice,
+ bool* pIsBluetooth
+);
+
+// ----------------------------------------------------------------------
+HRESULT IsSideband
+(
+ IMMDevice* pDevice,
+ bool* pIsSideband
+);
+
+// ----------------------------------------------------------------------
+HRESULT IsMVA
+(
+ EndpointConnectorType eConnectorType,
+ IMMDevice* pDevice,
+ bool* pIsMVA
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetPnpDevnodeFromMMDevice
+(
+ IMMDevice* pDevice,
+ IMMDevice** pDevnodeDevice
+);
diff --git a/audio/tests/PinResourceHelpers/TestResource.cpp b/audio/tests/PinResourceHelpers/TestResource.cpp
index f3762b84..c7e06728 100644
--- a/audio/tests/PinResourceHelpers/TestResource.cpp
+++ b/audio/tests/PinResourceHelpers/TestResource.cpp
@@ -1,6 +1,6 @@
// ------------------------------------------------------------------------------
//
-// Copyright (C) Microsoft. All rights reserved.
+// Copyright (C) Microsoft Corporation. All rights reserved.
//
// File Name:
//
@@ -95,7 +95,7 @@ CPinTestResource::Initialize
}
szMode = ModeName(m_spHalfApp->m_Mode);
- szPin = PinName(m_spHalfApp->m_ConnectorType);
+ szPin = PinName(m_spHalfApp->m_ConnectorType, m_spHalfApp->m_bIsMVA);
// Id: Combine device Id, pin type and mode
m_szId.Attach(W2BSTR(m_spHalfApp->m_pwstrDeviceId.get()));
@@ -300,6 +300,7 @@ static const struct
{ AUDIO_SIGNALPROCESSINGMODE_MEDIA, L"MEDIA" },
{ AUDIO_SIGNALPROCESSINGMODE_MOVIE, L"MOVIE" },
{ AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, L"NOTIFICATION" },
+ { AUDIO_SIGNALPROCESSINGMODE_FAR_FIELD_SPEECH, L"FAR_FIELD_SPEECH" },
};
@@ -313,7 +314,7 @@ LPWSTR CPinTestResource::ModeName(REFGUID guidMode)
return L"UNKNOWN";
}
-LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType)
+LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType, bool IsMVA)
{
switch (eConnectorType)
{
@@ -324,7 +325,7 @@ LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType)
case eLoopbackConnector:
return L"LOOPBACK";
case eKeywordDetectorConnector:
- return L"KEYWORD";
+ return IsMVA?L"MVAKEYWORD":L"SVAKEYWORD";
default:
return L"UNKNOWN";
}
diff --git a/audio/tests/PinResourceHelpers/TestResource.h b/audio/tests/PinResourceHelpers/TestResource.h
new file mode 100644
index 00000000..7bc6a792
--- /dev/null
+++ b/audio/tests/PinResourceHelpers/TestResource.h
@@ -0,0 +1,68 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// File Name:
+//
+// TestResource.h
+//
+// Abstract:
+//
+// TAEF Test Resource
+//
+// -------------------------------------------------------------------------------
+#pragma once
+
+#include "HalfApp.h"
+
+class CPinTestResource :
+ public WEX::TestExecution::ITestResource,
+ public IHalfAppContainer
+{
+public:
+ //IUnknown
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+ STDMETHODIMP QueryInterface(
+ __in REFIID riid,
+ __deref_out VOID **ppvObject
+ );
+ // ITestResouce
+ STDMETHODIMP GetGuid(GUID* pGuid);
+ STDMETHODIMP SetGuid(GUID guid);
+ STDMETHODIMP GetValue(BSTR name, BSTR* pValue);
+ STDMETHODIMP SetValue(BSTR name, BSTR value);
+
+ //IHalfAppContainer
+ STDMETHODIMP GetHalfApp(CHalfApp ** ppHalfApp);
+
+ static HRESULT STDMETHODCALLTYPE CreateInstance(
+ CHalfApp * pHalf,
+ REFGUID guid,
+ WEX::TestExecution::ITestResource ** ppOut
+ );
+
+private:
+
+ CPinTestResource();
+ ~CPinTestResource();
+
+ HRESULT STDMETHODCALLTYPE Initialize(
+ CHalfApp * pHalf,
+ REFGUID guid
+ );
+
+ LPWSTR ModeName(REFGUID guidMode);
+ LPWSTR PinName(EndpointConnectorType eConnectorType, bool IsMVA);
+
+ CComBSTR m_szType;
+ CComBSTR m_szName;
+ CComBSTR m_szId;
+ CComBSTR m_szMode;
+ CComBSTR m_szPin;
+
+ CAutoPtr<CHalfApp> m_spHalfApp;
+
+ ULONG m_cRef;
+ GUID m_guid;
+};
diff --git a/audio/tests/PinResourceHelpers/TestResourceBuild.cpp b/audio/tests/PinResourceHelpers/TestResourceBuild.cpp
new file mode 100644
index 00000000..02c1691d
--- /dev/null
+++ b/audio/tests/PinResourceHelpers/TestResourceBuild.cpp
@@ -0,0 +1,562 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//
+// File Name:
+//
+// TestResourceBuild.cpp
+//
+// Abstract:
+//
+// TAEF BuildResourceList implementation
+//
+// ------------------------------------------------------------------------------
+#include "PreComp.h"
+
+#include <Functiondiscoverykeys_devpkey.h>
+#include <PropertyHelper.h>
+#include <TestResource.h>
+
+using namespace WEX::Common;
+using namespace WEX::Logging;
+using namespace WEX::TestExecution;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// CreateTestResource
+//
+// Create the test resource with MMDevice, device id, device name, data flow, connector type, connector id, mode,
+// list of formats
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT CreateTestResource
+(
+ ResourceList& resourceList,
+ DeviceDescriptor descriptor
+)
+{
+ HRESULT hr = S_OK;
+ CComHeapPtr<CHalfApp> spHalfApp;
+ wil::com_ptr_nothrow<ITestResource> spTestResource;
+ GUID ResourceGUID;
+ CComBSTR szResourceName;
+
+ // Create HalfApp
+ spHalfApp.Attach(new CHalfApp(descriptor));
+ if (!VERIFY_IS_NOT_NULL(spHalfApp)) {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+
+ // Create PinTestResource
+ if (!VERIFY_SUCCEEDED(hr = CoCreateGuid(&ResourceGUID))) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(hr = CPinTestResource::CreateInstance(
+ spHalfApp, ResourceGUID, &spTestResource))) {
+ return hr;
+ }
+
+ // Add to resource list
+ if (!VERIFY_SUCCEEDED(hr = spTestResource->GetValue(CComBSTR(TestResourceProperty::c_szName), &szResourceName))) {
+ return hr;
+ }
+ spHalfApp.Detach();
+ if (!VERIFY_SUCCEEDED(hr = resourceList.Add(spTestResource.get()))) {
+ return hr;
+ }
+ Log::Comment(String().Format(L"Test Resource (%s) added", (PCWSTR)szResourceName));
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetProcessingModesForConnector
+//
+// For host/keyword detector pin, processing mode info is cached in property store and can be directly read. For offload
+// pin, query for ks processing mode property. For loopback pin, it doesn't support any processing modes, return GUID_NULL.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetProcessingModesForConnector
+(
+ IMMDevice* pDevice,
+ UINT uConnectorId,
+ EndpointConnectorType eConnectorType,
+ ULONG *pCount,
+ AUDIO_SIGNALPROCESSINGMODE **ppModes
+)
+{
+ HRESULT hr = S_OK;
+
+ *pCount = 0;
+ *ppModes = NULL;
+
+ if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) {
+ if (!VERIFY_SUCCEEDED(hr = GetCachedProcessingModes(pDevice, eConnectorType, pCount, ppModes))) {
+ return hr;
+ }
+ }
+ else if (eConnectorType == eOffloadConnector) {
+ if (!VERIFY_SUCCEEDED(hr = GetProcessingModes(pDevice, uConnectorId, pCount, ppModes))) {
+ return hr;
+ }
+ }
+ else if (eConnectorType == eLoopbackConnector) {
+ // Loopback pin doesn't support any processing modes, so put 1 GUID_NULL
+ CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE> spModes;
+
+ if (!spModes.Allocate(1))
+ return E_OUTOFMEMORY;
+
+ spModes[0] = GUID_NULL;
+
+ *pCount = 1;
+ *ppModes = spModes.Detach();
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetDefaultFormatForConnector
+//
+// Read audio engine device format from property store as default format.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetDefaultFormatForConnector
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ WAVEFORMATEX **ppDefaultFormat
+)
+{
+ HRESULT hr = S_OK;
+
+ *ppDefaultFormat = NULL;
+ if (!VERIFY_SUCCEEDED(hr = GetCachedDefaultFormat(pDevice, eConnectorType, ppDefaultFormat))){
+ return hr;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetSupportedFormatsForConnector
+//
+// For host/keyword detector pin, supported formats info is cached in property store and can be directly read. For offload
+// pin, provide with a predefined list of formats and check whether format is supported. For loopback pin, it matches the format
+// of host pin so put 0 format record.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetSupportedFormatRecordsForConnector
+(
+ IMMDevice* pDevice,
+ UINT uConnectorId,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ STACKWISE_DATAFLOW dataFlow,
+ ULONG *pCount,
+ FORMAT_RECORD **ppFormatRecords
+)
+{
+ HRESULT hr = S_OK;
+
+ *pCount = 0;
+ *ppFormatRecords = NULL;
+
+ if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) {
+ if (!VERIFY_SUCCEEDED(hr = GetCachedSupportedFormatRecords(pDevice, eConnectorType, mode, pCount, ppFormatRecords))) {
+ return hr;
+ }
+ }
+ else if (eConnectorType == eOffloadConnector) {
+ if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecords(pDevice, uConnectorId, eConnectorType, mode, dataFlow, pCount, ppFormatRecords))) {
+ return hr;
+ }
+ }
+ else if (eConnectorType == eLoopbackConnector) {
+ *pCount = 0;
+ *ppFormatRecords = NULL;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetPreferredFormatForConnector
+//
+// If KSPROPERTY_PIN_PROPOSEDATAFORMAT2 is supported, use the proposed format for mode as preferred format for mode.
+// Otherwise, use default format as preferred format.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetPreferredFormatForConnector
+(
+ IMMDevice* pDevice,
+ UINT uConnectorId,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ WAVEFORMATEX **ppPreferredFormat
+)
+{
+ HRESULT hr = S_OK;
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> pDefaultFormat;
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> pProposedFormat;
+
+ *ppPreferredFormat = NULL;
+
+
+ // Get default format for connector
+ if (!VERIFY_SUCCEEDED(hr = GetDefaultFormatForConnector(pDevice, eConnectorType, wil::out_param(pDefaultFormat)))) {
+ return hr;
+ }
+
+ // Return the processing mode specific format proposed by the driver
+ hr = GetProposedFormatForProcessingMode(pDevice, uConnectorId, mode, wil::out_param(pProposedFormat));
+ if (hr == S_OK) {
+ CloneWaveFormat(pProposedFormat.get(), ppPreferredFormat);
+ }
+ else {
+ CloneWaveFormat(pDefaultFormat.get(), ppPreferredFormat);
+ hr = S_OK;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetPreferredFormatPeriodicityCharacteristicsForConnector
+//
+// Get periodicity characteristics for format. For host/keyword detector pin, periodicity info is cached along with format
+// in property store and can be searched from supported format list. For offload pin, it should be calculate from
+// DiscoverPeriodicityCharacteristicsForFormat.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetPreferredFormatPeriodicityCharacteristicsForConnector
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ STACKWISE_DATAFLOW dataFlow,
+ WAVEFORMATEX *pPreferredFormat,
+ ULONG cFormatRecords,
+ PFORMAT_RECORD pFormatRecords,
+ UINT32 *pDefaultPeriodicityInFrames,
+ UINT32 *pFundamentalPeriodicityInFrames,
+ UINT32 *pMinPeriodicityInFrames,
+ UINT32 *pMaxPeriodicityInFrames,
+ UINT32 *pMaxPeriodicityInFramesExtended
+)
+{
+ HRESULT hr = S_OK;
+ bool bFormatInList = false;
+
+ for (ULONG i = 0; i < cFormatRecords; i++) {
+ if (CompareWaveFormat((WAVEFORMATEX *)&pFormatRecords[i].wfxEx, pPreferredFormat)) {
+ bFormatInList = true;
+ *pDefaultPeriodicityInFrames = pFormatRecords[i].defaultPeriodInFrames;
+ *pFundamentalPeriodicityInFrames = pFormatRecords[i].fundamentalPeriodInFrames;
+ *pMinPeriodicityInFrames = pFormatRecords[i].minPeriodInFrames;
+ *pMaxPeriodicityInFrames = pFormatRecords[i].maxPeriodInFrames;
+ *pMaxPeriodicityInFramesExtended = pFormatRecords[i].maxPeriodInFramesExtended;
+ break;
+ }
+ }
+
+ if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) {
+ if (!VERIFY_IS_TRUE(bFormatInList)) {
+ hr = E_NOTFOUND;
+ return hr;
+ }
+ }
+ else if (eConnectorType == eOffloadConnector || eConnectorType == eLoopbackConnector) {
+ if (!VERIFY_SUCCEEDED(hr = DiscoverPeriodicityCharacteristicsForFormat(pDevice, eConnectorType, mode, pPreferredFormat, dataFlow, pDefaultPeriodicityInFrames, pFundamentalPeriodicityInFrames, pMinPeriodicityInFrames, pMaxPeriodicityInFrames, pMaxPeriodicityInFramesExtended))) {
+ return hr;
+ }
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// AddTestResourceForConnector
+//
+// For each connector, read connector id, get all processing modes, identify default and preferred format for each mode
+// and also enumerate a list of supported formats for each mode. Store all the infos in test resource.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT AddTestResourceForConnector
+(
+ ResourceList& resourceList,
+ LPWSTR deviceId,
+ LPWSTR deviceName,
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ STACKWISE_DATAFLOW dataFlow
+)
+{
+ HRESULT hr = S_OK;
+ bool bHasConnector = false;
+ UINT uConnectorId;
+ ULONG cModes = 0;
+ CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE> spModes;
+ AUDIO_SIGNALPROCESSINGMODE mode;
+ ULONG cFormatRecords = 0;
+ CComHeapPtr<FORMAT_RECORD> spFormatRecords;
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> pPreferredFormat;
+ UINT32 u32DefaultPeriodicityInFrames;
+ UINT32 u32FundamentalPeriodicityInFrames;
+ UINT32 u32MinPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFramesExtended;
+ bool isPortCls = false;
+ bool isAVStream = false;
+ bool isBluetooth = false;
+ bool isSideband = false;
+ bool isMVA = false;
+
+ // Get connector id
+ if (!VERIFY_SUCCEEDED(hr = GetConnectorId(pDevice, eConnectorType, &bHasConnector, &uConnectorId))) {
+ return hr;
+ }
+ if (!bHasConnector) {
+ return hr;
+ }
+
+ Log::Comment(String().Format(L"Adding Test Resource for pin [%u]:", (uConnectorId & PARTID_MASK)));
+
+
+ // Get all signal processing modes for connector
+ if (!VERIFY_SUCCEEDED(hr = GetProcessingModesForConnector(pDevice, uConnectorId, eConnectorType, &cModes, &spModes))) {
+ return hr;
+ }
+
+ // Loop through each mode, get preferred format and list of formats and create test resource for each mode
+ for (ULONG i = 0; i < cModes; i++) {
+
+ mode = spModes[i];
+
+ if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(pDevice, uConnectorId, eConnectorType, mode, dataFlow, &cFormatRecords, &spFormatRecords))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatForConnector(pDevice, uConnectorId, eConnectorType, mode, wil::out_param(pPreferredFormat)))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatPeriodicityCharacteristicsForConnector(pDevice, eConnectorType, mode, dataFlow, pPreferredFormat.get(), cFormatRecords, spFormatRecords, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) {
+ return hr;
+ }
+
+ // Check if audio endpoint is PortCls
+ if (!VERIFY_SUCCEEDED(hr = IsPortCls(pDevice, &isPortCls))) {
+ return hr;
+ }
+
+ // Check if audio endpoint is AVStream
+ if (!VERIFY_SUCCEEDED(hr = IsAVStream(pDevice, &isAVStream))) {
+ return hr;
+ }
+
+ // Check if audio endpoint is Bluetooth
+ if (!VERIFY_SUCCEEDED(hr = IsBluetooth(pDevice, &isBluetooth))) {
+ return hr;
+ }
+
+ // Check if audio endpoint is side band
+ if (!VERIFY_SUCCEEDED(hr = IsSideband(pDevice, &isSideband))) {
+ return hr;
+ }
+
+ // Check if audio endpoint is MVA
+ if (!VERIFY_SUCCEEDED(hr = IsMVA(eConnectorType, pDevice, &isMVA))) {
+ return hr;
+ }
+
+ DeviceDescriptor descriptor = { 0 };
+ descriptor.pDevice = pDevice;
+ descriptor.pwstrAudioEndpointId = deviceId;
+ descriptor.pwstrAudioEndpointFriendlyName = deviceName;
+ descriptor.dataFlow = dataFlow;
+ descriptor.eConnectorType = eConnectorType;
+ descriptor.uConnectorId = uConnectorId;
+ descriptor.mode = mode;
+ descriptor.cModes = cModes;
+ descriptor.pModes = spModes;
+ descriptor.cFormatRecords = cFormatRecords;
+ descriptor.pFormatRecords = spFormatRecords;
+ descriptor.pPreferredFormat = pPreferredFormat.get();
+ descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames;
+ descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames;
+ descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames;
+ descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames;
+ descriptor.bIsPortCls = isPortCls;
+ descriptor.bIsAVStream = isAVStream;
+ descriptor.bIsBluetooth = isBluetooth;
+ descriptor.bIsSideband = isSideband;
+ descriptor.bIsMVA = isMVA;
+
+ if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, descriptor))) {
+ return hr;
+ }
+
+ spFormatRecords.Free();
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// AddTestResourcesForDevice
+//
+// Identify the existence of all pin types and add test resources for each pin
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT AddTestResourcesForDevice
+(
+ ResourceList& resourceList,
+ LPWSTR deviceId,
+ LPWSTR deviceName,
+ STACKWISE_DATAFLOW dataFlow
+)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
+ wil::com_ptr_nothrow<IMMDevice> spDevice;
+
+ Log::Comment(String().Format(L"Adding Test Resource for Device [%s]:", deviceName));
+
+ // Read the default device format from the property store
+ if (!VERIFY_SUCCEEDED(hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator))) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(deviceId, &spDevice))) {
+ return hr;
+ }
+
+ // Identify existence of host pin. Add test resources for host pin.
+ if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eHostProcessConnector, dataFlow))) {
+ return hr;
+ }
+
+ // Identify existence of offload pin. Add test resources for offload pin.
+ if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eOffloadConnector, dataFlow))) {
+ return hr;
+ }
+
+ // Identify existence of loopback pin. Add test resources for loopback pin.
+ if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eLoopbackConnector, capture ))) {
+ return hr;
+ }
+
+ // Identify existence of keyword detector pin. Add test resources for keyword detector pin.
+ if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eKeywordDetectorConnector, dataFlow))) {
+ return hr;
+ }
+
+ return hr;
+}
+
+HRESULT AddDevices(ResourceList& resourceList)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
+ wil::com_ptr_nothrow<IMMDeviceCollection> spEndpoints;
+ wil::com_ptr_nothrow<IMMDevice> spEndpoint;
+ UINT cDevices = 0;
+ UINT i = 0;
+ wil::unique_cotaskmem_string id;
+ wil::unique_cotaskmem_string friendlyName;
+ EDataFlow endpointDataFlow;
+
+ SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
+ DisableVerifyExceptions disable;
+
+ VERIFY_SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED));
+
+ Log::Comment(L"In BuildResourceList");
+
+ // Create IMMDevice Enumerator
+ VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator));
+
+ // Enumerate all endpoints
+ VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spEndpoints));
+ VERIFY_SUCCEEDED(spEndpoints->GetCount(&cDevices));
+
+ if (!VERIFY_IS_TRUE(cDevices))
+ {
+ Log::Comment(L"No device was found!");
+ return E_FAIL;
+ }
+
+ // Check if a device ID was specified.
+ String instanceId;
+ BOOL isDeviceSelected = false;
+ HRESULT res = RuntimeParameters::TryGetValue(L"InstanceId", instanceId);
+ if (res == S_OK)
+ {
+ Log::Comment(String().Format(L"Selected device ID: %s", static_cast<LPCWSTR>(instanceId)));
+ isDeviceSelected = true;
+ }
+
+ Log::Comment(String().Format(L"Found %d viable endpoint(s)!", cDevices));
+ // Add test resources for endpoints
+ for (i = 0; i < cDevices; i++) {
+ VERIFY_SUCCEEDED(spEndpoints->Item(i, &spEndpoint));
+ VERIFY_SUCCEEDED(spEndpoint->GetId(&id));
+ VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName));
+
+ // Check whether it is a render or capture endpoint.
+ CComPtr<IMMEndpoint> pMmEndpoint;
+ VERIFY_SUCCEEDED(spEndpoint->QueryInterface(__uuidof(IMMEndpoint), (void**)&pMmEndpoint));
+ pMmEndpoint->GetDataFlow(&endpointDataFlow);
+
+ // If there is a device selected, check if the current endpoint belongs to the device.
+ if (isDeviceSelected)
+ {
+ // Get the IDeviceTopology interface of the endpoint.
+ CComPtr<IDeviceTopology> pEndpointTopology;
+ if (!VERIFY_SUCCEEDED(hr = spEndpoint->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pEndpointTopology))) { return hr; }
+
+ // Get the connector inside the device topology object.
+ CComPtr<IConnector> spConnector;
+ if (!VERIFY_SUCCEEDED(hr = pEndpointTopology->GetConnector(0, &spConnector))) { return hr; }
+
+ // Get the id of the device adapter this endpoint is connected to.
+ CComHeapPtr<WCHAR> szFilterId;
+ if (!VERIFY_SUCCEEDED(hr = spConnector->GetDeviceIdConnectedTo(&szFilterId))) { return hr; }
+
+ // Get the IMMDevice object of this device adapter.
+ CComPtr<IMMDevice> spDevnode;
+ if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(szFilterId, &spDevnode))) { return hr; }
+
+ // Open the property store and get the instance ID of the device adapter.
+ PROPVARIANT varInstanceId;
+ PropVariantInit(&varInstanceId);
+
+ CComPtr<IPropertyStore> spDevnodePropertyStore;
+ if (!VERIFY_SUCCEEDED(hr = spDevnode->OpenPropertyStore(STGM_READ, &spDevnodePropertyStore))) { return hr; }
+
+ if (!VERIFY_SUCCEEDED(hr = spDevnodePropertyStore->GetValue(PKEY_Device_InstanceId, &varInstanceId))) { return hr; }
+ if (!VERIFY_ARE_EQUAL(varInstanceId.vt, VT_LPWSTR)) { return E_UNEXPECTED; }
+
+ // If this endpoint does not belong to the selected device, skip it.
+ if (0 != _wcsicmp(instanceId, varInstanceId.pwszVal))
+ {
+ continue;
+ }
+ }
+ Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get()));
+ VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), endpointDataFlow == eRender ? render : capture));
+ }
+
+ Log::Comment(String().Format(L"Enumerated %u resources", resourceList.Count()));
+
+ return hr;
+}
diff --git a/audio/tests/PinResourceHelpers/TestResourceHelper.h b/audio/tests/PinResourceHelpers/TestResourceHelper.h
index 08fafa23..df5b8720 100644
--- a/audio/tests/PinResourceHelpers/TestResourceHelper.h
+++ b/audio/tests/PinResourceHelpers/TestResourceHelper.h
@@ -1,6 +1,6 @@
// ------------------------------------------------------------------------------
//
-// Copyright (C) Microsoft. All rights reserved.
+// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Module Name:
//
@@ -71,3 +71,12 @@ HRESULT GetSupportedFormatRecordsForConnector
ULONG* pCount,
FORMAT_RECORD** ppFormatRecords
);
+
+// ----------------------------------------------------------------------
+void VerifyAllEndpointsPluggedIn();
+
+// ----------------------------------------------------------------------
+HRESULT AddDevices
+(
+ WEX::TestExecution::ResourceList& resourceList
+);