diff options
| author | J M Rossy <[email protected]> | 2015-07-29 15:40:09 -0700 |
|---|---|---|
| committer | J M Rossy <[email protected]> | 2015-07-29 16:34:40 -0700 |
| commit | 5d61a4a79a1e96dc5d9af1e5e712c84507307544 (patch) | |
| tree | 49ed270893578cd18758b74ffcca16dc7ab948d6 /audio/sysvad | |
| parent | 5d41613e26e147d2300c786bb2b34cb4b4067a25 (diff) | |
Samples update for public Windows 10 release
Fix #2 Enabling WPP Recorder in Sensors Samples causes errors
Fix #4 Add back fixed KMDOD sample
Add new BarcodeScanner sample in pos folder
Add new MagneticStripeReader sample in pos folder
Add new SynpaticsTouch sample in input folder
Add new Power Engine Plugin sample in pofx folder
Add new DeviceMft sample in avstream folder
Add new AvsCamera sample in root
Add new SimBatt sample in root
Add other pre-existing samples not yet released for Win10
Diffstat (limited to 'audio/sysvad')
166 files changed, 54587 insertions, 0 deletions
diff --git a/audio/sysvad/ContosoKeywordDetector.h b/audio/sysvad/ContosoKeywordDetector.h new file mode 100644 index 00000000..fce0f3df --- /dev/null +++ b/audio/sysvad/ContosoKeywordDetector.h @@ -0,0 +1,34 @@ +/* + Copyright (c) Microsoft Corporation All Rights Reserved + + Contoso voice activation driver definitions + + Hardware manufacturers define identifiers and data structures specific to + their detection technology to pass voice models, keyword data, speaker + data, or any other data relevant to their technology. + +*/ + +// +// Identifier for Contoso keyword configuration data. +// +// {6F7DBCC1-202E-498D-99C5-61C36C4EB2DC} +DEFINE_GUID(CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER, 0x6f7dbcc1, 0x202e, 0x498d, 0x99, 0xc5, 0x61, 0xc3, 0x6c, 0x4e, 0xb2, 0xdc); + +// +// The format of the Contoso keyword pattern matching data. +// +typedef struct +{ + SOUNDDETECTOR_PATTERNHEADER Header; + LONGLONG ContosoDetectorConfigurationData; +} CONTOSO_KEYWORDCONFIGURATION; + +// +// The format of the Contoso match result data. +// +typedef struct +{ + SOUNDDETECTOR_PATTERNHEADER Header; + LONGLONG ContosoDetectorResultData; +} CONTOSO_KEYWORDDETECTIONRESULT; diff --git a/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj new file mode 100644 index 00000000..aef0e973 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{E3BA10BE-08FF-4244-86C6-C788072CEA25}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{4DA68694-1C5F-491B-9272-A8223477E4B7}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>EndpointsCommon</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>EndpointsCommon</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>EndpointsCommon</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>EndpointsCommon</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="bthhfpmictopo.cpp" /> + <ClCompile Include="bthhfpminwavert.cpp" /> + <ClCompile Include="bthhfpspeakertopo.cpp" /> + <ClCompile Include="bthhfptopo.cpp" /> + <ClCompile Include="micarraytopo.cpp" /> + <ClCompile Include="MiniportAudioEngineNode.cpp" /> + <ClCompile Include="MiniportStreamAudioEngineNode.cpp" /> + <ClCompile Include="mintopo.cpp" /> + <ClCompile Include="minwavert.cpp" /> + <ClCompile Include="minwavertstream.cpp" /> + <ClCompile Include="speakerhptopo.cpp" /> + <ClCompile Include="speakertopo.cpp" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj.Filters b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj.Filters new file mode 100644 index 00000000..167f0468 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj.Filters @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{44370F68-49C1-4647-BD26-31F7EBE942A3}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{AEA2BA25-EBF9-454D-BC73-91D1CC36FB6F}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{95FAF38F-0673-42EF-A3C6-35F98AE2E188}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{4D0AE1D8-6C62-4B48-B101-690D5E955972}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="bthhfpmictopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="bthhfpminwavert.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="bthhfpspeakertopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="bthhfptopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="micarraytopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MiniportAudioEngineNode.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MiniportStreamAudioEngineNode.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="mintopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="minwavert.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="minwavertstream.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="speakerhptopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="speakertopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp b/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp new file mode 100644 index 00000000..db3fda39 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp @@ -0,0 +1,1045 @@ +/*++ + +Module Name: + + MiniportAudioEngineNode.cpp + +Abstract: + + Implementation of IMiniportAudioEngineNode interface for wavert miniport. + +--*/ +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include <limits.h> +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "IHVPrivatePropertySet.h" +#include "UnittestData.h" + +#define MINWAVERT_POOLTAG 'RWNM' +//============================================================================= +// IMiniportAudioEngineNode +//============================================================================= + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetAudioEngineDescriptor + +Description: + + Portcls calls this method to get the pin id for each pin (host process, + offload, and loopback) on a specific audio engine node. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pAudioEngineDescriptor: a pointer to a KSAUDIOENGINE_DESCRIPTOR to receive the pin id informationived as input to its DriverEntry routine. + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks: + + The property value is of type KSAUDIOENGINE_DESCRIPTOR and indicates the static properties of the audio engine node. + The KSAUDIOENGINE_DESCRIPTOR structure is defined as follows: + + typedef struct _tagKSAUDIOENGINE_DESCRIPTOR + { + UINT nHostPinId; + UINT nOffloadPinId; + UINT nLoopbackPinId; + } KSAUDIOENGINE_DESCRIPTOR, *PKSAUDIOENGINE_DESCRIPTOR; + + The fields are defined as: + nHostPinId � The ID of the pin factory connected to the audio engine node that is intended for host processed audio data. This is the pin factory on which a software audio engine will run. + nOffloadPinId � The ID of the pin factory connected to the audio engine node that is intended for offloaded streams. + nLoopbackPinId � The ID of the pin factory connected to the audio engine that is intended for supplying a post-mix loopback or reference stream. + + All pin ids need to be unique for each audio engine node. + + A wave miniport might have more than one audio engine node and each engine node has their own set of engine pin ids. + A driver needs to make sure that the correct set of audio engine descriptor information is returned. +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetAudioEngineDescriptor(_In_ ULONG _ulNodeId, _Out_ KSAUDIOENGINE_DESCRIPTOR *_pAudioEngineDescriptor) +{ + PAGED_CODE (); + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + + ASSERT (_pAudioEngineDescriptor); + DPF_ENTER(("[CMiniportWaveRT::GetAudioEngineDescriptor]")); + // In this sample driver, only one single engine node is exposed in its wave filter + if (_ulNodeId == KSNODE_WAVE_AUDIO_ENGINE) + { + _pAudioEngineDescriptor->nHostPinId = GetSystemPinId(); + _pAudioEngineDescriptor->nOffloadPinId = GetOffloadPinId(); + _pAudioEngineDescriptor->nLoopbackPinId = GetLoopbackPinId(); + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetGfxState + +Description: + + Portcls calls this method to get the audio engine GFX's state + +Parameters + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pbEnable: a pointer to a BOOL value for receieving the returned GFX state + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + The global operations (on the device stream) inside HW Audio Engine (such as src, dsp, and other special effects) are hidden from the software audio stack. +So, the driver should return TRUE if any one of the effects is on and returns FALSE when all the opertations are off. +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetGfxState(_In_ ULONG _ulNodeId, _Out_ BOOL *_pbEnable) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetGfxState]")); + + UNREFERENCED_PARAMETER(_ulNodeId); + + *_pbEnable = m_bGfxEnabled; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::SetGfxState + +Decscription: + + Portcls calls this method to set the audio engine GFX's state + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ bEnable: a BOOL value. TRU: to enable GFX, FALSE: to disable GFX + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + The global operations (on the device stream) inside HW Audio Engine (such as src, dsp, and other special effects) are hidden from the software audio stack. +So, when handling disabling GFX, the driver should ALL the effects. When enabling GFX, it's up the driver+HW Audio Engine to decide what opertations to turn on +when they see appropriate. +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::SetGfxState(_In_ ULONG _ulNodeId, _In_ BOOL _bEnable) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::SetGfxState]")); + + UNREFERENCED_PARAMETER(_ulNodeId); + + // see above comments for appropriate enabling/disabling opertations on the HW Audio Engine + m_bGfxEnabled = _bEnable; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetEngineFormatSize + +Decscription: + + When handling GetMixFormat, DeviceFormat, or SupportDeviceFormatsList, Portcls calls + this method to know the correct data size to allocate for the receiving the corresponding + format information. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ eEngineFormatType: format target to indicate which format size is being asked + _Out_ pulFormatSize: a pointer to a ULONG variable for receiving returned szize information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRT::GetEngineFormatSize +( + _In_ ULONG _ulNodeId, + _In_ eEngineFormatType _formatType, + _Out_ ULONG *_pulFormatSize +) +{ + PAGED_CODE (); + NTSTATUS ntStatus = STATUS_SUCCESS; + + DPF_ENTER(("[CMiniportWaveRT::GetEngineFormatSize]")); + ASSERT (_pulFormatSize); + + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + switch (_formatType) + { + case eMixFormat: + DPF_ENTER(("[eMixFormat]")); + *_pulFormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + break; + case eDeviceFormat: + DPF_ENTER(("[eDeviceFormat]")); + *_pulFormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + break; + case eSupportedDeviceFormats: + DPF_ENTER(("[eSupportedDeviceFormats]")); + *_pulFormatSize = sizeof(KSMULTIPLE_ITEM) + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE) * GetAudioEngineSupportedDeviceFormats(NULL); + break; + default: + ntStatus = STATUS_INVALID_PARAMETER; + break; + } +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetMixFormat + +Decscription: + + GetMixFormat returns the current mix format used by the HW Audio Engine + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pFormat: a buffer pointer for receiving the mix format information being asked + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by pFormat + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + This is a read only operation; the HW Audio Engine mix format is determined by the HW Audio Engine alone. + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetMixFormat(_In_ ULONG _ulNodeId, _Out_ KSDATAFORMAT_WAVEFORMATEX *_pFormat, _In_ ULONG _ulBufferSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + ASSERT (_pFormat); + + DPF_ENTER(("[CMiniportWaveRT::GetMixFormat]")); + + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + IF_TRUE_ACTION_JUMP(_ulBufferSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), ntStatus = STATUS_BUFFER_TOO_SMALL, Exit); +#pragma warning(push) + // IMiniportAudioEngineNode::GetMixFormat's annotation on _pFormat requires it to be KSDATAFORMAT_WAVEFORMATEX. However, + // this implementation here will always be called by our own code with _pFormat to be KSDATAFORMAT_WAVEFORMATEXTENSIBLE, + // so there should be no buffer overrun; also the IF_TRUE_ACTION_JUMP above also help to avoid buffer overrun. +#pragma warning(disable:6386) + RtlCopyMemory((PVOID)_pFormat, (PVOID)m_pMixFormat, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); +#pragma warning (pop) + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceFormat + +Decscription: + + GetDeviceFormat returns the current device format used by the HW Audio Engine + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pFormat: a buffer pointer for receiving the device format information being asked + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by pFormat + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + Setting the device format of a HW Audio Engine could potential impact the mix format inside the HD Audio Engine. +The driver might need to add appropriate src/format converter according or change mix format. + +-------------------------------------------------------------------------------------------------------------------------*/ +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceFormat(_In_ ULONG _ulNodeId, _Out_ KSDATAFORMAT_WAVEFORMATEX *_pFormat, _In_ ULONG _ulBufferSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PAGED_CODE (); + + ASSERT (_pFormat); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceFormat]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + IF_TRUE_ACTION_JUMP(_ulBufferSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), ntStatus = STATUS_BUFFER_TOO_SMALL, Exit); +#pragma warning(push) + // IMiniportAudioEngineNode::GetDeviceFormat's annotation on _pFormat requires it to be KSDATAFORMAT_WAVEFORMATEX. However, + // this implementation here will always be called by our own code with _pFormat to be KSDATAFORMAT_WAVEFORMATEXTENSIBLE, + // so there should be no buffer overrun; also the IF_TRUE_ACTION_JUMP above also help to avoid buffer overrun. +#pragma warning(disable:6386) + RtlCopyMemory((PVOID)_pFormat, (PVOID)m_pDeviceFormat, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); +#pragma warning (pop) + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::SetDeviceFormat + +Decscription: + + GetDeviceFormat set the current device format to be used by the HW Audio Engine + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pFormat: a buffer pointer with the device format to be set to the hw Audio Engine + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by pFormat + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + Setting the device format of a HW Audio Engine could potential impact the mix format inside the HD Audio Engine. +The driver might need to add appropriate src/format converter according or change mix format. + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::SetDeviceFormat(_In_ ULONG _ulNodeId, _In_ KSDATAFORMAT_WAVEFORMATEX *_pFormat, _In_ ULONG _ulBufferSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PAGED_CODE (); + + ASSERT (_pFormat); + + DPF_ENTER(("[CMiniportWaveRT::SetDeviceFormat]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + IF_TRUE_ACTION_JUMP(_ulBufferSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), ntStatus = STATUS_BUFFER_TOO_SMALL, Exit); + + RtlCopyMemory((PVOID)m_pDeviceFormat, (PVOID)_pFormat, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetSupportedDeviceFormats + +Decscription: + + GetSupportedDeviceFormats get the complete format list supported by the hw Audio Engine + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pFormat: a buffer pointer for receiving the supported device formats + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by pFormat + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetSupportedDeviceFormats(_In_ ULONG _ulNodeId, _Out_ KSMULTIPLE_ITEM* _pFormat, _In_ ULONG _ulBufferSize) +{ + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pDeviceFormats; + ULONG cDeviceFormats; + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PAGED_CODE (); + + ASSERT(_pFormat); + + DPF_ENTER(("[CMiniportWaveRT::GetSupportedDeviceFormats]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + cDeviceFormats = GetAudioEngineSupportedDeviceFormats(&pDeviceFormats); + + KSMULTIPLE_ITEM *pKsMulti = static_cast<KSMULTIPLE_ITEM*>(_pFormat); + pKsMulti->Size = sizeof(KSMULTIPLE_ITEM) + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE) * cDeviceFormats; + pKsMulti->Count = cDeviceFormats; + + IF_TRUE_ACTION_JUMP(_ulBufferSize < pKsMulti->Size, ntStatus = STATUS_BUFFER_TOO_SMALL, Exit); + + RtlCopyMemory((PVOID)(pKsMulti + 1), pDeviceFormats, + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE) * cDeviceFormats); + + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceChannelCount + +Decscription: + + When handling volume, mute, and meter related KS properties, Portcls calls + this method, inside its property handlers, to know the number of channels for the + corresponding KS property. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _targetType: the query target (volume. mute, or peak meter) + _Out_ _pulChannelCount: a pointer to a UINT32 variable for receiving returned channel count information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetDeviceChannelCount +( + _In_ ULONG _ulNodeId, + _In_ eChannelTargetType _targetType, + _Out_ UINT32 *_pulChannelCount +) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + ASSERT(_pulChannelCount); + + DPF_ENTER(("[CMiniportWaveRT::GetChannelCount]")); + + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + switch (_targetType) + { + case eVolumeAttribute: + ntStatus = GetVolumeChannelCount(_pulChannelCount); + break; + case eMuteAttribute: + ntStatus = GetMuteChannelCount(_pulChannelCount); + break; + case ePeakMeterAttribute: + ntStatus = GetPeakMeterChannelCount(_pulChannelCount); + break; + default: + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceAttributeSteppings + +Decscription: + + When handling volume, mute, and meter related KS properties, Portcls calls + this method, inside its property handlers, to know the property stepping information for the + corresponding KS property. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _targetType: the query target (volume. mute, or peak meter) + _Out_ _pKsPropMembHead: a pointer to a PKSPROPERTY_STEPPING_LONG variable for receiving returned channel count information + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by _pKsPropMembHead + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRT::GetDeviceAttributeSteppings(_In_ ULONG _ulNodeId, _In_ eChannelTargetType _targetType, _Out_ PKSPROPERTY_STEPPING_LONG _pKsPropMembHead, _In_ UINT32 _ui32DataSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceAttributeSteppings]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + switch (_targetType) + { + case eVolumeAttribute: + ntStatus = GetVolumeSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + case eMuteAttribute: + ntStatus = GetMuteSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + case ePeakMeterAttribute: + ntStatus = GetPeakMeterSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + default: + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceChannelVolume + +Decscription: + + When handling GET volume KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pVolume: a pointer to a LONG variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceChannelVolume(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _Out_ LONG *_pVolume) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceChannelVolume]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + ntStatus = GetChannelVolume(_uiChannel, _pVolume); + +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::SetDeviceChannelVolume + +Decscription: + + When handling SET volume KS property for the device, Portcls calls + this method, inside its property handlers, to set the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _In_ _Volume: volume value to set + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::SetDeviceChannelVolume(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _In_ LONG _Volume) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::SetEndpointChannelVolume]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + // Snap the volume level to our range of steppings. + LONG lVolume = VOLUME_NORMALIZE_IN_RANGE(_Volume); + + ntStatus = SetChannelVolume(_uiChannel, lVolume); + +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceChannelPeakMeter + +Decscription: + + When handling GET peak meter KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pPeakMeterValue: a pointer to a LONG variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceChannelPeakMeter(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _Out_ LONG *_pPeakMeterValue) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceChannelPeakMeter]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + ntStatus = GetChannelPeakMeter(_uiChannel, _pPeakMeterValue); + +Exit: + return ntStatus; +} +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceChannelMute + +Decscription: + + When handling GET mute KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pbMute: a pointer to a BOOL variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceChannelMute(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _Out_ BOOL *_pbMute) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetEndpointChannelMute]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + ntStatus = GetChannelMute(_uiChannel, _pbMute); +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::SetDeviceChannelMute + +Decscription: + + When handling SET mute KS property for the device, Portcls calls + this method, inside its property handlers, to set the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _In_ _bMute: volume value to set + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRT::SetDeviceChannelMute(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _In_ BOOL _bMute) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::SetEndpointChannelMute]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + ntStatus = SetChannelMute(_uiChannel, _bMute); + +Exit: + return ntStatus; +} + + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetBufferSizeRange + +Decscription: + + Portcls calls this method, inside its property handlers, to get a buffer size capabilities for + a specifc format requested at the moment this call was made. + +Parameters: + + _In_ ULONG _ulNodeId: the node is for the target audio engine + _In_ KSDATAFORMAT_WAVEFORMATEX *_pKsDataFormatWfx: the requested format + _Out_ KSAUDIOENGINE_BUFFER_SIZE_RANGE *_pBufferSizeRange: + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + + The purpose of this call is to give an audio client (such as MF SAR or direct WASAPI clients) an idea on the supported buffer size range - + almost equivalent to the hardware's buffer capability. An application pick any size within that range that it determines it would best serve + its purpose (communication apps would like small buffer size for low latency while other media app might choose large buffer for power saving advantage). + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRT::GetBufferSizeRange(_In_ ULONG _ulNodeId, _In_ KSDATAFORMAT_WAVEFORMATEX *_pKsDataFormatWfx, _Out_ KSAUDIOENGINE_BUFFER_SIZE_RANGE *_pBufferSizeRange) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + PAGED_CODE (); + UNREFERENCED_PARAMETER(_ulNodeId); + ASSERT(_pBufferSizeRange); + ASSERT(_pKsDataFormatWfx); + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamBufferSizeRange]")); + + _pBufferSizeRange->MinBufferBytes = (_pKsDataFormatWfx->WaveFormatEx.nAvgBytesPerSec * MIN_BUFFER_DURATION_MS) / MS_PER_SEC; + _pBufferSizeRange->MaxBufferBytes = (_pKsDataFormatWfx->WaveFormatEx.nAvgBytesPerSec * MAX_BUFFER_DURATION_MS) / MS_PER_SEC; + + return ntStatus; +} + +//------------------------------------------------------------------------------------------------------------------------ +//CMiniportWaveRT private supporting functions +//------------------------------------------------------------------------------------------------------------------------ +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetVolumeChannelCount(_Out_ UINT32 *_pulChannelCount) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PAGED_CODE (); + ASSERT (_pulChannelCount); + + DPF_ENTER(("[CMiniportWaveRT::GetVolumeChannelCount]")); + *_pulChannelCount = m_DeviceMaxChannels; + return ntStatus; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetVolumeSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRT::GetVolumeSteppings]")); + + ASSERT(ulChannelCount <= m_DeviceMaxChannels); + if (ulChannelCount > m_DeviceMaxChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = VOLUME_STEPPING_DELTA; + _pKsPropStepLong[i].Bounds.SignedMaximum = VOLUME_SIGNED_MAXIMUM; + _pKsPropStepLong[i].Bounds.SignedMinimum = VOLUME_SIGNED_MINIMUM; + } + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetChannelVolume(_In_ UINT32 _uiChannel, _Out_ LONG *_pVolume) +{ + PAGED_CODE (); + ASSERT (_pVolume); + DPF_ENTER(("[CMiniportWaveRT::GetChannelVolume]")); + + if (_uiChannel == ALL_CHANNELS_ID) + { + *_pVolume = m_plVolumeLevel[0]; + } + else + { + ASSERT(_uiChannel <= m_DeviceMaxChannels); + *_pVolume = m_plVolumeLevel[_uiChannel]; + } + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::SetChannelVolume(_In_ UINT32 _uiChannel, _In_ LONG _Volume) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRT::SetChannelVolume]")); + + if (_uiChannel == ALL_CHANNELS_ID) + { + for (int i=0; i<m_DeviceMaxChannels; i++) + { + m_plVolumeLevel[i] = _Volume; + } + } + else + { + ASSERT(_uiChannel <= m_DeviceMaxChannels); + m_plVolumeLevel[_uiChannel] = _Volume; + } + + return STATUS_SUCCESS; +} +///- metering +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetPeakMeterChannelCount(_Out_ UINT32 *_pulChannelCount) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PAGED_CODE (); + ASSERT (_pulChannelCount); + + DPF_ENTER(("[CMiniportWaveRT::GetVolumeChannelCount]")); + *_pulChannelCount = m_DeviceMaxChannels; + return ntStatus; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetPeakMeterSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRT::GetVolumeSteppings]")); + + ASSERT(ulChannelCount <= m_DeviceMaxChannels); + + if (ulChannelCount > m_DeviceMaxChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = PEAKMETER_STEPPING_DELTA; + _pKsPropStepLong[i].Bounds.SignedMaximum = PEAKMETER_SIGNED_MAXIMUM; + _pKsPropStepLong[i].Bounds.SignedMinimum = PEAKMETER_SIGNED_MINIMUM; + } + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetChannelPeakMeter(_In_ UINT32 _uiChannel, _Out_ LONG *_plPeakMeter) +{ + PAGED_CODE (); + ASSERT (_plPeakMeter); + DPF_ENTER(("[CMiniportWaveRT::GetChannelPeakMeter]")); + + ASSERT(_uiChannel < m_DeviceMaxChannels); + + if (_uiChannel >= m_DeviceMaxChannels) + { + return STATUS_INVALID_PARAMETER; + } + + if (m_ulSystemAllocated + m_ulOffloadAllocated > 0) + { + *_plPeakMeter = PEAKMETER_NORMALIZE_IN_RANGE(PEAKMETER_SIGNED_MAXIMUM / 2); + } + else + { + *_plPeakMeter = 0; + } + //*_plPeakMeter = m_plPeakMeter[lChannel]; + + return STATUS_SUCCESS; +} +// mute +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetMuteChannelCount(_Out_ UINT32 *_pulChannelCount) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PAGED_CODE (); + ASSERT (_pulChannelCount); + + DPF_ENTER(("[CMiniportWaveRT::GetMuteChannelCount]")); + *_pulChannelCount = m_DeviceMaxChannels; + return ntStatus; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetMuteSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRT::GetMuteSteppings]")); + + ASSERT(ulChannelCount <= m_DeviceMaxChannels); + + if (ulChannelCount > m_DeviceMaxChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = 1; + _pKsPropStepLong[i].Bounds.SignedMaximum = TRUE; + _pKsPropStepLong[i].Bounds.SignedMinimum = FALSE; + } + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetChannelMute(_In_ UINT32 _uiChannel, _Out_ BOOL *_pbMute) +{ + PAGED_CODE (); + ASSERT (_pbMute); + DPF_ENTER(("[CMiniportWaveRT::GetChannelMute]")); + + + if (_uiChannel == ALL_CHANNELS_ID) + { + *_pbMute = m_pbMuted[0]; + } + else + { + ASSERT(_uiChannel <= m_DeviceMaxChannels); + *_pbMute = m_pbMuted[_uiChannel]; + } + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::SetChannelMute(_In_ UINT32 _uiChannel, _In_ BOOL _bMute) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRT::SetChannelVolume]")); + + if (_uiChannel == ALL_CHANNELS_ID) + { + for (int i=0; i<m_DeviceMaxChannels; i++) + { + m_pbMuted[i] = _bMute; + } + } + else + { + ASSERT(_uiChannel <= m_DeviceMaxChannels); + m_pbMuted[_uiChannel] = _bMute; + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::SetLoopbackProtection +( + _In_ CONSTRICTOR_OPTION ulProtectionOption +) +/*++ + +Routine Description: + + Allows or disallows loopback streaming. + +Arguments: + + Parameters: + _In_ protectionOption: protection option + CONSTRICTOR_OPTION_DISABLE: Turn protection off. + CONSTRICTOR_OPTION_MUTE: Mute the loopback stream + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRT::SetLoopbackProtection]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (ulProtectionOption == m_LoopbackProtection) + { + goto Done; + } + + for (ULONG i = 0; i < MAX_OUTPUT_LOOPBACK_STREAMS; i++) + { + if (m_LoopbackStreams[i]) + { + ntStatus = m_LoopbackStreams[i]->SetLoopbackProtection(ulProtectionOption); + if (!NT_SUCCESS(ntStatus)) + { + break; + } + } + } + + if (NT_SUCCESS(ntStatus)) + { + m_LoopbackProtection = ulProtectionOption; + } + else + { + // + // Something went wrong, restore old protection setting. + // + for (ULONG i = 0; i < MAX_OUTPUT_LOOPBACK_STREAMS; i++) + { + if (m_LoopbackStreams[i]) + { + (void)m_LoopbackStreams[i]->SetLoopbackProtection(m_LoopbackProtection); + } + } + } + +Done: + return ntStatus; +} + + diff --git a/audio/sysvad/EndpointsCommon/MiniportStreamAudioEngineNode.cpp b/audio/sysvad/EndpointsCommon/MiniportStreamAudioEngineNode.cpp new file mode 100644 index 00000000..3c775d01 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/MiniportStreamAudioEngineNode.cpp @@ -0,0 +1,971 @@ +/*++ + +Module Name: + + MiniportStreamAudioEngineNode.cpp + +Abstract: + + Implementation of IMiniportstreamAudioEngineNode interface for wavert steam. + +--*/ + +#include <sysvad.h> +#include <intsafe.h> +#include <ks.h> +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "UnittestData.h" +#define MINWAVERTSTREAM_POOLTAG 'SRWM' +#define HNSTIME_PER_MILLISECOND 10000 + +#pragma warning (disable : 4127) + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetLfxState + +Description: + + Portcls calls this method to get a offload stream's Lfx state + +Parameters + + _Out_ pbEnable: a pointer to a BOOL value for receieving the returned LFX state + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + The Lfx operations (on the offload stream) inside HW Audio Engine (such as src, dsp, and other special effects) are hidden from the software audio stack. +So, the driver should return TRUE if any one of the effects is on and returns FALSE when all the opertations are off. +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetLfxState(_Out_ BOOL *_pbEnable) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetLfxState]")); + + *_pbEnable = m_bLfxEnabled; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetLfxState + +Decscription: + + Portcls calls this method to set a offload stream's Lfx state + +Parameters: + + _In_ bEnable: a BOOL value. TRU: to enable Lfx, FALSE: to disable Lfx + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + The local operations (on the offload stream) inside HW Audio Engine (such as src, dsp, and other special effects) are hidden from the software audio stack. +So, when handling disabling Lfx, the driver should ALL the effects. When enabling Lfx, it's up the driver+HW Audio Engine to decide what opertations to turn on +when they see appropriate. +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetLfxState(_In_ BOOL _bEnable) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::SetGfxState]")); + + UNREFERENCED_PARAMETER(_bEnable); + m_bLfxEnabled = _bEnable; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamChannelVolume + +Decscription: + + When handling GET volume KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pVolume: a pointer to a LONG variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamChannelVolume(_In_ UINT32 _uiChannel, _Out_ LONG *_pVolume) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamChannelVolume]")); + + *_pVolume = m_plVolumeLevel[_uiChannel]; + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamChannelMute + +Decscription: + + When handling GET mute KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pbMute: a pointer to a BOOL variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRTStream::GetStreamChannelMute(_In_ UINT32 _uiChannel, _Out_ BOOL *_pbMute) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + ASSERT (_pbMute); + + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamChannelMute]")); + ntStatus = GetChannelMute(_uiChannel, _pbMute); + + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamAttributeSteppings + +Decscription: + + When handling volume, mute, and meter related KS properties, Portcls calls + this method, inside its property handlers, to know the property stepping information for the + corresponding KS property. + +Parameters: + + _In_ _targetType: the query target (volume. mute, or peak meter) + _Out_ _pKsPropMembHead: a pointer to a PKSPROPERTY_STEPPING_LONG variable for receiving returned channel count information + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by _pKsPropMembHead + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamAttributeSteppings(_In_ eChannelTargetType _targetType, _Out_ PKSPROPERTY_STEPPING_LONG _pKsPropMembHead, _In_ UINT32 _ui32DataSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetDeviceAttributeSteppings]")); + + switch (_targetType) + { + case eVolumeAttribute: + ntStatus = GetVolumeSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + case eMuteAttribute: + ntStatus = GetMuteSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + case ePeakMeterAttribute: + ntStatus = GetPeakMeterSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + default: + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } + return ntStatus; + +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetStreamChannelVolume + +Decscription: + + When handling SET volume KS property for the device, Portcls calls + this method, inside its property handlers, to set the current setting on the specific channel. + +Parameters: + + _In_ Channel: the target channel for this GET volume operation + _In_ TargetVolume: volume value to set + _In_ CurveType: type of curve to apply to the ramp + _In_ CurveDuration: amount of time in hns over which to ramp the volume + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRTStream::SetStreamChannelVolume +( + _In_ UINT32 Channel, + _In_ LONG TargetVolume, + _In_ AUDIO_CURVE_TYPE CurveType, + _In_ ULONGLONG CurveDuration +) +{ + UNREFERENCED_PARAMETER(CurveType); + UNREFERENCED_PARAMETER(CurveDuration); + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::SetStreamChannelVolume]")); + + // Snap the volume level to our range of steppings. + LONG lVolume = VOLUME_NORMALIZE_IN_RANGE(TargetVolume); + + // If Channel is ALL_CHANNELS_ID, then set the level on all channels + if ( ALL_CHANNELS_ID == Channel ) + { + for (UINT32 i = 0; i < m_pWfExt->Format.nChannels; i++) + { + ntStatus = SetChannelVolume(i, lVolume); + } + } + else + { + ntStatus = SetChannelVolume(Channel, lVolume); + } + + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamChannelPeakMeter + +Decscription: + + When handling GET peak meter KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _uiChannel: the target channel for this GET peak meter operation + _Out_ _pPeakMeterValue: a pointer to a LONG variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRTStream::GetStreamChannelPeakMeter(_In_ UINT32 _uiChannel, _Out_ LONG *_pPeakMeterValue) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamChannelPeakMeter]")); + + ntStatus = GetChannelPeakMeter(_uiChannel, _pPeakMeterValue); + + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetStreamChannelMute + +Decscription: + + When handling SET mute KS property for the device, Portcls calls + this method, inside its property handlers, to set the current setting on the specific channel. + +Parameters: + + _In_ _uiChannel: the target channel for this Set mute operation + _In_ _bMute: mute value to set + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetStreamChannelMute(_In_ UINT32 _uiChannel, _In_ BOOL _bMute) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::SetStreamChannelMute]")); + + // If Channel is ALL_CHANNELS_ID, then set the mute info on all channels + if ( ALL_CHANNELS_ID == _uiChannel ) + { + for (UINT32 i = 0; i < m_pWfExt->Format.nChannels; i++) + { + ntStatus = SetChannelMute(i, _bMute); + } + } + else + { + ntStatus = SetChannelMute(_uiChannel, _bMute); + } + + return ntStatus; +} +//presentation +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamPresentationPosition + +Decscription: + + Portcls calls this method, inside its property handlers, to get a stream's presentation + postion. + +Parameters: + + _Out_ pPresentationPosition: a pointer to a KSAUDIO_PRESENTATION_POSITION variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamPresentationPosition(_Out_ KSAUDIO_PRESENTATION_POSITION *_pPresentationPosition) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + ASSERT(_pPresentationPosition); + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamPresentationPosition]")); + + ntStatus = GetPresentationPosition(_pPresentationPosition); + + return ntStatus; +} +//StreamCurrentWritePosition +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetStreamCurrentWritePosition + +Decscription: + + Portcls calls this method, inside its property handlers, to set a stream's write position + postion. + +Parameters: + + _In_ ulCurrentWritePosition: a position value indicating the last valid byte in the buffer + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +_ulCurrentWritePosition specifies the current write position in bytes, which an offload driver can use to know how much valid data in the WaveRT buffer. +Let say, BufferByteSize is the WaveRT buffer size, the valid value range for _ulCurrentWritePosition be 0... BufferByteSize (inclusive) + +0: no valid data in the buffer +n: the last valid data byte is at the offset n from the beginning of the buffer. If this is a value smaller than a previous write position, +it means a wrap-around has happened, in the case, the driver would need to take that in to consider when calculating how many bytes are valid for fetching. +BufferByteSize: the last valid data byte is at the end of the buffer + +After a pin is instantiated and before any KSPROPERTY_AUDIO_WAVERT_CURRENT_WRITE_POSITION is received, a driver should assume its current write position is zero. + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetStreamCurrentWritePosition(_In_ ULONG _ulCurrentWritePosition) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetStreamCurrentWritePosition]")); + + ntStatus = SetCurrentWritePosition(_ulCurrentWritePosition); + + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamLinearBufferPosition + +Decscription: + + Portcls calls this method, inside its property handlers, to set a stream's write position + postion. + +Parameters: + + _Out_ pullLinearBufferPosition: a pointer to a ULONGLONG variable for receiving returned information + + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks +The returned value is the number of bytes that the DMA has fetched from the audio buffer since the beginning of the stream +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamLinearBufferPosition(_Out_ ULONGLONG *_pullLinearBufferPosition) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + ASSERT(_pullLinearBufferPosition); + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamLinearBufferPosition]")); + + ntStatus = GetLinearBufferPosition(_pullLinearBufferPosition, NULL); + + return ntStatus; +} +//SetStreamLoopbackProtection +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetStreamLoopbackProtection + +Decscription: + + Portcls calls this method, inside its property handlers, to set the loopback + protection option + +Parameters: + _In_ protectionOption: protection option + CONSTRICTOR_OPTION_DISABLE: Turn protection off. + CONSTRICTOR_OPTION_MUTE: Mute the loopback stream +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + This content protection settings requestion could come in from the host process + pin or offload pin. The miniport needs to mute its loopback stream contents for + for this request from either host process pin or offload pin. +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetStreamLoopbackProtection(_In_ CONSTRICTOR_OPTION protectionOption) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetStreamLoopbackProtection]")); + + // + // Miniport driver mutes/unmutes the loopback here. + // + ntStatus = m_pMiniport->SetLoopbackProtection(protectionOption); + + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamChannelCount + +Decscription: + + When handling volume, mute, and meter related KS properties, Portcls calls + this method, inside its property handlers, to know the number of channels for the + corresponding KS property. + +Parameters: + + _In_ _targetType: the query target (volume, mute, or peak meter) + _Out_ _pulChannelCount: a pointer to a UINT32 variable for receiving returned channel count information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamChannelCount(_In_ eChannelTargetType _targetType,_Out_ UINT32 *_pulChannelCount) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamChannelCount]")); + + switch (_targetType) + { + case eVolumeAttribute: + ntStatus = GetVolumeChannelCount(_pulChannelCount); + break; + case eMuteAttribute: + ntStatus = GetMuteChannelCount(_pulChannelCount); + break; + case ePeakMeterAttribute: + ntStatus = GetPeakMeterChannelCount(_pulChannelCount); + break; + default: + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return ntStatus; +} + + +//------------------------------------------------------------------------------------------------------------------------ +//CMiniportWaveRTStream private supporting functions +//------------------------------------------------------------------------------------------------------------------------ +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetVolumeChannelCount(_Out_ UINT32 *_puiChannelCount) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRTStream::GetVolumeChannelCount]")); + + ASSERT(_puiChannelCount); + ASSERT(m_pWfExt); + + NTSTATUS ntStatus = STATUS_SUCCESS; + *_puiChannelCount = m_pWfExt->Format.nChannels; + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetVolumeSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRTStream::GetVolumeSteppings]")); + + if (ulChannelCount != m_pWfExt->Format.nChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = VOLUME_STEPPING_DELTA; + _pKsPropStepLong[i].Bounds.SignedMaximum = VOLUME_SIGNED_MAXIMUM; + _pKsPropStepLong[i].Bounds.SignedMinimum = VOLUME_SIGNED_MINIMUM; + } + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetChannelVolume(_In_ UINT32 _uiChannel, _Out_ LONG *_pVolume) +{ + PAGED_CODE (); + ASSERT (_pVolume); + DPF_ENTER(("[CMiniportWaveRTStream::GetChannelVolume]")); + + *_pVolume = m_plVolumeLevel[_uiChannel]; + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::SetChannelVolume(_In_ UINT32 _uiChannel, _In_ LONG _Volume) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetChannelVolume]")); + + m_plVolumeLevel[_uiChannel] = _Volume; + + return STATUS_SUCCESS; +} +///- metering +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetPeakMeterChannelCount(_Out_ UINT32 *puiChannelCount) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRTStream::GetPeakMeterChannelCount]")); + + ASSERT(puiChannelCount); + ASSERT(m_pWfExt); + + NTSTATUS ntStatus = STATUS_SUCCESS; + *puiChannelCount = m_pWfExt->Format.nChannels; + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetPeakMeterSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRTStream::GetPeakMeterSteppings]")); + + if (ulChannelCount != m_pWfExt->Format.nChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = PEAKMETER_STEPPING_DELTA; + _pKsPropStepLong[i].Bounds.SignedMaximum = PEAKMETER_SIGNED_MAXIMUM; + _pKsPropStepLong[i].Bounds.SignedMinimum = PEAKMETER_SIGNED_MINIMUM; + } + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetChannelPeakMeter(_In_ UINT32 _uiChannel, _Out_ LONG *_plPeakMeter) +{ + PAGED_CODE (); + ASSERT (_plPeakMeter); + UNREFERENCED_PARAMETER(_uiChannel); + DPF_ENTER(("[CMiniportWaveRTStream::GetChannelPeakMeter]")); + + *_plPeakMeter = PEAKMETER_NORMALIZE_IN_RANGE(PEAKMETER_SIGNED_MAXIMUM / 2); + + return STATUS_SUCCESS; +} + + //============================================================================= +// stream Mute +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetMuteChannelCount(_Out_ UINT32 *puiChannelCount) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRTStream::GetMuteChannelCount]")); + + ASSERT(puiChannelCount); + ASSERT(m_pWfExt); + + NTSTATUS ntStatus = STATUS_SUCCESS; + *puiChannelCount = m_pWfExt->Format.nChannels; + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetMuteSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRTStream::GetMuteSteppings]")); + + if (ulChannelCount != m_pWfExt->Format.nChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = 1; + _pKsPropStepLong[i].Bounds.SignedMaximum = TRUE; + _pKsPropStepLong[i].Bounds.SignedMinimum = FALSE; + } + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetChannelMute(_In_ UINT32 _uiChannel, _Out_ BOOL *_pbMute) +{ + PAGED_CODE (); + ASSERT (_pbMute); + DPF_ENTER(("[CMiniportWaveRTStream::GetChannelMute]")); + + *_pbMute = m_pbMuted[_uiChannel]; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::SetChannelMute(_In_ UINT32 _uiChannel, _In_ BOOL _bMute) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetChannelMute]")); + + m_plVolumeLevel[_uiChannel] = _bMute; + + return STATUS_SUCCESS; +} +//presentation +#pragma code_seg("PAGE") +// +// UINT64 u64PositionInBlocks; // The block offset from the start of the stream to the current post-decoded uncompressed +// // position in the stream, where a block is the group of channels in the same sample; for a PCM stream, +// // a block is same as a frame. For compressed formats, a block is a single sample within a frame +// // (eg. each MP3 frame has 1152 samples or 1152 blocks) +// UINT64 u64QPCPosition; // The value of the performance counter at the time that the audio endpoint device read the device +// // position (*pu64Position) in response to the KSAUDIO_PRESENTATION_POSITION call. + +NTSTATUS CMiniportWaveRTStream::GetPresentationPosition(_Out_ KSAUDIO_PRESENTATION_POSITION *_pPresentationPosition) +{ + PAGED_CODE (); + ASSERT (_pPresentationPosition); + LARGE_INTEGER timeStamp; + PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj(); + + DPF_ENTER(("[CMiniportWaveRTStream::GetPresentationPosition]")); + + ULONGLONG ullLinearPosition = {0}; + NTSTATUS status = STATUS_SUCCESS; + + status = GetLinearBufferPosition(&ullLinearPosition, &timeStamp); + if (!NT_SUCCESS(status)) + { + return status; + } + + _pPresentationPosition->u64PositionInBlocks = ullLinearPosition * m_pWfExt->Format.nSamplesPerSec / m_pWfExt->Format.nAvgBytesPerSec; + + _pPresentationPosition->u64QPCPosition = (UINT64)timeStamp.QuadPart; + + + //Event type: eMINIPORT_GET_PRESENTATION_POSITION + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: Presentation position + //Parameter 4:0 + pAdapterComm->WriteEtwEvent(eMINIPORT_GET_PRESENTATION_POSITION, + ullLinearPosition, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, + _pPresentationPosition->u64PositionInBlocks, + 0); // always zero + + return STATUS_SUCCESS; +} +NTSTATUS CMiniportWaveRTStream::SetCurrentWritePosition(_In_ ULONG _ulCurrentWritePosition) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetCurrentWritePosition]")); + + NTSTATUS ntStatus; + +#ifdef SYSVAD_BTH_BYPASS + if (m_ScoOpen) + { + ntStatus = GetScoStreamNtStatus(); + IF_FAILED_JUMP(ntStatus, Done); + } +#endif // SYSVAD_BTH_BYPASS + + // + // Basic validation. WritePosition indicates the position (1-based) of the last valid byte. + // + if (_ulCurrentWritePosition == 0) + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + goto Done; + } + + ntStatus = SetCurrentWritePositionInternal(_ulCurrentWritePosition); + +Done: + return ntStatus; +} +NTSTATUS CMiniportWaveRTStream::SetCurrentWritePositionInternal(_In_ ULONG _ulCurrentWritePosition) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetCurrentWritePositionInternal]")); + + if (_ulCurrentWritePosition > m_ulDmaBufferSize) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj(); + + //Event type: eMINIPORT_SET_WAVERT_BUFFER_WRITE_POSITION + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: Target WaveRtBufferWritePosition received from portcls + //Parameter 4:0 + pAdapterComm->WriteEtwEvent(eMINIPORT_SET_WAVERT_BUFFER_WRITE_POSITION, + 100, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, + _ulCurrentWritePosition, // this is the passed in parameter + 0); // always zero + + + // + // Check for eMINIPORT_GLITCH_REPORT - 'same writert buffer' only when in event mode. + // + if (m_ulNotificationIntervalMs > 0) + { + if (m_ulCurrentWritePosition == _ulCurrentWritePosition) + { + //Event type: eMINIPORT_GLITCH_REPORT + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: major glitch code: 3:receive the same wavert buffer two in a row in event driven mode + //Parameter 4: minor code for the glitch cause + pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT, + 100, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, + 3, // receive the same wavert buffer two in a row in event driven mode + _ulCurrentWritePosition); + } + } + + m_ulCurrentWritePosition = _ulCurrentWritePosition; + InterlockedExchange(&m_IsCurrentWritePositionUpdated, 1); + + return STATUS_SUCCESS; +} +//linear position +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetLinearBufferPosition(_Out_ ULONGLONG *_pullLinearBufferPosition, LARGE_INTEGER *_pliQPCTime) +{ + PAGED_CODE (); + ASSERT (_pullLinearBufferPosition); + DPF_ENTER(("[CMiniportWaveRTStream::GetLinearBufferPosition]")); + + NTSTATUS ntStatus; + LARGE_INTEGER ilQPC; + +#ifdef SYSVAD_BTH_BYPASS + if (m_ScoOpen) + { + ntStatus = GetScoStreamNtStatus(); + IF_FAILED_JUMP(ntStatus, Done); + } +#endif // SYSVAD_BTH_BYPASS + + // Update *_pullLinearBufferPosition with the the number of bytes fetched from waveRT ever since a stream got set into RUN + // state. + // Once the stream is set to STOP state, any further read on this call would return zero. + + // + // Get the current time and update position. + // + ilQPC = KeQueryPerformanceCounter(NULL); + if (m_KsState == KSSTATE_RUN) + { + UpdatePosition(ilQPC); + } + if (_pliQPCTime) + { + *_pliQPCTime = ilQPC; + } + *_pullLinearBufferPosition = m_ullLinearPosition; + + ntStatus = STATUS_SUCCESS; + +#ifdef SYSVAD_BTH_BYPASS +Done: +#endif // SYSVAD_BTH_BYPASS + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +CMiniportWaveRTStream::SetLoopbackProtection + +Decscription: + + Portcls calls this method, inside its property handlers, to set the loopback + protection option + +Parameters: + _In_ protectionOption: protection option + CONSTRICTOR_OPTION_DISABLE: Turn protection off. + CONSTRICTOR_OPTION_MUTE: Mute the loopback stream +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + This content protection settings requestion could come in from the host process + pin or offload pin. The miniport needs to mute its loopback stream contents for + for this request from either host process pin or offload pin. +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetLoopbackProtection(_In_ CONSTRICTOR_OPTION protectionOption) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetLoopbackProtection]")); + + // + // Miniport driver mutes/unmutes the loopback here. + // + m_ToneGenerator.SetMute(protectionOption == CONSTRICTOR_OPTION_MUTE); + + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +//---------------------------------------------------------------------------------- +// Description: +// Set current write position for the very last buffer in a stream +// +// Parameters: +// ULONG _ulWritePosition: point to the last valid data byte +// +// Remark: +// +// In Win 8, for offload streams, double buffering mechanism is used and the buffer completion contact +// between the audio stack and driver is: a driver signals buffer completion only when it reaches the +// end of either buffer. Thus, there is no concept of partial buffer (not all the data in a buffer are +// valid, only the first N frames are valid and the rest of the buffer might contain invalid or silence +// data. So, when the very last buffer is partially filled, its completion will not get singled until +// all data is consumed, which might become undesirable when the size of a buffer is large and valid frame +// count is low. +// In WinBlue, a driver implements IMiniportStreamAudioEngineNode2 to claim being able to handle the very +// last buffer that's partial filled. +// For a driver claiming supporting IMiniportStreamAudioEngineNode2, for the very last buffer position write, +// it will receive the very last buffer write via SetStreamCurrentWritePositionForLastBuffer() call instead of +// SetStreamCurrentWritePosition(). A driver should check _ulWritePosition parameter to see how much valid data +// in the buffer (instead of blindly assuming all the data are valid) before signaling buffer completion. +// No more SetStreamCurrentWritePositionForLastBuffer or SetStreamCurrentWritePosition will be called after this +// SetStreamCurrentWritePositionForLastBuffer() call. +//----------------------------------------------------------------------------------------------------------------------- + +NTSTATUS CMiniportWaveRTStream::SetStreamCurrentWritePositionForLastBuffer(_In_ ULONG _ulWritePosition) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRT::SetStreamCurrentWritePositionForLastBuffer]")); + + return SetCurrentWritePositionInternal(_ulWritePosition); + // Miniport driver needs to prepare to signal buffer completion event + // when it's done with reading the last valid byte - an _ulWritePosition offset from the beginning WaveRT buffer + // Note: _ulWritePosition will be smaller than buffer size in most of the cases +} + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp b/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp new file mode 100644 index 00000000..ec3f4367 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp @@ -0,0 +1,281 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmictopo.cpp + +Abstract: + + Implementation of topology miniport for the mic (external: headphone). + +--*/ +#ifdef SYSVAD_BTH_BYPASS + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "bthhfptopo.h" +#include "bthhfpmictopo.h" +#include "bthhfpmictoptable.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpMicTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_JACK_DESCRIPTION: + ntStatus = PropertyHandler_BthHfpJackDescription( + PropertyRequest, + ARRAYSIZE(BthHfpMicJackDescriptions), + BthHfpMicJackDescriptions); + break; + + case KSPROPERTY_JACK_DESCRIPTION2: + ntStatus = PropertyHandler_BthHfpJackDescription2( + PropertyRequest, + ARRAYSIZE(BthHfpMicJackDescriptions), + BthHfpMicJackDescriptions); + break; + + case KSPROPERTY_JACK_CONTAINERID: + ntStatus = PropertyHandler_BthHfpJackContainerId( + PropertyRequest, + ARRAYSIZE(BthHfpMicJackDescriptions), + BthHfpMicJackDescriptions); + break; + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_BtAudio)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_ONESHOT_RECONNECT: + ntStatus = PropertyHandler_BthHfpOneShotReconnect(PropertyRequest); + break; + + case KSPROPERTY_ONESHOT_DISCONNECT: + ntStatus = PropertyHandler_BthHfpOneDisconnect(PropertyRequest); + break; + } + } + + return ntStatus; +} // PropertyHandler_BthHfpMicTopoFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpMicVolumeLevel +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicVolumeLevel]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + ULONG channel = (ULONG)-1; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + if (bthHfpDevice->IsVolumeSupported() == FALSE) + { + ntStatus = miniport->PropertyHandlerGeneric(PropertyRequest); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BthHfpVolumeLevel_BasicSupport(PropertyRequest); + } + else if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + // Instance is the channel #, Bluetooth HFP supports mono streams. + channel = *(PULONG(PropertyRequest->Instance)); + + if (channel == 0) + { + ULONG cbNeeded = sizeof(LONG); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + LONG* volume = (LONG*)PropertyRequest->Value; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *volume = bthHfpDevice->GetMicVolume(); + + ntStatus = STATUS_SUCCESS; + + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + ntStatus = bthHfpDevice->SetMicVolume(*volume); + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpMicTopoNode +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicTopoNode]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL) + { + ntStatus = PropertyHandler_BthHfpMicVolumeLevel(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_BthHfpMicTopoFilter + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpMicTopoNodeEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicTopoNodeEvent]")); + + // Validate the node. + if (EventRequest->Node != KSNODE_TOPO_VOLUME) + return STATUS_INVALID_PARAMETER; + + return PropertyHandler_BthHfpTopoNodeEvent(EventRequest); +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpMicTopoFilterEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicTopoFilterEvent]")); + + return PropertyHandler_BthHfpTopoNodeEvent(EventRequest); +} + +#pragma code_seg() +#endif // SYSVAD_BTH_BYPASS + + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmictopo.h b/audio/sysvad/EndpointsCommon/bthhfpmictopo.h new file mode 100644 index 00000000..48f93a69 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmictopo.h @@ -0,0 +1,41 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmictopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: headphone). + +--*/ + +#ifndef _SYSVAD_BTHHFPMICTOPO_H_ +#define _SYSVAD_BTHHFPMICTOPO_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_BthHfpMicTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpMicTopoNode( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpMicTopoNodeEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +NTSTATUS +PropertyHandler_BthHfpMicTopoFilterEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +#endif // _SYSVAD_BTHHFPMICTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmictoptable.h b/audio/sysvad/EndpointsCommon/bthhfpmictoptable.h new file mode 100644 index 00000000..0ea69956 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmictoptable.h @@ -0,0 +1,279 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmictopotable.h + +Abstract: + + Declaration of topology table for the Bluetooth hands-free profile mic (external) + +--*/ + +#ifndef _SYSVAD_BTHHFPMICTOPTABLE_H_ +#define _SYSVAD_BTHHFPMICTOPTABLE_H_ + +#include "bthhfpmictopo.h" + +//============================================================================= +static +KSDATARANGE BthHfpMicTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE BthHfpMicTopoPinDataRangePointersBridge[] = +{ + &BthHfpMicTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpMicTopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(BthHfpMicTopoPinDataRangePointersBridge),// DataRangesCount + BthHfpMicTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_MICROPHONE, // Category + NULL, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(BthHfpMicTopoPinDataRangePointersBridge),// DataRangesCount + BthHfpMicTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION BthHfpMicJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0, // color + eConnTypeOtherDigital, + eGeoLocNotApplicable, + eGenLocOther, + ePortConnUnknown, + FALSE // run-time code sets this value. +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION BthHfpMicJackDescriptions[] = +{ + &BthHfpMicJackDesc, + NULL +}; + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpMicPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoNode + } +}; + +static +PCEVENT_ITEM BthHfpMicPropertiesVolumeEvent[] = +{ + // This is a generic event for nearly every node property. + { + &KSEVENTSETID_AudioControlChange, // Something changed! + KSEVENT_CONTROL_CHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoNodeEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationBthHfpMicVolumeWithEvent, + BthHfpMicPropertiesVolume, + BthHfpMicPropertiesVolumeEvent); + + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpMicPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpMicMute, BthHfpMicPropertiesMute); + +//============================================================================= +// The volume node should be implemented as follows: +// +// #1 If the Bluetooth headset supports volume control, then the audio driver should +// include a volume node in its KS topology. The audio driver's volume property +// handlers send the above IOCLTs to the Bluetooth HFP driver to handle the volume. +// +// #2 If the Bluetooth headset does not implement a hardware volume, and the codec +// (or DSP) has a hardware volume, the audio driver should handle the volume control +// on the codec (or DSP). +// +// #3 If the Bluetooth headset and the audio device do not have hardware volume controls, +// no volume node should be presented and Windows will insert a software volume +// control node. +// +// This sample assumes #1 or #2, and it adds a valume node in its topology. +// +// The mute node is opitional. The driver shall implement the mute node if and only if +// the DSP or audio codec provides the capability to mute the bypass PCM signal before +// passing it to the Bluetooth controller. The mute nodes support +// KSPROPSETID_Audio/KSPROPERTY_AUDIO_MUTE +// +static +PCNODE_DESCRIPTOR BthHfpMicTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationBthHfpMicVolumeWithEvent, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationBthHfpMicMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR BthHfpMicMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpMicPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_CONTAINERID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_RECONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_DISCONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + } +}; + +static +PCEVENT_ITEM BthHfpMicTopoPropertiesFilterEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed! + KSEVENT_PINCAPS_JACKINFOCHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilterEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationBthHfpMicTopoFilterWithEvent, + BthHfpMicPropertiesTopoFilter, + BthHfpMicTopoPropertiesFilterEvent); + + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpMicTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpMicTopoFilterWithEvent, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpMicTopoMiniportPins), // PinCount + BthHfpMicTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpMicTopologyNodes), // NodeCount + BthHfpMicTopologyNodes, // Nodes + SIZEOF_ARRAY(BthHfpMicMiniportConnections),// ConnectionCount + BthHfpMicMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_BTHHFPMICTOPTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmicwavtable.h b/audio/sysvad/EndpointsCommon/bthhfpmicwavtable.h new file mode 100644 index 00000000..fe28f3f3 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmicwavtable.h @@ -0,0 +1,292 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmicwavtable.h + +Abstract: + + Declaration of wave miniport tables for the Bluetooth handls-free profile (external). + +--*/ + +#ifndef _SYSVAD_BTHHFPMICWAVTABLE_H_ +#define _SYSVAD_BTHHFPMICWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_BthHfpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +// +// Bluetooth Headset Mic (external) range. +// +#define BTHHFPMIC_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPMIC_MIN_BITS_PER_SAMPLE_PCM 8 // Min Bits Per Sample +#define BTHHFPMIC_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define BTHHFPMIC_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPMIC_MAX_SAMPLE_RATE 8000 // Max Sample Rate + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpMicPinSupportedDeviceFormats[] = +{ + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins) - NREC not supported. +// +static +MODE_AND_DEFAULT_FORMAT BthHfpMicPinSupportedDeviceModesNoNrec[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpMicPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpMicPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// Supported modes (only on streaming pins) - NREC supported. +// +static +MODE_AND_DEFAULT_FORMAT BthHfpMicPinSupportedDeviceModesNrec[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &BthHfpMicPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpMicPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES BthHfpMicPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + BthHfpMicPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpMicPinSupportedDeviceFormats), + NULL, // Init dynamically, see GetCapturePinSupportedDeviceModes[Count] + 0 // Init dynamically, see GetCapturePinSupportedDeviceModes[Count] + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO BthHfpMicPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPMIC_DEVICE_MAX_CHANNELS, + BTHHFPMIC_MIN_BITS_PER_SAMPLE_PCM, + BTHHFPMIC_MAX_BITS_PER_SAMPLE_PCM, + BTHHFPMIC_MIN_SAMPLE_RATE, + BTHHFPMIC_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE BthHfpMicPinDataRangePointersStream[] = +{ + PKSDATARANGE(&BthHfpMicPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +//============================================================================= +static +KSDATARANGE BthHfpMicPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE BthHfpMicPinDataRangePointersBridge[] = +{ + &BthHfpMicPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpMicWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpMicPinDataRangePointersBridge), + BthHfpMicPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + MAX_INPUT_STREAMS, + MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpMicPinDataRangePointersStream), + BthHfpMicPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR BthHfpMicWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR BthHfpMicWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesBthHfpMicWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpMicWaveFilter, PropertiesBthHfpMicWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpMicWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpMicWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpMicWaveMiniportPins), // PinCount + BthHfpMicWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpMicWaveMiniportNodes), // NodeCount + BthHfpMicWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(BthHfpMicWaveMiniportConnections), // ConnectionCount + BthHfpMicWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_BTHHFPMICWAVTABLE_H_ diff --git a/audio/sysvad/EndpointsCommon/bthhfpmicwbwavtable.h b/audio/sysvad/EndpointsCommon/bthhfpmicwbwavtable.h new file mode 100644 index 00000000..9a41f460 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmicwbwavtable.h @@ -0,0 +1,361 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmicwbwavtable.h + +Abstract: + + Declaration of wave miniport tables for the Bluetooth handls-free profile (external), + for Bluetooth connections that support Wideband Speech. + +--*/ + +#ifndef _SYSVAD_BTHHFPMICWIDEBANDWAVTABLE_H_ +#define _SYSVAD_BTHHFPMICWBWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_BthHfpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +// +// Bluetooth Headset Mic (external) range. +// +#define BTHHFPMICWB_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPMICWB_MIN_BITS_PER_SAMPLE_PCM 8 // Min Bits Per Sample +#define BTHHFPMICWB_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define BTHHFPMICWB_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPMICWB_MAX_SAMPLE_RATE 16000 // Max Sample Rate + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpMicWBPinSupportedDeviceFormats[] = +{ + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins) - NREC not supported. +// +static +MODE_AND_DEFAULT_FORMAT BthHfpMicWBPinSupportedDeviceModesNoNrec[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpMicWBPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpMicWBPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// Supported modes (only on streaming pins) - NREC supported. +// +static +MODE_AND_DEFAULT_FORMAT BthHfpMicWBPinSupportedDeviceModesNrec[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &BthHfpMicWBPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpMicWBPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES BthHfpMicWBPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + BthHfpMicWBPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpMicWBPinSupportedDeviceFormats), + NULL, // Init dynamically, see GetCapturePinSupportedDeviceModes[Count] + 0 // Init dynamically, see GetCapturePinSupportedDeviceModes[Count] + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO BthHfpMicWBPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPMICWB_DEVICE_MAX_CHANNELS, + BTHHFPMICWB_MIN_BITS_PER_SAMPLE_PCM, + BTHHFPMICWB_MAX_BITS_PER_SAMPLE_PCM, + BTHHFPMICWB_MIN_SAMPLE_RATE, + BTHHFPMICWB_MIN_SAMPLE_RATE + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPMICWB_DEVICE_MAX_CHANNELS, + BTHHFPMICWB_MIN_BITS_PER_SAMPLE_PCM, + BTHHFPMICWB_MAX_BITS_PER_SAMPLE_PCM, + BTHHFPMICWB_MAX_SAMPLE_RATE, + BTHHFPMICWB_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE BthHfpMicWBPinDataRangePointersStream[] = +{ + PKSDATARANGE(&BthHfpMicWBPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&BthHfpMicWBPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +//============================================================================= +static +KSDATARANGE BthHfpMicWBPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE BthHfpMicWBPinDataRangePointersBridge[] = +{ + &BthHfpMicWBPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpMicWBWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpMicWBPinDataRangePointersBridge), + BthHfpMicWBPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + MAX_INPUT_STREAMS, + MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpMicWBPinDataRangePointersStream), + BthHfpMicWBPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR BthHfpMicWBWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR BthHfpMicWBWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesBthHfpMicWBWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpMicWBWaveFilter, PropertiesBthHfpMicWBWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpMicWBWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpMicWBWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpMicWBWaveMiniportPins), // PinCount + BthHfpMicWBWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpMicWBWaveMiniportNodes), // NodeCount + BthHfpMicWBWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(BthHfpMicWBWaveMiniportConnections), // ConnectionCount + BthHfpMicWBWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_BTHHFPMICWBWAVTABLE_H_ diff --git a/audio/sysvad/EndpointsCommon/bthhfpminipairs.h b/audio/sysvad/EndpointsCommon/bthhfpminipairs.h new file mode 100644 index 00000000..7d2b82f7 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpminipairs.h @@ -0,0 +1,224 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpminipairs.h + +Abstract: + + Bluetooth HFP audio endpoint filter definitions. + +--*/ + +#ifndef _SYSVAD_BTHHFPMINIPAIRS_H_ +#define _SYSVAD_BTHHFPMINIPAIRS_H_ + +#include "bthhfpspeakertopo.h" +#include "bthhfpspeakertoptable.h" +#include "bthhfpspeakerwavtable.h" +#include "bthhfpspeakerwbwavtable.h" + +#include "bthhfpmictopo.h" +#include "bthhfpmictoptable.h" +#include "bthhfpmicwavtable.h" +#include "bthhfpmicwbwavtable.h" + +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +// +// Render miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for BTH-HFP speaker * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE BthHfpSpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR BthHfpSpeakerMiniports = +{ + eBthHfpSpeakerDevice, + L"TopologyBthHfpSpeaker", // make sure this name matches with SYSVAD.TopologyBthHfpSpeaker.szPname in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &BthHfpSpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveBthHfpSpeaker", // make sure this name matches with SYSVAD.WaveBthHfpSpeaker.szPname in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &BthHfpSpeakerWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + BthHfpSpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(BthHfpSpeakerPinDeviceFormatsAndModes), + BthHfpSpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(BthHfpSpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +static +ENDPOINT_MINIPAIR BthHfpSpeakerWBMiniports = +{ + eBthHfpSpeakerDevice, + L"TopologyBthHfpSpeaker", // make sure this name matches with SYSVAD.TopologyBthHfpSpeaker.szPname in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &BthHfpSpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveBthHfpSpeaker", // make sure this name matches with SYSVAD.WaveBthHfpSpeaker.szPname in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &BthHfpSpeakerWBWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + BthHfpSpeakerWBPinDeviceFormatsAndModes, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDeviceFormatsAndModes), + BthHfpSpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(BthHfpSpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +// +// Capture miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for BTH-HFP mic * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE BthHfpMicTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR BthHfpMicMiniports = +{ + eBthHfpMicDevice, + L"TopologyBthHfpMic", // make sure this name matches with SYSVAD.TopologyBthHfpMic.szPname in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &BthHfpMicTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveBthHfpMic", // make sure this name matches with SYSVAD.WaveBthHfpMic.szPname in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &BthHfpMicWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + BthHfpMicPinDeviceFormatsAndModes, + SIZEOF_ARRAY(BthHfpMicPinDeviceFormatsAndModes), + BthHfpMicTopologyPhysicalConnections, + SIZEOF_ARRAY(BthHfpMicTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +static +ENDPOINT_MINIPAIR BthHfpMicWBMiniports = +{ + eBthHfpMicDevice, + L"TopologyBthHfpMic", // make sure this name matches with SYSVAD.TopologyBthHfpMic.szPname in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &BthHfpMicTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveBthHfpMic", // make sure this name matches with SYSVAD.WaveBthHfpMic.szPname in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &BthHfpMicWBWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + BthHfpMicWBPinDeviceFormatsAndModes, + SIZEOF_ARRAY(BthHfpMicWBPinDeviceFormatsAndModes), + BthHfpMicTopologyPhysicalConnections, + SIZEOF_ARRAY(BthHfpMicTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +//============================================================================= +// +// Render miniport pairs. +// +static +PENDPOINT_MINIPAIR g_BthHfpRenderEndpoints[] = +{ + &BthHfpSpeakerMiniports, + &BthHfpSpeakerWBMiniports +}; + +#define g_cBthHfpRenderEndpoints (SIZEOF_ARRAY(g_BthHfpRenderEndpoints)) + +// If the count of Render endpoints changes, update formula below +C_ASSERT(g_cBthHfpRenderEndpoints == 2); + +//============================================================================= +// +// Capture miniport pairs. +// +static +PENDPOINT_MINIPAIR g_BthHfpCaptureEndpoints[] = +{ + &BthHfpMicMiniports, + &BthHfpMicWBMiniports +}; + +#define g_cBthHfpCaptureEndpoints (SIZEOF_ARRAY(g_BthHfpCaptureEndpoints)) + +// If the count of Capture endpoints changes, update formula below +C_ASSERT(g_cBthHfpCaptureEndpoints == 2); + +//============================================================================= +// +// Total miniports = # endpoints * 2 (topology + wave) / 2 (since we have one each for narrowband-only and wideband) +// +#define g_MaxBthHfpMiniports ((g_cBthHfpRenderEndpoints + g_cBthHfpCaptureEndpoints)) + +// g_MaxBthHfpMiniports is used when calculating the MaxObjects inside AddDevice. +C_ASSERT(g_MaxBthHfpMiniports == 4); + +#endif // _SYSVAD_BTHHFPMINIPAIRS_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpminwavert.cpp b/audio/sysvad/EndpointsCommon/bthhfpminwavert.cpp new file mode 100644 index 00000000..77f12e41 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpminwavert.cpp @@ -0,0 +1,206 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpminwavert.cpp + +Abstract: + + Implementation of wavert miniport. + +--*/ +#ifdef SYSVAD_BTH_BYPASS + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include <limits.h> +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "IHVPrivatePropertySet.h" + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandler_BthHfpAudioEffectsDiscoveryEffectsList +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_AudioEffectsDiscovery, KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[PropertyHandler_BthHfpAudioEffectsDiscoveryEffectsList]")); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + ULONG nPinId = (ULONG)-1; + + // + // Validate Pin ID. + // + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + // This prop is valid only on streaming pins. + if (IsSystemRenderPin(nPinId) || IsSystemCapturePin(nPinId)) + { + ntStatus = STATUS_SUCCESS; + } + else if (IsBridgePin(nPinId) || + IsLoopbackPin(nPinId) || + IsOffloadPin(nPinId)) + { + ntStatus = STATUS_NOT_SUPPORTED; + } + } + + IF_FAILED_JUMP(ntStatus, Done); + + // + // Valid actions: get and basicsupport. + // + ntStatus = STATUS_INVALID_PARAMETER; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ASSERT(m_BthHfpDevice != NULL); + + // + // bHfpNrecPresent is set to TRUE when the HF (remote device) handles NR + EC. + // Note that GetNRECDisableStatus() returns TRUE if the HF notified the AG to disable + // the NR + EC locally. + // + BOOL bHfpNrecPresent = m_BthHfpDevice->IsNRECSupported() && m_BthHfpDevice->GetNRECDisableStatus(); + + // If it is HFP render pin (data flow in) or if the NR-EC is not supported, + // return size 0 effect list + if (IsSystemRenderPin(nPinId) || !bHfpNrecPresent) + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_SUCCESS; + } + else + { + // Compute total size, two effects: NS and EC (see below). + ULONG cbMinSize = sizeof(GUID) * 2; + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + PGUID effectList = PGUID(PropertyRequest->Value); + + *effectList = AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION; + *(effectList + 1) = AUDIO_EFFECT_TYPE_NOISE_SUPPRESSION; + + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_SUCCESS; + } + } + } + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpWaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CMiniportWaveRT* waveRt = reinterpret_cast<CMiniportWaveRT*>(PropertyRequest->MajorTarget); + + if (waveRt == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + waveRt->AddRef(); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Pin)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_PIN_PROPOSEDATAFORMAT: + ntStatus = waveRt->PropertyHandlerProposedFormat(PropertyRequest); + break; + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_AudioEffectsDiscovery)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST: + ntStatus = waveRt->PropertyHandler_BthHfpAudioEffectsDiscoveryEffectsList(PropertyRequest); + break; + } + } + + waveRt->Release(); + + return ntStatus; +} // PropertyHandler_BthHfpWaveFilter + +#pragma code_seg("PAGE") +#endif // SYSVAD_BTH_BYPASS + + + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp new file mode 100644 index 00000000..8697d973 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp @@ -0,0 +1,281 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakertopo.cpp + +Abstract: + + Implementation of topology miniport for the Bluetooth Hands-Free Profile speaker (external). + +--*/ +#ifdef SYSVAD_BTH_BYPASS + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "bthhfptopo.h" +#include "bthhfpspeakertopo.h" +#include "bthhfpspeakertoptable.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_JACK_DESCRIPTION: + ntStatus = PropertyHandler_BthHfpJackDescription( + PropertyRequest, + ARRAYSIZE(BthHfpSpeakerJackDescriptions), + BthHfpSpeakerJackDescriptions); + break; + + case KSPROPERTY_JACK_DESCRIPTION2: + ntStatus = PropertyHandler_BthHfpJackDescription2( + PropertyRequest, + ARRAYSIZE(BthHfpSpeakerJackDescriptions), + BthHfpSpeakerJackDescriptions); + break; + + case KSPROPERTY_JACK_CONTAINERID: + ntStatus = PropertyHandler_BthHfpJackContainerId( + PropertyRequest, + ARRAYSIZE(BthHfpSpeakerJackDescriptions), + BthHfpSpeakerJackDescriptions); + break; + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_BtAudio)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_ONESHOT_RECONNECT: + ntStatus = PropertyHandler_BthHfpOneShotReconnect(PropertyRequest); + break; + + case KSPROPERTY_ONESHOT_DISCONNECT: + ntStatus = PropertyHandler_BthHfpOneDisconnect(PropertyRequest); + break; + } + } + + return ntStatus; +} // PropertyHandler_BthHfpSpeakerTopoFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpSpeakerVolumeLevel +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerVolumeLevel]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + ULONG channel = (ULONG)-1; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + if (bthHfpDevice->IsVolumeSupported() == FALSE) + { + ntStatus = miniport->PropertyHandlerGeneric(PropertyRequest); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BthHfpVolumeLevel_BasicSupport(PropertyRequest); + } + else if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + // Instance is the channel #, Bluetooth HFP supports mono streams. + channel = *(PULONG(PropertyRequest->Instance)); + + if (channel == 0) + { + ULONG cbNeeded = sizeof(LONG); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + LONG* volume = (LONG*)PropertyRequest->Value; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *volume = bthHfpDevice->GetSpeakerVolume(); + + ntStatus = STATUS_SUCCESS; + + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + ntStatus = bthHfpDevice->SetSpeakerVolume(*volume); + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoNode +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerTopoNode]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL) + { + ntStatus = PropertyHandler_BthHfpSpeakerVolumeLevel(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_BthHfpSpeakerTopoFilter + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoNodeEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerTopoNodeEvent]")); + + // Validate the node. + if (EventRequest->Node != KSNODE_TOPO_VOLUME) + return STATUS_INVALID_PARAMETER; + + return PropertyHandler_BthHfpTopoNodeEvent(EventRequest); +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoFilterEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerTopoFilterEvent]")); + + return PropertyHandler_BthHfpTopoNodeEvent(EventRequest); +} + +#pragma code_seg() +#endif // SYSVAD_BTH_BYPASS + + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.h b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.h new file mode 100644 index 00000000..3ef8482d --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.h @@ -0,0 +1,41 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakertopo.h + +Abstract: + + Declaration of topology miniport for the Bluetooth Hands-Free Profile speaker (external). + +--*/ + +#ifndef _SYSVAD_BTHHFPSPEAKERTOPO_H_ +#define _SYSVAD_BTHHFPSPEAKERTOPO_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoNode( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoNodeEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoFilterEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +#endif // _SYSVAD_BTHHFPSPEAKERTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakertoptable.h b/audio/sysvad/EndpointsCommon/bthhfpspeakertoptable.h new file mode 100644 index 00000000..48b66f03 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakertoptable.h @@ -0,0 +1,275 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakertoptable.h + +Abstract: + + Declaration of topology tables for the Bluetooth Hands-Free Profile speaker (external). + +--*/ + +#ifndef _SYSVAD_BTHHFPSPEAKERTOPTABLE_H_ +#define _SYSVAD_BTHHFPSPEAKERTOPTABLE_H_ + +#include "bthhfpspeakertopo.h" + +//============================================================================= +static +KSDATARANGE BthHfpSpeakerTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE BthHfpSpeakerTopoPinDataRangePointersBridge[] = +{ + &BthHfpSpeakerTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpSpeakerTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(BthHfpSpeakerTopoPinDataRangePointersBridge),// DataRangesCount + BthHfpSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(BthHfpSpeakerTopoPinDataRangePointersBridge),// DataRangesCount + BthHfpSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_SPEAKERS, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION BthHfpSpeakerJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0, // color + eConnTypeOtherDigital, + eGeoLocNotApplicable, + eGenLocOther, + ePortConnUnknown, + FALSE // run-time code sets this value. +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION BthHfpSpeakerJackDescriptions[] = +{ + NULL, + &BthHfpSpeakerJackDesc +}; + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpSpeakerPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoNode + } +}; + +static +PCEVENT_ITEM BthHfpSpeakerPropertiesVolumeEvent[] = +{ + { + &KSEVENTSETID_AudioControlChange, // Something changed! + KSEVENT_CONTROL_CHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoNodeEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationBthHfpSpeakerVolumeWithEvent, + BthHfpSpeakerPropertiesVolume, + BthHfpSpeakerPropertiesVolumeEvent); + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpSpeakerPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerMute, BthHfpSpeakerPropertiesMute); + +//============================================================================= +// The volume node should be implemented as follows: +// +// #1 If the Bluetooth headset supports volume control, then the audio driver should +// include a volume node in its KS topology. The audio driver's volume property +// handlers send the above IOCLTs to the Bluetooth HFP driver to handle the volume. +// +// #2 If the Bluetooth headset does not implement a hardware volume, and the codec +// (or DSP) has a hardware volume, the audio driver should handle the volume control +// on the codec (or DSP). +// +// #3 If the Bluetooth headset and the audio device do not have hardware volume controls, +// no volume node should be presented and Windows will insert a software volume +// control node. +// +// This sample assumes #1 or #2, and it adds a valume node in its topology. +// +// The mute node is opitional. The driver shall implement the mute node if and only if +// the DSP or audio codec provides the capability to mute the bypass PCM signal before +// passing it to the Bluetooth controller. The mute nodes support +// KSPROPSETID_Audio/KSPROPERTY_AUDIO_MUTE +// +static +PCNODE_DESCRIPTOR BthHfpSpeakerTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationBthHfpSpeakerVolumeWithEvent, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_LINE_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationBthHfpSpeakerMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_LINE_MUTE // Name + }, +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR BthHfpSpeakerTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpSpeakerPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_CONTAINERID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_RECONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_DISCONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + } +}; + +static +PCEVENT_ITEM BthHfpSpeakerPropertiesTopoFilterEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed! + KSEVENT_PINCAPS_JACKINFOCHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilterEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationBthHfpSpeakerTopoFilterWithEvent, + BthHfpSpeakerPropertiesTopoFilter, + BthHfpSpeakerPropertiesTopoFilterEvent); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpSpeakerTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpSpeakerTopoFilterWithEvent, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpSpeakerTopoMiniportPins), // PinCount + BthHfpSpeakerTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpSpeakerTopologyNodes), // NodeCount + BthHfpSpeakerTopologyNodes, // Nodes + SIZEOF_ARRAY(BthHfpSpeakerTopoMiniportConnections), // ConnectionCount + BthHfpSpeakerTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_BTHHFPSPEAKERTOPTABLE_H_ + + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakerwavtable.h b/audio/sysvad/EndpointsCommon/bthhfpspeakerwavtable.h new file mode 100644 index 00000000..bab3d38a --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakerwavtable.h @@ -0,0 +1,609 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakerwavtable.h + +Abstract: + + Declaration of wave miniport tables for the Bluetooth Hands-Free Profile speaker (external). + +--*/ + +#ifndef _SYSVAD_BTHHFPSPEAKERWAVTABLE_H_ +#define _SYSVAD_BTHHFPSPEAKERWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_BthHfpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + + + +#define BTHHFPSPEAKER_DEVICE_MAX_CHANNELS 1 // Max Channels. + +#define BTHHFPSPEAKER_HOST_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKER_HOST_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKER_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKER_HOST_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKER_HOST_MAX_SAMPLE_RATE 8000 // Max Sample Rate + +#define BTHHFPSPEAKER_OFFLOAD_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKER_OFFLOAD_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKER_OFFLOAD_MAX_SAMPLE_RATE 8000 // Max Sample Rate + +#define BTHHFPSPEAKER_LOOPBACK_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKER_LOOPBACK_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKER_LOOPBACK_MAX_SAMPLE_RATE 8000 // Max Sample Rate + +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE 8000 // Max Sample Rate + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerAudioEngineSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT BthHfpSpeakerHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpSpeakerHostPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpSpeakerHostPinSupportedDeviceFormats)-1].DataFormat + } +}; + +static +MODE_AND_DEFAULT_FORMAT BthHfpSpeakerOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpSpeakerHostPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpSpeakerHostPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES BthHfpSpeakerPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + BthHfpSpeakerHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerHostPinSupportedDeviceFormats), + BthHfpSpeakerHostPinSupportedDeviceModes, + SIZEOF_ARRAY(BthHfpSpeakerHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + BthHfpSpeakerOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerOffloadPinSupportedDeviceFormats), + BthHfpSpeakerOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(BthHfpSpeakerOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + BthHfpSpeakerLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + BthHfpSpeakerAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO BthHfpSpeakerPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKER_HOST_MAX_CHANNELS, + BTHHFPSPEAKER_HOST_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKER_HOST_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKER_HOST_MIN_SAMPLE_RATE, + BTHHFPSPEAKER_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKER_OFFLOAD_MAX_CHANNELS, + BTHHFPSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKER_OFFLOAD_MIN_SAMPLE_RATE, + BTHHFPSPEAKER_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKER_LOOPBACK_MAX_CHANNELS, + BTHHFPSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKER_LOOPBACK_MIN_SAMPLE_RATE, + BTHHFPSPEAKER_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE BthHfpSpeakerPinDataRangePointersStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE BthHfpSpeakerPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE BthHfpSpeakerPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE BthHfpSpeakerPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE BthHfpSpeakerPinDataRangePointersBridge[] = +{ + &BthHfpSpeakerPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesBthHfpSpeakerOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerOffloadPin, PropertiesBthHfpSpeakerOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpSpeakerWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + MAX_INPUT_SYSTEM_STREAMS, + MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerPinDataRangePointersStream), + BthHfpSpeakerPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + MAX_INPUT_OFFLOAD_STREAMS, + MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationBthHfpSpeakerOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerPinDataRangePointersOffloadStream), + BthHfpSpeakerPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + MAX_OUTPUT_LOOPBACK_STREAMS, + MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerPinDataRangePointersLoopbackStream), + BthHfpSpeakerPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerPinDataRangePointersBridge), + BthHfpSpeakerPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR BthHfpSpeakerWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR BthHfpSpeakerWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesBthHfpSpeakerWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerWaveFilter, PropertiesBthHfpSpeakerWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpSpeakerWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpSpeakerWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpSpeakerWaveMiniportPins), // PinCount + BthHfpSpeakerWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpSpeakerWaveMiniportNodes), // NodeCount + BthHfpSpeakerWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(BthHfpSpeakerWaveMiniportConnections),// ConnectionCount + BthHfpSpeakerWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_BTHHFPSPEAKERWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakerwbwavtable.h b/audio/sysvad/EndpointsCommon/bthhfpspeakerwbwavtable.h new file mode 100644 index 00000000..1c491022 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakerwbwavtable.h @@ -0,0 +1,862 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakerwbwavtable.h + +Abstract: + + Declaration of wave miniport tables for the Bluetooth Hands-Free Profile speaker (external), + for Bluetooth connections that support Wideband Speech. + +--*/ + +#ifndef _SYSVAD_BTHHFPSPEAKERWBWAVTABLE_H_ +#define _SYSVAD_BTHHFPSPEAKERWBWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_BthHfpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + + + +#define BTHHFPSPEAKERWB_DEVICE_MAX_CHANNELS 1 // Max Channels. + +#define BTHHFPSPEAKERWB_HOST_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKERWB_HOST_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKERWB_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKERWB_HOST_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKERWB_HOST_MAX_SAMPLE_RATE 16000 // Max Sample Rate + +#define BTHHFPSPEAKERWB_OFFLOAD_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKERWB_OFFLOAD_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKERWB_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKERWB_OFFLOAD_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKERWB_OFFLOAD_MAX_SAMPLE_RATE 16000 // Max Sample Rate + +#define BTHHFPSPEAKERWB_LOOPBACK_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKERWB_LOOPBACK_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKERWB_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKERWB_LOOPBACK_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKERWB_LOOPBACK_MAX_SAMPLE_RATE 16000 // Max Sample Rate + +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MAX_SAMPLE_RATE 16000 // Max Sample Rate + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerWBAudioEngineSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerWBHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerWBOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerWBLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT BthHfpSpeakerWBHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpSpeakerWBHostPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpSpeakerWBHostPinSupportedDeviceFormats)-1].DataFormat + } +}; + +static +MODE_AND_DEFAULT_FORMAT BthHfpSpeakerWBOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpSpeakerWBHostPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpSpeakerWBHostPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES BthHfpSpeakerWBPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + BthHfpSpeakerWBHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerWBHostPinSupportedDeviceFormats), + BthHfpSpeakerWBHostPinSupportedDeviceModes, + SIZEOF_ARRAY(BthHfpSpeakerWBHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + BthHfpSpeakerWBOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerWBOffloadPinSupportedDeviceFormats), + BthHfpSpeakerWBOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(BthHfpSpeakerWBOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + BthHfpSpeakerWBLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerWBLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + BthHfpSpeakerWBAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerWBAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO BthHfpSpeakerWBPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_HOST_MAX_CHANNELS, + BTHHFPSPEAKERWB_HOST_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_HOST_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_HOST_MIN_SAMPLE_RATE, + BTHHFPSPEAKERWB_HOST_MIN_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_HOST_MAX_CHANNELS, + BTHHFPSPEAKERWB_HOST_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_HOST_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_HOST_MAX_SAMPLE_RATE, + BTHHFPSPEAKERWB_HOST_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_OFFLOAD_MAX_CHANNELS, + BTHHFPSPEAKERWB_OFFLOAD_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_OFFLOAD_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_OFFLOAD_MIN_SAMPLE_RATE, + BTHHFPSPEAKERWB_OFFLOAD_MIN_SAMPLE_RATE + }, + { // 3 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_OFFLOAD_MAX_CHANNELS, + BTHHFPSPEAKERWB_OFFLOAD_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_OFFLOAD_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_OFFLOAD_MAX_SAMPLE_RATE, + BTHHFPSPEAKERWB_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 4 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_LOOPBACK_MAX_CHANNELS, + BTHHFPSPEAKERWB_LOOPBACK_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_LOOPBACK_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_LOOPBACK_MIN_SAMPLE_RATE, + BTHHFPSPEAKERWB_LOOPBACK_MIN_SAMPLE_RATE + }, + { // 5 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_LOOPBACK_MAX_CHANNELS, + BTHHFPSPEAKERWB_LOOPBACK_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_LOOPBACK_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_LOOPBACK_MAX_SAMPLE_RATE, + BTHHFPSPEAKERWB_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE BthHfpSpeakerWBPinDataRangePointersStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE BthHfpSpeakerWBPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[2]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[3]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE BthHfpSpeakerWBPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[4]), + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[5]) +}; + +//============================================================================= +static +KSDATARANGE BthHfpSpeakerWBPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE BthHfpSpeakerWBPinDataRangePointersBridge[] = +{ + &BthHfpSpeakerWBPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesBthHfpSpeakerWBOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerWBOffloadPin, PropertiesBthHfpSpeakerWBOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpSpeakerWBWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + MAX_INPUT_SYSTEM_STREAMS, + MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDataRangePointersStream), + BthHfpSpeakerWBPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + MAX_INPUT_OFFLOAD_STREAMS, + MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationBthHfpSpeakerWBOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDataRangePointersOffloadStream), + BthHfpSpeakerWBPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + MAX_OUTPUT_LOOPBACK_STREAMS, + MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDataRangePointersLoopbackStream), + BthHfpSpeakerWBPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDataRangePointersBridge), + BthHfpSpeakerWBPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; +//============================================================================= +static +PCNODE_DESCRIPTOR BthHfpSpeakerWBWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR BthHfpSpeakerWBWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesBthHfpSpeakerWBWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerWBWaveFilter, PropertiesBthHfpSpeakerWBWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpSpeakerWBWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpSpeakerWBWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpSpeakerWBWaveMiniportPins), // PinCount + BthHfpSpeakerWBWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpSpeakerWBWaveMiniportNodes), // NodeCount + BthHfpSpeakerWBWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(BthHfpSpeakerWBWaveMiniportConnections),// ConnectionCount + BthHfpSpeakerWBWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_BTHHFPSPEAKERWBWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfptopo.cpp b/audio/sysvad/EndpointsCommon/bthhfptopo.cpp new file mode 100644 index 00000000..6847dc05 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfptopo.cpp @@ -0,0 +1,623 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfptopo.cpp + +Abstract: + + Implementation of topology miniport for the Bluetooth Hands-Free Profile (external). + +--*/ +#ifdef SYSVAD_BTH_BYPASS + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "bthhfptopo.h" + +typedef struct _BTHHFP_VOLUME_VALUES_BLOCK +{ + KSPROPERTY_DESCRIPTION Description; + KSPROPERTY_MEMBERSHEADER SteppingHeader; + KSPROPERTY_STEPPING_LONG Stepping[1]; + KSPROPERTY_MEMBERSHEADER DefaultHeader; + ULONG Default[1]; + +} BTHHFP_VOLUME_VALUES_BLOCK, *PBTHHFP_VOLUME_VALUES_BLOCK; + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpVolumeLevel_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles basic support for Bluetooth HFP ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[PropertyHandler_BthHfpVolumeLevel_BasicSupport]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ASSERT(PropertyRequest); + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + ULONG volumeSettingsSize = 0; + PKSPROPERTY_VALUES volumeSettings; + PKSPROPERTY_DESCRIPTION propDesc; + PBTHHFPDEVICECOMMON bthHfpDevice; + ULONG cbFullProperty = sizeof(BTHHFP_VOLUME_VALUES_BLOCK); + + propDesc = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + // + // Convert the KSPROPERTY_VALUES into a BTHHFP_VOLUME_VALUES_BLOCK. + // + volumeSettings = bthHfpDevice->GetVolumeSettings(&volumeSettingsSize); + ASSERT(volumeSettings != NULL); + ASSERT(volumeSettingsSize != 0); + + if (volumeSettings->MembersListCount != 2 || + volumeSettings->MembersList[0].MembersHeader.MembersFlags != KSPROPERTY_MEMBER_STEPPEDRANGES || + volumeSettings->MembersList[1].MembersHeader.MembersFlags != KSPROPERTY_MEMBER_VALUES) + { + ntStatus = STATUS_NOT_SUPPORTED; + DPF(D_ERROR, ("PropertyHandler_BthHfpVolumeLevel_BasicSupport: invalid KSPROPERTY_VALUES settings")); + goto Done; + } + + // + // Init description prop header. + // + propDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + propDesc->DescriptionSize = cbFullProperty; + propDesc->PropTypeSet = volumeSettings->PropTypeSet; + propDesc->MembersListCount = 2; + propDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + PBTHHFP_VOLUME_VALUES_BLOCK settings = (PBTHHFP_VOLUME_VALUES_BLOCK)propDesc; + + // + // First entry is the range. + // + settings->SteppingHeader.MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + settings->SteppingHeader.MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + settings->SteppingHeader.MembersCount = 1; + settings->SteppingHeader.Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range. + PKSPROPERTY_STEPPING_LONG ranges = (PKSPROPERTY_STEPPING_LONG)volumeSettings->MembersList[0].Members; + settings->Stepping[0] = *ranges; + //settings->Stepping[0].SteppingDelta = 2 * 0x10000; // 2 dB + //settings->Stepping[0].Bounds.SignedMinimum = -15 * 0x10000; // -15 dB + + // + // Second entry is the default value. + // + settings->DefaultHeader.MembersFlags = KSPROPERTY_MEMBER_VALUES; + settings->DefaultHeader.MembersSize = sizeof(LONG); + settings->DefaultHeader.MembersCount = 1; + settings->DefaultHeader.Flags = KSPROPERTY_MEMBER_FLAG_DEFAULT; + + // fill in the default value. + PLONG defValue = (PLONG) volumeSettings->MembersList[1].Members; + settings->Default[0] = *defValue; + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + + pDesc->IsConnected = bthHfpDevice->GetConnectionStatus() ? TRUE : FALSE; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpJackContainerId +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_CONTAINERID ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpJackContainerId]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + // This property is only valid on bridge pins. + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(GUID); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + GUID* guid = (GUID *)PropertyRequest->Value; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + *guid = bthHfpDevice->GetContainerId(); + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpOneShotReconnect +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_BtAudio, KSPROPERTY_ONESHOT_RECONNECT ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpOneShotReconnect]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + ntStatus = bthHfpDevice->Connect(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpOneDisconnect +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_BtAudio, KSPROPERTY_ONESHOT_DISCONNECT ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpOneDisconnect]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + ntStatus = bthHfpDevice->Disconnect(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpTopoNodeEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpTopoNodeEvent]")); + + // The major target is the object pointer to the topology miniport. + PCMiniportTopology pMiniport = (PCMiniportTopology)EventRequest->MajorTarget; + ASSERT (pMiniport); + + switch (EventRequest->Verb) + { + // Do we support event handling?!? + case PCEVENT_VERB_SUPPORT: + DPF(D_VERBOSE, ("BasicSupport Query for event.")); + break; + + // We should add the event now! + case PCEVENT_VERB_ADD: + DPF(D_VERBOSE, ("Adding event.")); + + // If we have the interface and EventEntry is defined ... + if (EventRequest->EventEntry) + { + pMiniport->AddEventToEventList(EventRequest->EventEntry); + } + else + { + return STATUS_UNSUCCESSFUL; + } + break; + + case PCEVENT_VERB_REMOVE: + // We cannot remove the event but we can stop generating the + // events. However, it also doesn't hurt to always generate them ... + DPF(D_VERBOSE, ("Removing event.")); + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +#pragma code_seg() +#endif // SYSVAD_BTH_BYPASS + + diff --git a/audio/sysvad/EndpointsCommon/bthhfptopo.h b/audio/sysvad/EndpointsCommon/bthhfptopo.h new file mode 100644 index 00000000..24c0e1c4 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfptopo.h @@ -0,0 +1,61 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfptopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: headphone). + +--*/ + +#ifndef _SYSVAD_BTHHFPTOPO_H_ +#define _SYSVAD_BTHHFPTOPO_H_ + +NTSTATUS +PropertyHandler_BthHfpVolumeLevel_BasicSupport( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpJackDescription( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_BthHfpJackDescription2( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_BthHfpJackContainerId( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_BthHfpOneShotReconnect( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpOneDisconnect( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpTopoNodeEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +#endif // _SYSVAD_BTHHFPTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/micarray1toptable.h b/audio/sysvad/EndpointsCommon/micarray1toptable.h new file mode 100644 index 00000000..26148eff --- /dev/null +++ b/audio/sysvad/EndpointsCommon/micarray1toptable.h @@ -0,0 +1,228 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray1toptable.h + +Abstract: + + Declaration of topology tables for the mic array (front). + +--*/ + +#ifndef _SYSVAD_MICARRAY1TOPTABLE_H_ +#define _SYSVAD_MICARRAY1TOPTABLE_H_ + +// +// {6ae81ff4-203e-4fe1-88aa-f2d57775cd4a} +DEFINE_GUID(MICARRAY1_CUSTOM_NAME, +0x6ae81ff4, 0x203e, 0x4fe1, 0x88, 0xaa, 0xf2, 0xd5, 0x77, 0x75, 0xcd, 0x4a); + +//============================================================================= +static +KSDATARANGE MicArray1TopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicArray1TopoPinDataRangePointersBridge[] = +{ + &MicArray1TopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray1TopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray1TopoPinDataRangePointersBridge), // DataRangesCount + MicArray1TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE_ARRAY, // Category + &MICARRAY1_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray1TopoPinDataRangePointersBridge), // DataRangesCount + MicArray1TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1Volume, MicArray1PropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1Mute, MicArray1PropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1PeakMeter, MicArray1PropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray1TopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicArray1Volume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicArray1Mute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicArray1PeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArray1TopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1TopoFilter, MicArray1PropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray1TopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray1TopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray1TopoMiniportPins), // PinCount + MicArray1TopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray1TopologyNodes), // NodeCount + MicArray1TopologyNodes, // Nodes + SIZEOF_ARRAY(MicArray1TopoMiniportConnections),// ConnectionCount + MicArray1TopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICARRAY1TOPTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/micarraytopo.cpp b/audio/sysvad/EndpointsCommon/micarraytopo.cpp new file mode 100644 index 00000000..236d03ea --- /dev/null +++ b/audio/sysvad/EndpointsCommon/micarraytopo.cpp @@ -0,0 +1,787 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraytopo.cpp + +Abstract: + + Implementation of topology miniport for the mic array. + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "micarraytopo.h" +#include "micarray1toptable.h" + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CreateMicArrayMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CMicArrayMiniportTopology *obj = + new (PoolType, MINTOPORT_POOLTAG) + CMicArrayMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels, + MiniportPair->DeviceType ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} // CreateMicArrayMiniportTopology + +//============================================================================= +CMicArrayMiniportTopology::~CMicArrayMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMicArrayMiniportTopology::~CMicArrayMiniportTopology]")); +} // ~CMicArrayMiniportTopology + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the IUnknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CMicArrayMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerMicArrayGeometry +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerMicArrayGeometry]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cElements = IsCombined()? 4 : 2; + ULONG cbNeeded = FIELD_OFFSET(KSAUDIO_MIC_ARRAY_GEOMETRY, KsMicCoord) + + cElements * sizeof(KSAUDIO_MICROPHONE_COORDINATES); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSAUDIO_MIC_ARRAY_GEOMETRY pMAG = (PKSAUDIO_MIC_ARRAY_GEOMETRY)PropertyRequest->Value; + const SHORT MicArray_180_Degrees = 31416; // 10000 * pi + const SHORT MicArray_45_Degrees = 7854; // 10000 * pi / 4 + + if (IsFront()) + { + // fill in mic array geometry fields + pMAG->usVersion = 0x0100; // Version of Mic array specification (0x0100) + pMAG->usMicArrayType = (USHORT)KSMICARRAY_MICARRAYTYPE_LINEAR; // Type of Mic Array + pMAG->wVerticalAngleBegin = -MicArray_45_Degrees; // Work Volume Vertical Angle Begin + pMAG->wVerticalAngleEnd = MicArray_45_Degrees; // Work Volume Vertical Angle End + pMAG->wHorizontalAngleBegin = 0; // Work Volume HorizontalAngle Begin + pMAG->wHorizontalAngleEnd = 0; // Work Volume HorizontalAngle End + pMAG->usFrequencyBandLo = 100; // Low end of Freq Range + pMAG->usFrequencyBandHi = 8000; // High end of Freq Range + + pMAG->usNumberOfMicrophones = 2; // Count of microphone coordinate structures to follow. + + pMAG->KsMicCoord[0].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[0].wXCoord = 0; + pMAG->KsMicCoord[0].wYCoord = -100; // mic elements are 200 mm apart + pMAG->KsMicCoord[0].wZCoord = 0; + pMAG->KsMicCoord[0].wVerticalAngle = 0; + pMAG->KsMicCoord[0].wHorizontalAngle = 0; + + pMAG->KsMicCoord[1].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[1].wXCoord = 0; + pMAG->KsMicCoord[1].wYCoord = 100; // mic elements are 200 mm apart + pMAG->KsMicCoord[1].wZCoord = 0; + pMAG->KsMicCoord[1].wVerticalAngle = 0; + pMAG->KsMicCoord[1].wHorizontalAngle = 0; + } + else if (IsBack()) // in this sample the geometries for front and back are the same. + { + // fill in mic array geometry fields + pMAG->usVersion = 0x0100; // Version of Mic array specification (0x0100) + pMAG->usMicArrayType = (USHORT)KSMICARRAY_MICARRAYTYPE_LINEAR; // Type of Mic Array + pMAG->wVerticalAngleBegin = -MicArray_45_Degrees; // Work Volume Vertical Angle Begin + pMAG->wVerticalAngleEnd = MicArray_45_Degrees; // Work Volume Vertical Angle End + pMAG->wHorizontalAngleBegin = 0; // Work Volume HorizontalAngle Begin + pMAG->wHorizontalAngleEnd = 0; // Work Volume HorizontalAngle End + pMAG->usFrequencyBandLo = 100; // Low end of Freq Range + pMAG->usFrequencyBandHi = 8000; // High end of Freq Range + + pMAG->usNumberOfMicrophones = 2; // Count of microphone coordinate structures to follow. + + pMAG->KsMicCoord[0].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[0].wXCoord = 0; + pMAG->KsMicCoord[0].wYCoord = -100; // mic elements are 200 mm apart + pMAG->KsMicCoord[0].wZCoord = 0; + pMAG->KsMicCoord[0].wVerticalAngle = 0; + pMAG->KsMicCoord[0].wHorizontalAngle = 0; + + pMAG->KsMicCoord[1].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[1].wXCoord = 0; + pMAG->KsMicCoord[1].wYCoord = 100; // mic elements are 200 mm apart + pMAG->KsMicCoord[1].wZCoord = 0; + pMAG->KsMicCoord[1].wVerticalAngle = 0; + pMAG->KsMicCoord[1].wHorizontalAngle = 0; + } + else + { + // fill in mic array geometry fields + pMAG->usVersion = 0x0100; // Version of Mic array specification (0x0100) + pMAG->usMicArrayType = (USHORT)KSMICARRAY_MICARRAYTYPE_LINEAR; // Type of Mic Array + pMAG->wVerticalAngleBegin = -MicArray_45_Degrees; // Work Volume Vertical Angle Begin + pMAG->wVerticalAngleEnd = MicArray_45_Degrees; // Work Volume Vertical Angle End + pMAG->wHorizontalAngleBegin = -MicArray_180_Degrees; // Work Volume HorizontalAngle Begin + pMAG->wHorizontalAngleEnd = MicArray_180_Degrees; // Work Volume HorizontalAngle End + pMAG->usFrequencyBandLo = 100; // Low end of Freq Range + pMAG->usFrequencyBandHi = 8000; // High end of Freq Range + + pMAG->usNumberOfMicrophones = 4; // Count of microphone coordinate structures to follow. + + // front elements + pMAG->KsMicCoord[0].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[0].wXCoord = 0; + pMAG->KsMicCoord[0].wYCoord = -100; // mic elements are 200 mm apart + pMAG->KsMicCoord[0].wZCoord = 0; + pMAG->KsMicCoord[0].wVerticalAngle = 0; + pMAG->KsMicCoord[0].wHorizontalAngle = 0; + + pMAG->KsMicCoord[1].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[1].wXCoord = 0; + pMAG->KsMicCoord[1].wYCoord = 100; // mic elements are 200 mm apart + pMAG->KsMicCoord[1].wZCoord = 0; + pMAG->KsMicCoord[1].wVerticalAngle = 0; + pMAG->KsMicCoord[1].wHorizontalAngle = 0; + + // back elements + pMAG->KsMicCoord[2].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[2].wXCoord = 0; + pMAG->KsMicCoord[2].wYCoord = -100; // mic elements are 200 mm apart + pMAG->KsMicCoord[2].wZCoord = 0; + pMAG->KsMicCoord[2].wVerticalAngle = 0; + pMAG->KsMicCoord[2].wHorizontalAngle = -MicArray_180_Degrees; + + pMAG->KsMicCoord[3].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[3].wXCoord = 0; + pMAG->KsMicCoord[3].wYCoord = 100; // mic elements are 200 mm apart + pMAG->KsMicCoord[3].wZCoord = 0; + pMAG->KsMicCoord[3].wVerticalAngle = 0; + pMAG->KsMicCoord[3].wHorizontalAngle = -MicArray_180_Degrees; + + } + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + pDesc->ChannelMapping = 0; // Don't specify channel mask for array mic + pDesc->Color = 0x00000000; // Black. This is an integrated device + pDesc->ConnectionType = eConnTypeUnknown; // Integrated. + pDesc->GenLocation = eGenLocPrimaryBox; + pDesc->GeoLocation = IsFront() ? eGeoLocFront : + (IsBack() ? eGeoLocRear : eGeoLocNotApplicable); + pDesc->PortConnection = ePortConnIntegratedDevice; + pDesc->IsConnected = TRUE; // This is an integrated device, so it's always "connected" + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = 0; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicArrayTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicArrayTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMicArrayMiniportTopology pMiniport = (PCMicArrayMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY) + { + ntStatus = pMiniport->PropertyHandlerMicArrayGeometry(PropertyRequest); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_TopoFilter + +//============================================================================= +NTSTATUS +PropertyHandler_MicArrayTopology +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicArrayTopology]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + PCMicArrayMiniportTopology pMiniport = (PCMicArrayMiniportTopology)PropertyRequest->MajorTarget; + + return pMiniport->PropertyHandlerGeneric(PropertyRequest); +} // PropertyHandler_MicArrayTopology + +#pragma code_seg() + diff --git a/audio/sysvad/EndpointsCommon/micarraytopo.h b/audio/sysvad/EndpointsCommon/micarraytopo.h new file mode 100644 index 00000000..8076a359 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/micarraytopo.h @@ -0,0 +1,112 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraytopo.h + +Abstract: + + Declaration of mic array topology miniport. + +--*/ + +#ifndef _SYSVAD_MICARRAYTOPO_H_ +#define _SYSVAD_MICARRAYTOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMicArrayMiniportTopology +// + +class CMicArrayMiniportTopology : + public CMiniportTopologySYSVAD, + public IMiniportTopology, + public CUnknown +{ + public: + DECLARE_STD_UNKNOWN(); + CMicArrayMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels, + _In_ eDeviceType DeviceType + ) + : CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels), + m_DeviceType(DeviceType) + { + ASSERT(m_DeviceType == eMicArrayDevice1 || + m_DeviceType == eMicArrayDevice2 || + m_DeviceType == eMicArrayDevice3); + } + + ~CMicArrayMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerMicArrayGeometry + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + protected: + eDeviceType m_DeviceType; + + protected: + bool IsFront() + { + return m_DeviceType == eMicArrayDevice1; + } + + bool IsBack() + { + return m_DeviceType == eMicArrayDevice2; + } + + bool IsCombined() + { + return m_DeviceType == eMicArrayDevice3; + } +}; + +typedef CMicArrayMiniportTopology *PCMicArrayMiniportTopology; + + +NTSTATUS +CreateMicArrayMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_MicArrayTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); +NTSTATUS PropertyHandler_MicArrayTopology(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_MICARRAYTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/micarraywavtable.h b/audio/sysvad/EndpointsCommon/micarraywavtable.h new file mode 100644 index 00000000..fb553d67 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/micarraywavtable.h @@ -0,0 +1,826 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraywavtable.h + +Abstract: + + Declaration of wave miniport tables for the mic array. + +--*/ + +#ifndef _SYSVAD_MICARRAYWAVTABLE_H_ +#define _SYSVAD_MICARRAYWAVTABLE_H_ + +// +// Mic array range. +// +#define MICARRAY_RAW_CHANNELS 2 // Channels for raw mode +#define MICARRAY_PROCESSED_CHANNELS 1 // Channels for default mode +#define MICARRAY_DEVICE_MAX_CHANNELS 2 // Max channels overall +#define MICARRAY_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define MICARRAY_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define MICARRAY_RAW_SAMPLE_RATE 48000 // Raw sample rate +#define MICARRAY_PROCESSED_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define MICARRAY_PROCESSED_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define MICARRAY_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicArrayPinSupportedDeviceFormats[] = +{ + // 0 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this first element as the proposed DEFAULT format + // 48 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 1 + // 8 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 2 + // 11.025 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 3 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this element as the proposed SPEECH format + // 16 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, // One channel + 16000, // 16KHz + 32000, // average bytes per second + 2, // 2 bytes per frame + 16, // 16 bits per sample container + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, // valid bits per sample + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 4 + // 22.05 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 22050, + 44100, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 5 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this element as the proposed COMMUNICATIONS format + // 24 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 24000, + 48000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 6 + // 32 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 32000, + 64000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 7 + // 44.1 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 8 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this last element as the proposed RAW format + // 48 KHz 16-bit 2 channels + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + 0, // No channel configuration for unprocessed mic array + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicArrayPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicArrayPinSupportedDeviceFormats[SIZEOF_ARRAY(MicArrayPinSupportedDeviceFormats)-1].DataFormat + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &MicArrayPinSupportedDeviceFormats[0].DataFormat + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH, + &MicArrayPinSupportedDeviceFormats[3].DataFormat + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &MicArrayPinSupportedDeviceFormats[5].DataFormat + } +}; + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE KeywordPinSupportedDeviceFormats[] = +{ + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, // One channel + 16000, // 16KHz + 32000, // average bytes per second + 2, // 2 bytes per frame + 16, // 16 bits per sample container + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, // valid bits per sample + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, +}; + +static +MODE_AND_DEFAULT_FORMAT KeywordPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &KeywordPinSupportedDeviceFormats[SIZEOF_ARRAY(KeywordPinSupportedDeviceFormats) - 1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES MicArrayPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicArrayPinSupportedDeviceFormats, + SIZEOF_ARRAY(MicArrayPinSupportedDeviceFormats), + MicArrayPinSupportedDeviceModes, + SIZEOF_ARRAY(MicArrayPinSupportedDeviceModes) + }, + { + KeywordCapturePin, + KeywordPinSupportedDeviceFormats, + SIZEOF_ARRAY(KeywordPinSupportedDeviceFormats), + KeywordPinSupportedDeviceModes, + SIZEOF_ARRAY(KeywordPinSupportedDeviceModes) + } +}; + +//============================================================================= +// Data ranges +// +// See CMiniportWaveRT::DataRangeIntersection. +// +// Both mono and two-channel formats are supported for the mic arrays. The +// design of this sample driver's data range intersection handler requires a +// separate data for each supported channel count. +// +static +KSDATARANGE_AUDIO MicArrayPinDataRangesRawStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_RAW_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + MICARRAY_RAW_SAMPLE_RATE, + MICARRAY_RAW_SAMPLE_RATE + }, +}; + +static +KSDATARANGE_AUDIO MicArrayPinDataRangesProcessedStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + MICARRAY_PROCESSED_MIN_SAMPLE_RATE, + MICARRAY_PROCESSED_MIN_SAMPLE_RATE + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 11025, + 11025 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 16000, + 16000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 22050, + 22050 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 24000, + 24000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 32000, + 32000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 44100, + 44100 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + MICARRAY_PROCESSED_MAX_SAMPLE_RATE, + MICARRAY_PROCESSED_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE MicArrayPinDataRangePointersStream[] = +{ + PKSDATARANGE(&MicArrayPinDataRangesProcessedStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&MicArrayPinDataRangesRawStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE MicArrayPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicArrayPinDataRangePointersBridge[] = +{ + &MicArrayPinDataRangesBridge[0] +}; + +static +KSDATARANGE_AUDIO KeywordPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + 1, // max channels + 16, // min bits per sample + 16, // max bits per sample + 16000, // min sample rate + 16000 // max sample rate + } +}; + +static +PKSDATARANGE KeywordPinDataRangePointersStream[] = +{ + PKSDATARANGE(&KeywordPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArrayWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArrayPinDataRangePointersBridge), + MicArrayPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave In Streaming Pin (Capture) KSPIN_WAVE_HOST + { + MICARRAY_MAX_INPUT_STREAMS, + MICARRAY_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArrayPinDataRangePointersStream), + MicArrayPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + }, + // Keyword Detector Streaming Pin (Capture) KSPIN_WAVEIN_KEYWORD + { + 1, + 1, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(KeywordPinDataRangePointersStream), + KeywordPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_KEYWORDDETECTOR, + NULL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArrayWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArrayWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_KEYWORD }, +}; + +//============================================================================= +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorSupportedPatterns); +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorPatterns); +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorArmed); +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorArmed); +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorMatchResult); + +static +SYSVADPROPERTY_ITEM PropertiesMicArrayWaveFilter[] = +{ + { + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL + }, + { + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL + }, + { + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL + }, + { + { + &KSPROPSETID_SoundDetector, + KSPROPERTY_SOUNDDETECTOR_SUPPORTEDPATTERNS, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + SysvadPropertyDispatch, + }, + 0, + sizeof(CONTOSO_SUPPORTEDPATTERNSVALUE), + CMiniportWaveRT_Get_SoundDetectorSupportedPatterns, + NULL, + NULL + }, + { + { + &KSPROPSETID_SoundDetector, + KSPROPERTY_SOUNDDETECTOR_PATTERNS, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + SysvadPropertyDispatch, + }, + 0, + (sizeof(KSMULTIPLE_ITEM) + sizeof(CONTOSO_KEYWORDCONFIGURATION)), + NULL, + CMiniportWaveRT_Set_SoundDetectorPatterns, + NULL + }, + { + { + &KSPROPSETID_SoundDetector, + KSPROPERTY_SOUNDDETECTOR_ARMED, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + SysvadPropertyDispatch, + }, + 0, + sizeof(BOOL), + CMiniportWaveRT_Get_SoundDetectorArmed, + CMiniportWaveRT_Set_SoundDetectorArmed, + NULL + }, + { + { + &KSPROPSETID_SoundDetector, + KSPROPERTY_SOUNDDETECTOR_MATCHRESULT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + SysvadPropertyDispatch, + }, + 0, + sizeof(CONTOSO_KEYWORDDETECTIONRESULT), + CMiniportWaveRT_Get_SoundDetectorMatchResult, + NULL, + NULL + }, +}; + +NTSTATUS CMiniportWaveRT_EventHandler_SoundDetectorMatchDetected( + _In_ PPCEVENT_REQUEST EventRequest + ); + +static +PCEVENT_ITEM EventsMicArrayWaveFilter[] = +{ + { + &KSEVENTSETID_SoundDetector, + KSEVENT_SOUNDDETECTOR_MATCHDETECTED, + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + CMiniportWaveRT_EventHandler_SoundDetectorMatchDetected, + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationMicArrayWaveFilter, PropertiesMicArrayWaveFilter, EventsMicArrayWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArrayWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArrayWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArrayWaveMiniportPins), // PinCount + MicArrayWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArrayWaveMiniportNodes), // NodeCount + MicArrayWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicArrayWaveMiniportConnections), // ConnectionCount + MicArrayWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_MICARRAYWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/mintopo.cpp b/audio/sysvad/EndpointsCommon/mintopo.cpp new file mode 100644 index 00000000..47a89cac --- /dev/null +++ b/audio/sysvad/EndpointsCommon/mintopo.cpp @@ -0,0 +1,731 @@ +/*++ + +Copyright (c) 1997-2011 Microsoft Corporation All Rights Reserved + +Module Name: + + mintopo.cpp + +Abstract: + + Implementation of topology miniport. + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "minwavert.h" +#include "mintopo.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + PoolType - + + UnknownOuter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER(UnknownAdapter); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + CMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels, + MiniportPair->DeviceType, + DeviceContext ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} // CreateMiniportTopologySYSVAD + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportTopology::~CMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportTopology::~CMiniportTopology]")); + +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + ASSERT(m_BthHfpDevice != NULL); + + // + // Register with BthHfpDevice to get notification events. + // + if (m_DeviceType == eBthHfpMicDevice) + { + m_BthHfpDevice->SetMicVolumeHandler(NULL, NULL); + m_BthHfpDevice->SetMicConnectionStatusHandler(NULL, NULL); + } + else + { + ASSERT(m_DeviceType == eBthHfpSpeakerDevice); + + m_BthHfpDevice->SetSpeakerVolumeHandler(NULL, NULL); + m_BthHfpDevice->SetSpeakerConnectionStatusHandler(NULL, NULL); + } + + SAFE_RELEASE(m_BthHfpDevice); // IBthHfpDeviceCommon + } +#endif // SYSVAD_BTH_BYPASS +} // ~CMiniportTopology + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: CMiniportTopologySYSVAD::Init failed, 0x%x", ntStatus)), + Done); + +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + bthHfpDevice = GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + // + // Register with BthHfpDevice to get notification events. + // + if (m_DeviceType == eBthHfpMicDevice) + { + bthHfpDevice->SetMicVolumeHandler( + EvtMicVolumeHandler, // handler + PCMiniportTopology(this)); // context. + + bthHfpDevice->SetMicConnectionStatusHandler( + EvtMicConnectionStatusHandler, // handler + PCMiniportTopology(this)); // context. + } + else + { + ASSERT(m_DeviceType == eBthHfpSpeakerDevice); + + bthHfpDevice->SetSpeakerVolumeHandler( + EvtSpeakerVolumeHandler, // handler + PCMiniportTopology(this)); // context. + + bthHfpDevice->SetSpeakerConnectionStatusHandler( + EvtSpeakerConnectionStatusHandler, // handler + PCMiniportTopology(this)); // context. + } + } +#endif // SYSVAD_BTH_BYPASS + +Done: + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + cJackDescriptions - # of elements in the jack descriptions array. + JackDescriptions - Array of jack descriptions pointers. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions, + _In_ DWORD JackCapabilities +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + cJackDescriptions - # of elements in the jack descriptions array. + JackDescriptions - Array of jack descriptions pointers. + JackCapabilities - Jack capabilities flags. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JackCapabilities; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +#ifdef SYSVAD_BTH_BYPASS +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopology::EvtSpeakerVolumeHandler +( + _In_opt_ PVOID Context +) +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::EvtSpeakerVolumeHandler]")); + + PCMiniportTopology This = PCMiniportTopology(Context); + if (This == NULL) + { + DPF(D_ERROR, ("EvtSpeakerVolumeHandler: context is null")); + return; + } + + This->GenerateEventList( + (GUID*)&KSEVENTSETID_AudioControlChange, // event set. NULL is a wild card for all events. + KSEVENT_CONTROL_CHANGE, // event ID. + FALSE, // do not use pid ID. + ULONG(-1), // pin ID, not used. + TRUE, // use node ID + KSNODE_TOPO_VOLUME); // node ID. +} + +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopology::EvtSpeakerConnectionStatusHandler +( + _In_opt_ PVOID Context +) +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::EvtSpeakerConnectionStatusHandler]")); + + PCMiniportTopology This = PCMiniportTopology(Context); + if (This == NULL) + { + DPF(D_ERROR, ("EvtSpeakerConnectionStatusHandler: context is null")); + return; + } + + This->GenerateEventList( + (GUID*)&KSEVENTSETID_PinCapsChange, // event set. NULL is a wild card for all events. + KSEVENT_PINCAPS_JACKINFOCHANGE, // event ID. + TRUE, // use pid ID. + KSPIN_TOPO_LINEOUT_DEST, // pin ID. + FALSE, // do not use node ID. + ULONG(-1)); // node ID, not used. +} + +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopology::EvtMicVolumeHandler +( + _In_opt_ PVOID Context +) +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::EvtMicVolumeHandler]")); + + PCMiniportTopology This = PCMiniportTopology(Context); + if (This == NULL) + { + DPF(D_ERROR, ("EvtMicVolumeHandler: context is null")); + return; + } + + This->GenerateEventList( + (GUID*)&KSEVENTSETID_AudioControlChange, // event set. NULL is a wild card for all events. + KSEVENT_CONTROL_CHANGE, // event ID. + FALSE, // do not use pid ID. + ULONG(-1), // pin ID, not used. + TRUE, // use node ID. + KSNODE_TOPO_VOLUME); // node ID. +} + +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopology::EvtMicConnectionStatusHandler +( + _In_opt_ PVOID Context +) +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::EvtMicConnectionStatusHandler]")); + + PCMiniportTopology This = PCMiniportTopology(Context); + if (This == NULL) + { + DPF(D_ERROR, ("EvtMicConnectionStatusHandler: context is null")); + return; + } + + This->GenerateEventList( + (GUID*)&KSEVENTSETID_PinCapsChange, // event set. NULL is a wild card for all events. + KSEVENT_PINCAPS_JACKINFOCHANGE, // event ID. + TRUE, // use pid ID. + KSPIN_TOPO_MIC_ELEMENTS, // pin ID. + FALSE, // do not use node ID. + ULONG(-1)); // node ID, not used. +} +#endif // SYSVAD_BTH_BYPASS + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_Topology +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_Topology]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + return + ((PCMiniportTopology) + (PropertyRequest->MajorTarget))->PropertyHandlerGeneric + ( + PropertyRequest + ); +} // PropertyHandler_Topology + +#pragma code_seg() + diff --git a/audio/sysvad/EndpointsCommon/mintopo.h b/audio/sysvad/EndpointsCommon/mintopo.h new file mode 100644 index 00000000..e232e1d7 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/mintopo.h @@ -0,0 +1,145 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minitopo.h + +Abstract: + + Declaration of topology miniport. + +--*/ + +#ifndef _SYSVAD_MINTOPO_H_ +#define _SYSVAD_MINTOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMiniportTopology +// + +class CMiniportTopology : + public CMiniportTopologySYSVAD, + public IMiniportTopology, + public CUnknown +{ + private: + eDeviceType m_DeviceType; + union { + PVOID m_DeviceContext; +#ifdef SYSVAD_BTH_BYPASS + PBTHHFPDEVICECOMMON m_BthHfpDevice; +#endif // SYSVAD_BTH_BYPASS + }; + +public: + DECLARE_STD_UNKNOWN(); + CMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels, + _In_ eDeviceType DeviceType, + _In_opt_ PVOID DeviceContext + ) + : CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels), + m_DeviceType(DeviceType), + m_DeviceContext(DeviceContext) + { +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + if (m_BthHfpDevice != NULL) + { + // This ref is released on dtor. + m_BthHfpDevice->AddRef(); // strong ref. + } + } +#endif // SYSVAD_BTH_BYPASS + } + + ~CMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions, + _In_ DWORD JackCapabilities + ); + +#ifdef SYSVAD_BTH_BYPASS + BOOL IsBthHfpDevice() + { + return (m_DeviceType == eBthHfpMicDevice || + m_DeviceType == eBthHfpSpeakerDevice) ? TRUE : FALSE; + } + + // Returns a weak ref to the Bluetooth HFP device. + PBTHHFPDEVICECOMMON GetBthHfpDevice() + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + if (IsBthHfpDevice()) + { + if (m_BthHfpDevice != NULL) + { + bthHfpDevice = m_BthHfpDevice; + } + } + + return bthHfpDevice; + } + + static + VOID + EvtSpeakerVolumeHandler + ( + _In_opt_ PVOID Context + ); + + static + VOID + EvtSpeakerConnectionStatusHandler + ( + _In_opt_ PVOID Context + ); + + static + VOID + EvtMicVolumeHandler + ( + _In_opt_ PVOID Context + ); + + static + VOID + EvtMicConnectionStatusHandler + ( + _In_opt_ PVOID Context + ); +#endif // SYSVAD_BTH_BYPASS +}; + +typedef CMiniportTopology *PCMiniportTopology; + +#endif // _SYSVAD_MINTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/minwavert.cpp b/audio/sysvad/EndpointsCommon/minwavert.cpp new file mode 100644 index 00000000..843706b1 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/minwavert.cpp @@ -0,0 +1,2287 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minwavert.cpp + +Abstract: + + Implementation of wavert miniport. + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include <limits.h> +#include "ContosoKeywordDetector.h" +#include "SysVadShared.h" +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "IHVPrivatePropertySet.h" + +//============================================================================= +// CMiniportWaveRT +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Create the wavert miniport. + +Arguments: + + Unknown - + + RefClsId - + + UnknownOuter - + + PoolType - + + UnkownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(UnknownOuter); + + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + CMiniportWaveRT *obj = new (PoolType, MINWAVERT_POOLTAG) CMiniportWaveRT + ( + UnknownAdapter, + MiniportPair, + DeviceContext + ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportWaveRT::~CMiniportWaveRT +( + void +) +/*++ + +Routine Description: + + Destructor for wavert miniport + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::~CMiniportWaveRT]")); + + if (m_pDeviceFormat) + { + ExFreePoolWithTag( m_pDeviceFormat, MINWAVERT_POOLTAG ); + m_pDeviceFormat = NULL; + } + + if (m_pMixFormat) + { + ExFreePoolWithTag( m_pMixFormat, MINWAVERT_POOLTAG ); + m_pMixFormat = NULL; + } + + if (m_pbMuted) + { + ExFreePoolWithTag( m_pbMuted, MINWAVERT_POOLTAG ); + m_pbMuted = NULL; + } + + if (m_plVolumeLevel) + { + ExFreePoolWithTag( m_plVolumeLevel, MINWAVERT_POOLTAG ); + m_plVolumeLevel = NULL; + } + + if (m_plPeakMeter) + { + ExFreePoolWithTag( m_plPeakMeter, MINWAVERT_POOLTAG ); + m_plPeakMeter = NULL; + } + + if (m_pDrmPort) + { + m_pDrmPort->Release(); + m_pDrmPort = NULL; + } + + if (m_pPortEvents) + { + m_pPortEvents->Release(); + m_pPortEvents = NULL; + } + + if (m_SystemStreams) + { + ExFreePoolWithTag( m_SystemStreams, MINWAVERT_POOLTAG ); + m_SystemStreams = NULL; + } + + if (m_OffloadStreams) + { + ExFreePoolWithTag( m_OffloadStreams, MINWAVERT_POOLTAG ); + m_OffloadStreams = NULL; + } + + if (m_LoopbackStreams) + { + ExFreePoolWithTag( m_LoopbackStreams, MINWAVERT_POOLTAG ); + m_LoopbackStreams = NULL; + } + +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + SAFE_RELEASE(m_BthHfpDevice); + } +#endif // SYSVAD_BTH_BYPASS +} // ~CMiniportWaveRT + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + + This sample just validates the # of channels and lets the class handler + do the rest. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data + range submitted by client in the data range intersection + property request. + + MyDataRange - Pin's data range to be compared with client's data + range. In this case we actually ignore our own data + range, because we know that we only support one range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format placed in + ResultantFormat. This should be less than or equal + to OutputBufferLength. + + Return Value: + + NT status code. + + Remarks: + + This sample driver's custom data intersection handler handles all the + audio endpoints defined in this driver. Some endpoints support mono formats + while others do not. The handler is written such that it requires an exact + match in MaximumChannels. This simplifies the handler but requires the pin + data ranges to include a separate data range for mono formats if the pin + supports mono formats. + +--*/ +{ + ULONG requiredSize; + + UNREFERENCED_PARAMETER(PinId); + UNREFERENCED_PARAMETER(ResultantFormat); + + PAGED_CODE(); + + if (!IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) + { + return STATUS_NOT_IMPLEMENTED; + } + + requiredSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); + + // + // Validate return buffer size, if the request is only for the + // size of the resultant structure, return it now before + // returning other types of errors. + // + if (!OutputBufferLength) + { + *ResultantFormatLength = requiredSize; + return STATUS_BUFFER_OVERFLOW; + } + else if (OutputBufferLength < requiredSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // Verify channel count is supported. This routine assumes a separate data + // range for each supported channel count. + if (((PKSDATARANGE_AUDIO)MyDataRange)->MaximumChannels != ((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumChannels) + { + return STATUS_NO_MATCH; + } + + // + // Ok, let the class handler do the rest. + // + return STATUS_NOT_IMPLEMENTED; +} // DataRangeIntersection + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + *OutFilterDescriptor = &m_FilterDesc; + + return STATUS_SUCCESS; +} // GetDescription + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::Init +( + _In_ PUNKNOWN UnknownAdapter_, + _In_ PRESOURCELIST ResourceList_, + _In_ PPORTWAVERT Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(UnknownAdapter_); + UNREFERENCED_PARAMETER(ResourceList_); + UNREFERENCED_PARAMETER(Port_); + PAGED_CODE(); + + ASSERT(UnknownAdapter_); + ASSERT(Port_); + + DPF_ENTER(("[CMiniportWaveRT::Init]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + size_t size; + + // + // Init class data members + // + m_ulLoopbackAllocated = 0; + m_ulSystemAllocated = 0; + m_ulOffloadAllocated = 0; + m_dwCaptureAllocatedModes = 0; + m_dwBiDiCaptureAllocatedModes = 0; + m_dwSystemAllocatedModes = 0; + m_SystemStreams = NULL; + m_OffloadStreams = NULL; + m_LoopbackStreams = NULL; + m_bGfxEnabled = FALSE; + m_pbMuted = NULL; + m_plVolumeLevel = NULL; + m_plPeakMeter = NULL; + m_pMixFormat = NULL; + m_pDeviceFormat = NULL; + m_ulMixDrmContentId = 0; + m_LoopbackProtection = CONSTRICTOR_OPTION_DISABLE; + RtlZeroMemory(&m_MixDrmRights, sizeof(m_MixDrmRights)); + + // + // Init the audio-engine used by the render devices. + // + if (IsRenderDevice()) + { + // Basic validation + if (m_ulMaxSystemStreams == 0 || + m_ulMaxLoopbackStreams == 0) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // System streams. + size = sizeof(PCMiniportWaveRTStream) * m_ulMaxSystemStreams; + m_SystemStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + if (m_SystemStreams == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_SystemStreams, size); + + // Loopback streams. + size = sizeof(PCMiniportWaveRTStream) * m_ulMaxLoopbackStreams; + m_LoopbackStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + if (m_LoopbackStreams == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_LoopbackStreams, size); + + // Basic validation + if (IsOffloadSupported()) + { + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pDeviceFormats; + ULONG numDeviceFormats; + + if (m_ulMaxOffloadStreams == 0) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // Offload streams. + size = sizeof(PCMiniportWaveRTStream) * m_ulMaxOffloadStreams; + m_OffloadStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + if (m_OffloadStreams == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_OffloadStreams, size); + + // Formats. + m_pDeviceFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), MINWAVERT_POOLTAG); + if (m_pDeviceFormat == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + numDeviceFormats = GetAudioEngineSupportedDeviceFormats(&pDeviceFormats); + + if (numDeviceFormats < 1) + { + return STATUS_UNSUCCESSFUL; + } + RtlCopyMemory((PVOID)(m_pDeviceFormat), (PVOID)(pDeviceFormats), sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); + + m_pMixFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), MINWAVERT_POOLTAG); + if (m_pMixFormat == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + m_pMixFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + m_pMixFormat->DataFormat.Flags = 0; + m_pMixFormat->DataFormat.Reserved = 0; + m_pMixFormat->DataFormat.SampleSize = 0; + m_pMixFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; + m_pMixFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + m_pMixFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; + m_pMixFormat->WaveFormatExt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + m_pMixFormat->WaveFormatExt.Format.nChannels = 2; + m_pMixFormat->WaveFormatExt.Format.nSamplesPerSec = 48000; + m_pMixFormat->WaveFormatExt.Format.nBlockAlign = 4; + m_pMixFormat->WaveFormatExt.Format.nAvgBytesPerSec = 192000; + m_pMixFormat->WaveFormatExt.Format.wBitsPerSample = 16; + m_pMixFormat->WaveFormatExt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + m_pMixFormat->WaveFormatExt.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + m_pMixFormat->WaveFormatExt.Samples.wValidBitsPerSample = 16; + m_pMixFormat->WaveFormatExt.dwChannelMask = KSAUDIO_SPEAKER_STEREO; + + m_bGfxEnabled = FALSE; + + m_pbMuted = (PBOOL)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(BOOL), MINWAVERT_POOLTAG); + if (m_pbMuted == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_pbMuted, m_DeviceMaxChannels * sizeof(BOOL)); + + m_plVolumeLevel = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(LONG), MINWAVERT_POOLTAG); + if (m_plVolumeLevel == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_plVolumeLevel, m_DeviceMaxChannels * sizeof(LONG)); + + m_plPeakMeter = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(LONG), MINWAVERT_POOLTAG); + if (m_plPeakMeter == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_plPeakMeter, m_DeviceMaxChannels * sizeof(LONG)); + } + + // + // For DRM support. + // + if (!NT_SUCCESS(Port_->QueryInterface(IID_IDrmPort2, (PVOID *)&m_pDrmPort))) + { + m_pDrmPort = NULL; + } + } + + // + // For KS event support. + // + if (!NT_SUCCESS(Port_->QueryInterface(IID_IPortEvents, (PVOID *)&m_pPortEvents))) + { + m_pPortEvents = NULL; + } + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::NewStream +( + _Out_ PMINIPORTWAVERTSTREAM * OutStream, + _In_ PPORTWAVERTSTREAM OuterUnknown, + _In_ ULONG Pin, + _In_ BOOLEAN Capture, + _In_ PKSDATAFORMAT DataFormat +) +/*++ + +Routine Description: + + The NewStream function creates a new instance of a logical stream + associated with a specified physical channel. Callers of NewStream should + run at IRQL PASSIVE_LEVEL. + +Arguments: + + OutStream - + + OuterUnknown - + + Pin - + + Capture - + + DataFormat - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutStream); + ASSERT(DataFormat); + + DPF_ENTER(("[CMiniportWaveRT::NewStream]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PCMiniportWaveRTStream stream = NULL; + GUID signalProcessingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT; + + *OutStream = NULL; + + // + // If the data format attributes were specified, extract them. + // + if ( DataFormat->Flags & KSDATAFORMAT_ATTRIBUTES ) + { + // The attributes are aligned (QWORD alignment) after the data format + PKSMULTIPLE_ITEM attributes = (PKSMULTIPLE_ITEM) (((PBYTE)DataFormat) + ((DataFormat->FormatSize + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT)); + ntStatus = GetAttributesFromAttributeList(attributes, attributes->Size, &signalProcessingMode); + } + + // Check if we have enough streams. + // + if (NT_SUCCESS(ntStatus)) + { + ntStatus = ValidateStreamCreate(Pin, Capture, signalProcessingMode); + } + + // Determine if the format is valid. + // + if (NT_SUCCESS(ntStatus)) + { + ntStatus = IsFormatSupported(Pin, Capture, DataFormat); + } + + // Instantiate a stream. Stream must be in + // NonPagedPool(Nx) because of file saving. + // + if (NT_SUCCESS(ntStatus)) + { + stream = new (NonPagedPoolNx, MINWAVERT_POOLTAG) + CMiniportWaveRTStream(NULL); + + if (stream) + { + stream->AddRef(); + + ntStatus = + stream->Init + ( + this, + OuterUnknown, + Pin, + Capture, + DataFormat, + signalProcessingMode + ); + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(ntStatus)) + { + *OutStream = PMINIPORTWAVERTSTREAM(stream); + (*OutStream)->AddRef(); + + // The stream has references now for the caller. The caller expects these + // references to be there. + } + + // This is our private reference to the stream. The caller has + // its own, so we can release in any case. + // + if (stream) + { + stream->Release(); + } + + return ntStatus; +} // NewStream + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface + +Arguments: + + Interface - GUID + + Object - interface pointer to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PMINIPORTWAVERT(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRT)) + { + *Object = PVOID(PMINIPORTWAVERT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportAudioSignalProcessing)) + { + *Object = PVOID(PMINIPORTAudioSignalProcessing(this)); + } + // In this sample, IMiniportAudioEngineNode is supported only for offloading endpoints. + // at thso moment, offload could only be enabled for render endpoints not capture. + // Incorrectly support IMiniportAudioEngineNode interface by the miniport without underlying + // HWAudioEngine node will cause miniport::Init to fail. + else if (IsEqualGUIDAligned(Interface, IID_IMiniportAudioEngineNode) && IsOffloadSupported()) + { + *Object = (PVOID)(IMiniportAudioEngineNode*)this; + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceDescription(_Out_ PDEVICE_DESCRIPTION DmaDeviceDescription) +{ + PAGED_CODE (); + + ASSERT (DmaDeviceDescription); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceDescription]")); + + RtlZeroMemory (DmaDeviceDescription, sizeof (DEVICE_DESCRIPTION)); + + // + // Init device description. This sample is using the same info for all m_DeviceType(s). + // + + DmaDeviceDescription->Master = TRUE; + DmaDeviceDescription->ScatterGather = TRUE; + DmaDeviceDescription->Dma32BitAddresses = TRUE; + DmaDeviceDescription->InterfaceType = PCIBus; + DmaDeviceDescription->MaximumLength = 0xFFFFFFFF; + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::GetModes +( + _In_ ULONG Pin, + _Out_writes_opt_(*NumSignalProcessingModes) GUID* SignalProcessingModes, + _Inout_ ULONG* NumSignalProcessingModes +) +/* + +1. If Pin is not a valid pin number, + return STATUS_INVALID_PARAMETER. + +2. If Pin is a valid pin number and it supports n modes (n>0), + init out-parameters and return STATUS_SUCCESS. + +3. Else this pin doesn't support any mode, + return STATUS_NOT_SUPPORTED. + example: bridge pins or another mode-not-aware pins. + +*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::GetModes]")); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + ULONG numModes = 0; + MODE_AND_DEFAULT_FORMAT *modeInfo = NULL; + + if (Pin >= m_pMiniportPair->WaveDescriptor->PinCount) + { + return STATUS_INVALID_PARAMETER; + } + + // + // This method is valid only on the following pins: + // render is offload capable: + // sink (#0) and offload (#1) pins. + // render is NOT offload capable: + // sink (#0) pin. + // capture device: + // source (#1) pin. + // + numModes = GetPinSupportedDeviceModes(Pin, &modeInfo); + if (numModes == 0) + { + return STATUS_NOT_SUPPORTED; + } + + // If caller requests the modes, verify sufficient buffer size then return the modes + if (SignalProcessingModes != NULL) + { + if (*NumSignalProcessingModes < numModes) + { + *NumSignalProcessingModes = numModes; + ntStatus = STATUS_BUFFER_TOO_SMALL; + goto Done; + } + + for (ULONG i=0; i<numModes; ++i) + { + SignalProcessingModes[i] = modeInfo[i].Mode; + } + } + + ASSERT(numModes > 0); + *NumSignalProcessingModes = numModes; + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::ValidateStreamCreate +( + _In_ ULONG _Pin, + _In_ BOOLEAN _Capture, + _In_ GUID _SignalProcessingMode +) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::ValidateStreamCreate]")); + + NTSTATUS ntStatus = STATUS_NOT_SUPPORTED; + + if (_Capture) + { + if (IsLoopbackPin(_Pin)) + { + if (m_ulLoopbackAllocated < m_ulMaxLoopbackStreams) + { + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + else if (IsSystemCapturePin(_Pin)) + { + VERIFY_MODE_RESOURCES_AVAILABLE(m_dwCaptureAllocatedModes, _SignalProcessingMode, ntStatus) + } + else if (IsCellularBiDiCapturePin(_Pin)) + { + VERIFY_MODE_RESOURCES_AVAILABLE(m_dwBiDiCaptureAllocatedModes, _SignalProcessingMode, ntStatus) + } + else if (m_DeviceFormatsAndModes[_Pin].PinType == PINTYPE::KeywordCapturePin) + { + ntStatus = STATUS_SUCCESS; + } + } + else + { + if (IsSystemRenderPin(_Pin)) + { + VERIFY_MODE_RESOURCES_AVAILABLE(m_dwSystemAllocatedModes, _SignalProcessingMode, ntStatus) + } + else if (IsOffloadPin(_Pin)) + { + if (m_ulOffloadAllocated < m_ulMaxOffloadStreams) + { + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::StreamCreated +( + _In_ ULONG _Pin, + _In_ PCMiniportWaveRTStream _Stream +) +{ + PAGED_CODE(); + + PCMiniportWaveRTStream * streams = NULL; + ULONG count = 0; + + DPF_ENTER(("[CMiniportWaveRT::StreamOpened]")); + + if (IsSystemCapturePin(_Pin)) + { + ALLOCATE_MODE_RESOURCES(m_dwCaptureAllocatedModes, _Stream->GetSignalProcessingMode()) + return STATUS_SUCCESS; + } + if (IsCellularBiDiCapturePin(_Pin)) + { + ALLOCATE_MODE_RESOURCES(m_dwBiDiCaptureAllocatedModes, _Stream->GetSignalProcessingMode()) + return STATUS_SUCCESS; + } + else if (IsLoopbackPin(_Pin)) + { + m_ulLoopbackAllocated++; + streams = m_LoopbackStreams; + count = m_ulMaxLoopbackStreams; + _Stream->m_SaveData.Disable(m_MixDrmRights.CopyProtect); + } + else if (IsSystemRenderPin(_Pin)) + { + ALLOCATE_MODE_RESOURCES(m_dwSystemAllocatedModes, _Stream->GetSignalProcessingMode()) + m_ulSystemAllocated++; + streams = m_SystemStreams; + count = m_ulMaxSystemStreams; + } + else if (IsOffloadPin(_Pin)) + { + m_ulOffloadAllocated++; + streams = m_OffloadStreams; + count = m_ulMaxOffloadStreams; + } + + // + // Cache this stream's ptr. + // + if (streams != NULL) + { + ULONG i = 0; + for (; i<count; ++i) + { + if (streams[i] == NULL) + { + streams[i] = _Stream; + break; + } + } + ASSERT(i != count); + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::StreamClosed +( + _In_ ULONG _Pin, + _In_ PCMiniportWaveRTStream _Stream +) +{ + PAGED_CODE(); + + bool updateDrmRights = false; + PCMiniportWaveRTStream * streams = NULL; + ULONG count = 0; + + DPF_ENTER(("[CMiniportWaveRT::StreamClosed]")); + + if (IsSystemCapturePin(_Pin)) + { + FREE_MODE_RESOURCES(m_dwCaptureAllocatedModes, _Stream->GetSignalProcessingMode()) + return STATUS_SUCCESS; + } + if (IsCellularBiDiCapturePin(_Pin)) + { + FREE_MODE_RESOURCES(m_dwBiDiCaptureAllocatedModes, _Stream->GetSignalProcessingMode()) + return STATUS_SUCCESS; + } + else if (IsLoopbackPin(_Pin)) + { + m_ulLoopbackAllocated--; + streams = m_LoopbackStreams; + count = m_ulMaxLoopbackStreams; + } + else if (IsSystemRenderPin(_Pin)) + { + FREE_MODE_RESOURCES(m_dwSystemAllocatedModes, _Stream->GetSignalProcessingMode()) + m_ulSystemAllocated--; + streams = m_SystemStreams; + count = m_ulMaxSystemStreams; + updateDrmRights = true; + } + else if (IsOffloadPin(_Pin)) + { + m_ulOffloadAllocated--; + streams = m_OffloadStreams; + count = m_ulMaxOffloadStreams; + updateDrmRights = true; + } + + // + // Cleanup. + // + if (streams != NULL) + { + ULONG i = 0; + for (; i<count; ++i) + { + if (streams[i] == _Stream) + { + streams[i] = NULL; + break; + } + } + ASSERT(i != count); + } + + // + // Update mixed drm rights. + // + if (updateDrmRights) + { + UpdateDrmRights(); + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::GetAttributesFromAttributeList +( + _In_ const KSMULTIPLE_ITEM *_pAttributes, + _In_ size_t _Size, + _Out_ GUID* _pSignalProcessingMode +) +/*++ + +Routine Description: + + Processes attributes list and return known attributes. + +Arguments: + + _pAttributes - pointer to KSMULTIPLE_ITEM at head of an attributes list. + + _Size - count of bytes in the buffer pointed to by _pAttributes. The routine + verifies sufficient buffer size while processing the attributes. + + _pSignalProcessingMode - returns the signal processing mode extracted from + the attribute list, or AUDIO_SIGNALPROCESSINGMODE_DEFAULT if the attribute + is not present in the list. + +Return Value: + + NT status code. + +Remarks + + This function is currently written for a single supported attribute + (KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE). As additional attributes are defined in the future, + this function should be rewritten to be data driven through tables, etc. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::GetAttributesFromAttributeList]")); + + size_t cbRemaining = _Size; + + *_pSignalProcessingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT; + + if (cbRemaining < sizeof(KSMULTIPLE_ITEM)) + { + return STATUS_INVALID_PARAMETER; + } + cbRemaining -= sizeof(KSMULTIPLE_ITEM); + + // + // Extract attributes. + // + PKSATTRIBUTE attributeHeader = (PKSATTRIBUTE)(_pAttributes + 1); + + for (ULONG i = 0; i < _pAttributes->Count; i++) + { + if (cbRemaining < sizeof(KSATTRIBUTE)) + { + return STATUS_INVALID_PARAMETER; + } + + if (attributeHeader->Attribute == KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE) + { + KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE* signalProcessingModeAttribute; + + if (cbRemaining < sizeof(KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE)) + { + return STATUS_INVALID_PARAMETER; + } + + if (attributeHeader->Size != sizeof(KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE)) + { + return STATUS_INVALID_PARAMETER; + } + + signalProcessingModeAttribute = (KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE*)attributeHeader; + + // Return mode to caller. + *_pSignalProcessingMode = signalProcessingModeAttribute->SignalProcessingMode; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + // Adjust pointer and buffer size to next attribute (QWORD aligned) + ULONG cbAttribute = ((attributeHeader->Size + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT); + + attributeHeader = (PKSATTRIBUTE) (((PBYTE)attributeHeader) + cbAttribute); + cbRemaining -= cbAttribute; + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::IsFormatSupported +( + _In_ ULONG _ulPin, + _In_ BOOLEAN _bCapture, + _In_ PKSDATAFORMAT _pDataFormat +) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::IsFormatSupported]")); + + NTSTATUS ntStatus = STATUS_NO_MATCH; + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pPinFormats = NULL; + ULONG cPinFormats = 0; + + UNREFERENCED_PARAMETER(_bCapture); + + if (_ulPin >= m_pMiniportPair->WaveDescriptor->PinCount) + { + return STATUS_INVALID_PARAMETER; + } + + cPinFormats = GetPinSupportedDeviceFormats(_ulPin, &pPinFormats); + + for (UINT iFormat = 0; iFormat < cPinFormats; iFormat++) + { + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pFormat = &pPinFormats[iFormat]; + // KSDATAFORMAT VALIDATION + if (!IsEqualGUIDAligned(pFormat->DataFormat.MajorFormat, _pDataFormat->MajorFormat)) { continue; } + if (!IsEqualGUIDAligned(pFormat->DataFormat.SubFormat, _pDataFormat->SubFormat)) { continue; } + if (!IsEqualGUIDAligned(pFormat->DataFormat.Specifier, _pDataFormat->Specifier)) { continue; } + if (pFormat->DataFormat.FormatSize < sizeof(KSDATAFORMAT_WAVEFORMATEX)) { continue; } + + // WAVEFORMATEX VALIDATION + PWAVEFORMATEX pWaveFormat = reinterpret_cast<PWAVEFORMATEX>(_pDataFormat + 1); + + if (pWaveFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE) + { + if (pWaveFormat->wFormatTag != EXTRACT_WAVEFORMATEX_ID(&(pFormat->WaveFormatExt.SubFormat))) { continue; } + } + if (pWaveFormat->nChannels != pFormat->WaveFormatExt.Format.nChannels) { continue; } + if (pWaveFormat->nSamplesPerSec != pFormat->WaveFormatExt.Format.nSamplesPerSec) { continue; } + if (pWaveFormat->nBlockAlign != pFormat->WaveFormatExt.Format.nBlockAlign) { continue; } + if (pWaveFormat->wBitsPerSample != pFormat->WaveFormatExt.Format.wBitsPerSample) { continue; } + + if (pWaveFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE) + { + ntStatus = STATUS_SUCCESS; + break; + } + + // WAVEFORMATEXTENSIBLE VALIDATION + if (pWaveFormat->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { continue; } + + PWAVEFORMATEXTENSIBLE pWaveFormatExt = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pWaveFormat); + if (pWaveFormatExt->Samples.wValidBitsPerSample != pFormat->WaveFormatExt.Samples.wValidBitsPerSample) { continue; } + if (pWaveFormatExt->dwChannelMask != pFormat->WaveFormatExt.dwChannelMask) { continue; } + if (!IsEqualGUIDAligned(pWaveFormatExt->SubFormat, pFormat->WaveFormatExt.SubFormat)) { continue; } + + ntStatus = STATUS_SUCCESS; + break; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandlerProposedFormat +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PKSP_PIN kspPin = NULL; + PKSDATAFORMAT pKsFormat = NULL; + ULONG cbMinSize = 0; + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::PropertyHandlerProposedFormat]")); + + // All properties handled by this handler require at least a KSP_PIN descriptor. + + // Verify instance data stores at least KSP_PIN fields beyond KSPPROPERTY. + if (PropertyRequest->InstanceSize < (sizeof(KSP_PIN) - RTL_SIZEOF_THROUGH_FIELD(KSP_PIN, Property))) + { + return STATUS_INVALID_PARAMETER; + } + + // Extract property descriptor from property request instance data + kspPin = CONTAINING_RECORD(PropertyRequest->Instance, KSP_PIN, PinId); + + // + // This method is valid only on streaming pins. + // + if (IsSystemRenderPin(kspPin->PinId) || + IsLoopbackPin(kspPin->PinId) || + IsOffloadPin(kspPin->PinId) || + IsSystemCapturePin(kspPin->PinId)|| + IsCellularBiDiCapturePin(kspPin->PinId)) + { + ntStatus = STATUS_SUCCESS; + } + else if (IsBridgePin(kspPin->PinId)) + { + ntStatus = STATUS_NOT_SUPPORTED; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + + cbMinSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + + // Handle KSPROPERTY_TYPE_BASICSUPPORT query + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + + return PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // Only SET is supported for this property + if ((PropertyRequest->Verb & KSPROPERTY_TYPE_SET) == 0) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + pKsFormat = (PKSDATAFORMAT)PropertyRequest->Value; + ntStatus = IsFormatSupported(kspPin->PinId, + IsSystemCapturePin(kspPin->PinId) || IsCellularBiDiCapturePin(kspPin->PinId) || + IsLoopbackPin(kspPin->PinId), + pKsFormat); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + + // + // Make sure there are enough resources to handle a new pin creation with + // this format. + // + if (IsOffloadPin(kspPin->PinId)) + { + ntStatus = ValidateStreamCreate(kspPin->PinId, FALSE, AUDIO_SIGNALPROCESSINGMODE_DEFAULT); + } + + return ntStatus; +} // PropertyHandlerProposedFormat + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandlerProposedFormat2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PKSP_PIN kspPin = NULL; + ULONG cbMinSize = 0; + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + ULONG numModes = 0; + MODE_AND_DEFAULT_FORMAT *modeInfo = NULL; + MODE_AND_DEFAULT_FORMAT *modeTemp = NULL; + PKSMULTIPLE_ITEM pKsItemsHeader = NULL; + PKSMULTIPLE_ITEM pKsItemsHeaderOut = NULL; + size_t cbItemsList = 0; + GUID signalProcessingMode = {0}; + BOOLEAN bFound = FALSE; + ULONG i; + + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::PropertyHandlerProposedFormat2]")); + + // All properties handled by this handler require at least a KSP_PIN descriptor. + + // Verify instance data stores at least KSP_PIN fields beyond KSPPROPERTY. + if (PropertyRequest->InstanceSize < (sizeof(KSP_PIN) - RTL_SIZEOF_THROUGH_FIELD(KSP_PIN, Property))) + { + return STATUS_INVALID_PARAMETER; + } + + // Extract property descriptor from property request instance data + kspPin = CONTAINING_RECORD(PropertyRequest->Instance, KSP_PIN, PinId); + + if (kspPin->PinId >= m_pMiniportPair->WaveDescriptor->PinCount) + { + return STATUS_INVALID_PARAMETER; + } + + // + // This property is supported only on some streaming pins. + // + numModes = GetPinSupportedDeviceModes(kspPin->PinId, &modeInfo); + + ASSERT((modeInfo != NULL && numModes > 0) || (modeInfo == NULL && numModes == 0)); + + if (modeInfo == NULL) + { + return STATUS_NOT_SUPPORTED; + } + + // + // Even for pins that support modes, the pin might not support proposed formats + // + bFound = FALSE; + for (i=0, modeTemp=modeInfo; i<numModes; ++i, ++modeTemp) + { + if (modeTemp->DefaultFormat != NULL) + { + bFound = TRUE; + break; + } + } + + if (!bFound) + { + return STATUS_NOT_SUPPORTED; + } + + // + // The property is generally supported on this pin. Handle basic support request. + // + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL); + } + + // + // Get the mode if specified. + // + pKsItemsHeader = (PKSMULTIPLE_ITEM)(kspPin + 1); + cbItemsList = (((PBYTE)PropertyRequest->Instance) + PropertyRequest->InstanceSize) - (PBYTE)pKsItemsHeader; + + ntStatus = GetAttributesFromAttributeList(pKsItemsHeader, cbItemsList, &signalProcessingMode); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + + // + // Get the info associated with this mode. + // + bFound = FALSE; + for (i=0; i<numModes; ++i, ++modeInfo) + { + if (modeInfo->Mode == signalProcessingMode) + { + bFound = TRUE; + break; + } + } + + // Either the mode isn't supported, or the driver doesn't support a + // proprosed format for this specific mode. + if (!bFound || modeInfo->DefaultFormat == NULL) + { + return STATUS_NOT_SUPPORTED; + } + + // + // Compute output data buffer. + // + cbMinSize = modeInfo->DefaultFormat->FormatSize; + cbMinSize = (cbMinSize + 7) & ~7; + + pKsItemsHeaderOut = (PKSMULTIPLE_ITEM)((PBYTE)PropertyRequest->Value + cbMinSize); + + if (cbItemsList > MAXULONG) + { + return STATUS_INVALID_PARAMETER; + } + + // Total # of bytes. + ntStatus = RtlULongAdd(cbMinSize, (ULONG)cbItemsList, &cbMinSize); + if (!NT_SUCCESS(ntStatus)) + { + return STATUS_INVALID_PARAMETER; + } + + // Property not supported. + if (cbMinSize == 0) + { + return STATUS_NOT_SUPPORTED; + } + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // Only GET is supported for this property + if ((PropertyRequest->Verb & KSPROPERTY_TYPE_GET) == 0) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + // Copy the proposed default format. + RtlCopyMemory(PropertyRequest->Value, modeInfo->DefaultFormat, modeInfo->DefaultFormat->FormatSize); + + // Copy back the attribute list. + ASSERT(cbItemsList > 0); + ((KSDATAFORMAT*)PropertyRequest->Value)->Flags = KSDATAFORMAT_ATTRIBUTES; + RtlCopyMemory(pKsItemsHeaderOut, pKsItemsHeader, cbItemsList); + + PropertyRequest->ValueSize = cbMinSize; + + return STATUS_SUCCESS; +} // PropertyHandlerProposedFormat + + + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandlerEffectListRequest +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + GUID StreamEffectList[] = + { + AUDIO_EFFECT_TYPE_LOUDNESS_EQUALIZER, + AUDIO_EFFECT_TYPE_VIRTUAL_SURROUND + }; + + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::PropertyHandlerEffectListRequest]")); + + // This specific APO->driver communication example is mainly added to show to this communication is done. + // It skips the pin id validation and returns pin specific answers to the caller, which a real miniport + // audio driver probably needs to tate care of. + + // Handle KSPROPERTY_TYPE_BASICSUPPORT query + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL); + } + + // Verify instance data stores at least KSP_PIN fields beyond KSPPROPERTY. + if (PropertyRequest->InstanceSize < (sizeof(KSP_PIN) - RTL_SIZEOF_THROUGH_FIELD(KSP_PIN, Property))) + { + return STATUS_INVALID_PARAMETER; + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM ksMultipleItem; + ULONG ulEffectsCount = ARRAYSIZE(StreamEffectList); + ULONG cbMinSize; + LPGUID pEffectGuids = NULL; + + // Compute min value size requirements + cbMinSize = sizeof(KSMULTIPLE_ITEM) + ulEffectsCount * sizeof(GUID); + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + // Value is a KSMULTIPLE_ITEM followed by list of GUIDs. + ksMultipleItem = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + pEffectGuids = (LPGUID)(ksMultipleItem + 1); + + // Copy effect guid + RtlCopyMemory(pEffectGuids, StreamEffectList, ulEffectsCount * sizeof(GUID)); + + // Miniport filled in the list of GUIDs. Fill in the KSMULTIPLE_ITEM header. + ksMultipleItem->Size = sizeof(KSMULTIPLE_ITEM) + ulEffectsCount * sizeof(GUID); + ksMultipleItem->Count = ulEffectsCount; + + PropertyRequest->ValueSize = ksMultipleItem->Size; + return STATUS_SUCCESS; + } + + return STATUS_INVALID_DEVICE_REQUEST; + +} // PropertyHandlerEffectListRequest +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::UpdateDrmRights +( + void +) +/*++ + +Routine Description: + + Updates the mixed DrmRights. This is done by creating an array of existing + content ids and asking DrmPort to create a new contend id with a mixed + DrmRights structure. + The new DrmRights structure should be enforced, if everything goes well. + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::UpdateDrmRights]")); + + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + ULONG ulMixDrmContentId = 0; + BOOL fCreatedContentId = FALSE; + DRMRIGHTS MixDrmRights = {FALSE, 0, FALSE}; + ULONG ulContentIndex = 0; + ULONG* ulContentIds = NULL; + + // + // This function only runs if IID_DrmPort is implemented in Wave port. + // + if (!m_pDrmPort) + { + return STATUS_UNSUCCESSFUL; + } + + ulContentIds = new (NonPagedPoolNx, MINWAVERT_POOLTAG) ULONG[m_ulMaxSystemStreams + m_ulMaxOffloadStreams]; + if (!ulContentIds) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Create an array of all StreamIds. + // + for (ULONG i = 0; i < m_ulMaxSystemStreams; i++) + { + if (m_SystemStreams[i]) + { + ulContentIds[ulContentIndex] = m_SystemStreams[i]->m_ulContentId; + ulContentIndex++; + } + } + + for (ULONG i = 0; i < m_ulMaxOffloadStreams; i++) + { + ASSERT(IsOffloadSupported()); + + if (m_OffloadStreams[i]) + { + ulContentIds[ulContentIndex] = m_OffloadStreams[i]->m_ulContentId; + ulContentIndex++; + } + } + + // + // Create the new contentId. + // + if (ulContentIndex) + { + ntStatus = + m_pDrmPort->CreateContentMixed + ( + ulContentIds, + ulContentIndex, + &ulMixDrmContentId + ); + + if (NT_SUCCESS(ntStatus)) + { + fCreatedContentId = TRUE; + ntStatus = + m_pDrmPort->GetContentRights + ( + ulMixDrmContentId, + &MixDrmRights + ); + } + } + + // + // If successful, destroy the old ContentId and update global rights. + // + if (NT_SUCCESS(ntStatus)) + { + m_pDrmPort->DestroyContent(m_ulMixDrmContentId); + m_ulMixDrmContentId = ulMixDrmContentId; + RtlCopyMemory(&m_MixDrmRights, &MixDrmRights, sizeof(m_MixDrmRights)); + + // + // At this point the driver should enforce the new DrmRights. + // The sample driver handles DrmRights per stream basis, and + // stops writing the stream to disk, if CopyProtect = TRUE. + // + + // + // If DigitalOutputDisable or CopyProtect is true, enable HDCP + // + if (m_DeviceType == eHdmiRenderDevice && + (m_MixDrmRights.DigitalOutputDisable || m_MixDrmRights.CopyProtect)) + { + // Enable HDCP here. + } + } + + // + // Cleanup if failed + // + if (!NT_SUCCESS(ntStatus) && fCreatedContentId) + { + m_pDrmPort->DestroyContent(ulMixDrmContentId); + } + + // + // Free allocated memory. + // + ASSERT(ulContentIds); + delete [] ulContentIds; + ulContentIds = NULL; + + return ntStatus; +} // UpdateDrmRights + +//============================================================================= +#pragma code_seg("PAGE") + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorSupportedPatterns) +{ + CONTOSO_SUPPORTEDPATTERNSVALUE *value; + + PAGED_CODE(); + + NT_ASSERT(PropertyRequest->ValueSize >= sizeof(*value)); + + // Does this filter support a sound detector? + if ((m_DeviceFlags & ENDPOINT_SOUNDDETECTOR_SUPPORTED) == 0) + { + return STATUS_NOT_SUPPORTED; + } + + value = (CONTOSO_SUPPORTEDPATTERNSVALUE*)PropertyRequest->Value; + + RtlZeroMemory(value, sizeof(*value)); + + value->MultipleItem.Size = sizeof(*value); + value->MultipleItem.Count = 1; + value->PatternType[0] = CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER; + + PropertyRequest->ValueSize = sizeof(*value); + + return STATUS_SUCCESS; +} + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorPatterns) +{ + KSMULTIPLE_ITEM *itemsHeader; + SOUNDDETECTOR_PATTERNHEADER *patternHeader; + CONTOSO_KEYWORDCONFIGURATION *pattern; + ULONG cbRemaining; // Tracks bytes remaining in property value + + PAGED_CODE(); + + cbRemaining = PropertyRequest->ValueSize; + + // The SYSVADPROPERTY_ITEM for this property ensures the value size is at + // least sizeof KSMULTIPLE_ITEM. + if (cbRemaining < sizeof(KSMULTIPLE_ITEM)) + { + return STATUS_INVALID_PARAMETER; + } + + itemsHeader = (KSMULTIPLE_ITEM*)PropertyRequest->Value; + + // Verify property value is large enough to include the items + if (itemsHeader->Size > cbRemaining) + { + PropertyRequest->ValueSize = 0; + return STATUS_INVALID_PARAMETER; + } + + // No items so clear the configuration. + if (itemsHeader->Count == 0) + { + m_KeywordDetector.ResetDetector(); + return STATUS_SUCCESS; + } + + // This sample supports only 1 pattern type. + if (itemsHeader->Count > 1) + { + PropertyRequest->ValueSize = 0; + return STATUS_NOT_SUPPORTED; + } + + // Bytes remaining after the items header + cbRemaining = itemsHeader->Size - sizeof(*itemsHeader); + + // Verify the property value is large enough to include the pattern header. + if (cbRemaining < sizeof(SOUNDDETECTOR_PATTERNHEADER)) + { + PropertyRequest->ValueSize = 0; + return STATUS_INVALID_PARAMETER; + } + + patternHeader = (SOUNDDETECTOR_PATTERNHEADER*)(itemsHeader + 1); + + // Verify the pattern type is supported. + if (patternHeader->PatternType != CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER) + { + PropertyRequest->ValueSize = 0; + return STATUS_NOT_SUPPORTED; + } + + // Verify the property value is large enough for the pattern. + if (cbRemaining < patternHeader->Size) + { + PropertyRequest->ValueSize = 0; + return STATUS_INVALID_PARAMETER; + } + + // Verify the pattern is large enough. + if (patternHeader->Size != sizeof(CONTOSO_KEYWORDCONFIGURATION)) + { + PropertyRequest->ValueSize = 0; + return STATUS_INVALID_PARAMETER; + } + + pattern = (CONTOSO_KEYWORDCONFIGURATION*)(patternHeader); + + // Program the hardware. + m_KeywordDetector.DownloadDetectorData(pattern->ContosoDetectorConfigurationData); + + return STATUS_SUCCESS; +} + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorArmed) +{ + PAGED_CODE(); + + // The SYSVADPROPERTY_ITEM for this property ensures the value size is at + // least sizeof BOOL. + NT_ASSERT(PropertyRequest->ValueSize >= sizeof(BOOL)); + + RtlZeroMemory(PropertyRequest->Value, PropertyRequest->ValueSize); + *(BOOL*)PropertyRequest->Value = m_KeywordDetector.GetArmed(); + + return STATUS_SUCCESS; +} + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorArmed) +{ + NTSTATUS ntStatus; + BOOL armed; + + PAGED_CODE(); + + // The SYSVADPROPERTY_ITEM for this property ensures the value size is at + // least sizeof BOOL. + NT_ASSERT(PropertyRequest->ValueSize >= sizeof(BOOL)); + + armed = ((*(BOOL*)PropertyRequest->Value) != 0); + + ntStatus = m_KeywordDetector.SetArmed(armed); + + if (NT_SUCCESS(ntStatus) && armed) + { + // FUTURE-2014/10/20 For now immediately signal a detection as soon as + // it is armed, but later, find a better way to demonstrate this from + // within CKeywordDetector. + m_pPortEvents->GenerateEventList(const_cast<GUID*>(&KSEVENTSETID_SoundDetector), KSEVENT_SOUNDDETECTOR_MATCHDETECTED, FALSE, 0, FALSE, 0); + m_KeywordDetector.SetArmed(FALSE); + } + + return ntStatus; +} + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorMatchResult) +{ + CONTOSO_KEYWORDDETECTIONRESULT *value; + + PAGED_CODE(); + + if (PropertyRequest->ValueSize < sizeof(*value)) + { + return STATUS_INVALID_PARAMETER; + } + + value = (CONTOSO_KEYWORDDETECTIONRESULT *)PropertyRequest->Value; + + RtlZeroMemory(value, sizeof(*value)); + + value->Header.Size = sizeof(CONTOSO_KEYWORDDETECTIONRESULT); + value->Header.PatternType = CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER; + value->ContosoDetectorResultData = m_KeywordDetector.GetDetectorData(); + + PropertyRequest->ValueSize = sizeof(*value); + + return STATUS_SUCCESS; +} + +#pragma code_seg() +NTSTATUS CMiniportWaveRT_EventHandler_SoundDetectorMatchDetected +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + CMiniportWaveRT* miniport = reinterpret_cast<CMiniportWaveRT*>(EventRequest->MajorTarget); + return miniport->EventHandler_SoundDetectorMatchDetected(EventRequest); +} + +#pragma code_seg() +NTSTATUS CMiniportWaveRT::EventHandler_SoundDetectorMatchDetected +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + if (EventRequest->Verb == PCEVENT_VERB_ADD) + { + _IRQL_limited_to_(PASSIVE_LEVEL); + m_pPortEvents->AddEventToEventList(EventRequest->EventEntry); + } + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_WaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CMiniportWaveRT* pWaveHelper = reinterpret_cast<CMiniportWaveRT*>(PropertyRequest->MajorTarget); + + if (pWaveHelper == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + pWaveHelper->AddRef(); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_SysVAD)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS: + ntStatus = pWaveHelper->PropertyHandlerEffectListRequest(PropertyRequest); + break; + default: + DPF(D_TERSE, ("[PropertyHandler_WaveFilter: Invalid Device Request]")); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Pin)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_PIN_PROPOSEDATAFORMAT: + ntStatus = pWaveHelper->PropertyHandlerProposedFormat(PropertyRequest); + break; + + case KSPROPERTY_PIN_PROPOSEDATAFORMAT2: + ntStatus = pWaveHelper->PropertyHandlerProposedFormat2(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandler_WaveFilter: Invalid Device Request]")); + } + } + else if ((pWaveHelper->m_DeviceType == eHdmiRenderDevice || + pWaveHelper->m_DeviceType == eCellularDevice || + pWaveHelper->m_DeviceType == eHandsetSpeakerDevice) && + IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_AUDIO_VOLUMELEVEL: + ntStatus = PropertyHandler_Volume( + pWaveHelper->m_pAdapterCommon, + PropertyRequest, + pWaveHelper->m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_MUTE: + ntStatus = PropertyHandler_Mute( + pWaveHelper->m_pAdapterCommon, + PropertyRequest, + pWaveHelper->m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_PEAKMETER2: + ntStatus = PropertyHandler_PeakMeter2( + pWaveHelper->m_pAdapterCommon, + PropertyRequest, + pWaveHelper->m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_CPU_RESOURCES: + ntStatus = PropertyHandler_CpuResources(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandler_WaveFilter: Invalid Device Request]")); + + } + } + + pWaveHelper->Release(); + + return ntStatus; +} // PropertyHandler_WaveFilter +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_OffloadPin +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_OffloadPin)) + { + switch (PropertyRequest->PropertyItem->Id) + { + //KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER + + case KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER: + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ULONG cbMinSize = sizeof(ULONG_PTR); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + ULONG_PTR *streamObjectPtr = static_cast<ULONG_PTR*>(PropertyRequest->Value); + *streamObjectPtr = (ULONG_PTR)(PropertyRequest->MinorTarget); + ntStatus = STATUS_SUCCESS; + } + } + + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + case KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER: + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + ULONG cbMinSize = sizeof(ULONG_PTR); + if (PropertyRequest->InstanceSize < cbMinSize) + { + return STATUS_INVALID_PARAMETER; + } + else + { + ULONG_PTR *streamObjectPtr = static_cast<ULONG_PTR*>(PropertyRequest->Instance); + if (*streamObjectPtr == (ULONG_PTR)(PropertyRequest->MinorTarget)) + { + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_UNSUCCESSFUL; + } + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + default: + DPF(D_TERSE, ("[PropertyHandler_OffloadPin: Invalid Request]")); + } + } + return ntStatus; +} + + +// ISSUE-2014/10/20 Add synchronization mechanism throughout this class +// ISSUE-2014/10/20 Add comment headers and commenting throughout +#pragma code_seg("PAGE") +CKeywordDetector::CKeywordDetector() + : + m_qpcStartCapture(0), + m_nLastQueuedPacket(-1), + m_SoundDetectorArmed(FALSE), + m_SoundDetectorData(0) +{ + PAGED_CODE(); + + // Initialize our pool of packets and the list structures + KeInitializeSpinLock(&PacketPoolSpinLock); + KeInitializeSpinLock(&PacketFifoSpinLock); + ResetFifo(); +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::ResetDetector() +{ + PAGED_CODE(); + + m_SoundDetectorData = 0; +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::DownloadDetectorData(LONGLONG Data) +{ + PAGED_CODE(); + + m_SoundDetectorData = Data; +} + +#pragma code_seg("PAGE") +LONGLONG CKeywordDetector::GetDetectorData() +{ + PAGED_CODE(); + + return m_SoundDetectorData; +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::ResetFifo() +{ + PAGED_CODE(); + + m_qpcStartCapture = 0; + m_nLastQueuedPacket = (-1); + InitializeListHead(&PacketPoolHead); + InitializeListHead(&PacketFifoHead); + + for (int i = 0; i < ARRAYSIZE(PacketPool); i++) + { + InsertTailList(&PacketPoolHead, &PacketPool[i].ListEntry); + } + return; +} + +#pragma code_seg("PAGE") +NTSTATUS CKeywordDetector::SetArmed(BOOL Arm) +{ + PAGED_CODE(); + + BOOL previousArming = m_SoundDetectorArmed; + + m_SoundDetectorArmed = Arm; + + if (Arm && !previousArming && m_qpcStartCapture == 0) + { + StartBufferingStream(); + } + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +BOOL CKeywordDetector::GetArmed() +{ + PAGED_CODE(); + + return m_SoundDetectorArmed; +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::Run() +{ + PAGED_CODE(); + + if (m_qpcStartCapture == 0) + { + StartBufferingStream(); + } +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::Stop() +{ + PAGED_CODE(); + + ResetFifo(); +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::StartBufferingStream() +{ + LARGE_INTEGER qpc; + LARGE_INTEGER qpcFrequency; + + PAGED_CODE(); + + NT_ASSERT(m_qpcStartCapture == 0); + NT_ASSERT(IsListEmpty(&PacketFifoHead)); + + qpc = KeQueryPerformanceCounter(&qpcFrequency); + m_qpcStartCapture = qpc.QuadPart; + + return; +} + +#pragma code_seg() +VOID CKeywordDetector::DpcRoutine(LONGLONG PerformanceCounter, LONGLONG PerformanceFrequency) +{ + LONGLONG currentPacket; + LONGLONG packetsToQueue; + + if (m_qpcStartCapture <= 0) + { + return; + } + + currentPacket = (PerformanceCounter - m_qpcStartCapture) * (SamplesPerSecond / SamplesPerPacket) / PerformanceFrequency; + packetsToQueue = currentPacket - m_nLastQueuedPacket; + + while (packetsToQueue > 0) + { + LIST_ENTRY* packetListEntry; + PACKET_ENTRY* packetEntry; + LONGLONG* signature; + + do + { + packetListEntry = ExInterlockedRemoveHeadList(&PacketPoolHead, &PacketPoolSpinLock); + if (packetListEntry != NULL) break; + + // Pool is empty, no room to buffer more, an overrun is occurring. Drop and reuse the + // oldest packet from head of fifo. + + // Since the pool is empty, the fifo should be full. However, although unlikely, the + // driver might empty the fifo before this routine removes a packet. In that case, the + // pool should have packets available again. Therefore this is a retry loop. + packetListEntry = ExInterlockedRemoveHeadList(&PacketFifoHead, &PacketFifoSpinLock); + if (packetListEntry != NULL) break; + } while (TRUE); + + packetEntry = CONTAINING_RECORD(packetListEntry, PACKET_ENTRY, ListEntry); + + packetEntry->PacketNumber = ++m_nLastQueuedPacket; + packetEntry->QpcWhenSampled = m_qpcStartCapture + (packetEntry->PacketNumber * PerformanceFrequency * SamplesPerPacket / SamplesPerSecond); + + RtlZeroMemory(&packetEntry->Samples[0], sizeof(packetEntry->Samples)); + + // For test purposes, embed the packet number and sample time into the audio data + signature = (LONGLONG*)(&packetEntry->Samples[0]); + + signature[0] = packetEntry->PacketNumber; + signature[1] = packetEntry->QpcWhenSampled; + + ExInterlockedInsertTailList(&PacketFifoHead, packetListEntry, &PacketFifoSpinLock); + + packetsToQueue -= 1; + } +} + +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CKeywordDetector::GetReadPacket +( + _In_ ULONG PacketsPerWaveRtBuffer, + _In_ ULONG WaveRtBufferSize, + _Out_writes_(WaveRtBufferSize) BYTE *WaveRtBuffer, + _Out_ ULONG *PacketNumber, + _Out_ ULONG64 *PerformanceCounterValue, + _Out_ BOOL *MoreData +) +{ + NTSTATUS ntStatus; + BYTE *packetData; + PACKET_ENTRY *packetEntry; + LIST_ENTRY *packetListEntry = NULL; + ULONG packetSize = WaveRtBufferSize / PacketsPerWaveRtBuffer; + + NT_ASSERT(SamplesPerPacket * 2 == packetSize); + NT_ASSERT(sizeof(packetEntry->Samples) == packetSize); + + packetListEntry = ExInterlockedRemoveHeadList(&PacketFifoHead, &PacketFifoSpinLock); + if (packetListEntry == NULL) + { + ntStatus = STATUS_DEVICE_NOT_READY; + goto Exit; + } + packetEntry = CONTAINING_RECORD(packetListEntry, PACKET_ENTRY, ListEntry); + + packetData = WaveRtBuffer + ((packetEntry->PacketNumber * packetSize) % WaveRtBufferSize); + + ntStatus = RtlLongLongToULong(packetEntry->PacketNumber, PacketNumber); + if (!NT_SUCCESS(ntStatus)) + { + goto Exit; + } + + *PerformanceCounterValue = packetEntry->QpcWhenSampled; + *MoreData = !IsListEmpty(&PacketFifoHead); + RtlCopyMemory(packetData, packetEntry->Samples, sizeof(packetEntry->Samples)); + +Exit: + if (packetListEntry != NULL) + { + ExInterlockedInsertTailList(&PacketPoolHead, packetListEntry, &PacketPoolSpinLock); + } + + return ntStatus; +} + diff --git a/audio/sysvad/EndpointsCommon/minwavert.h b/audio/sysvad/EndpointsCommon/minwavert.h new file mode 100644 index 00000000..c30faa2d --- /dev/null +++ b/audio/sysvad/EndpointsCommon/minwavert.h @@ -0,0 +1,665 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minwavert.h + +Abstract: + + Definition of wavert miniport class. + +--*/ + +#ifndef _SYSVAD_MINWAVERT_H_ +#define _SYSVAD_MINWAVERT_H_ + +#ifdef SYSVAD_BTH_BYPASS +#include "bthhfpmicwavtable.h" +#endif // SYSVAD_BTH_BYPASS + +//============================================================================= +// Referenced Forward +//============================================================================= +class CMiniportWaveRTStream; +typedef CMiniportWaveRTStream *PCMiniportWaveRTStream; + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CKeywordDetector +// +class CKeywordDetector +{ +public: + CKeywordDetector(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID ResetDetector(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID DownloadDetectorData(_In_ LONGLONG Data); + + _IRQL_requires_max_(PASSIVE_LEVEL) + LONGLONG GetDetectorData(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + NTSTATUS SetArmed(_In_ BOOL Arm); + + _IRQL_requires_max_(PASSIVE_LEVEL) + BOOL GetArmed(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID Run(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID Stop(); + + _IRQL_requires_min_(DISPATCH_LEVEL) + VOID DpcRoutine(_In_ LONGLONG PeformanceCounter, _In_ LONGLONG PerformanceFrequency); + + _IRQL_requires_max_(PASSIVE_LEVEL) + NTSTATUS GetReadPacket(_In_ ULONG PacketsPerWaveRtBuffer, _In_ ULONG WaveRtBufferSize, _Out_writes_(WaveRtBufferSize) BYTE *WaveRtBuffer, _Out_ ULONG *PacketNumber, _Out_ ULONGLONG *PerformanceCount, _Out_ BOOL *MoreData); + +private: + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID ResetFifo(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID StartBufferingStream(); + + // The Contoso keyword detector processes 10ms packets of 16KHz 16-bit PCM + // audio samples + static const int SamplesPerSecond = 16000; + static const int SamplesPerPacket = (10 * SamplesPerSecond / 1000); + + typedef struct + { + LIST_ENTRY ListEntry; + LONGLONG PacketNumber; + LONGLONG QpcWhenSampled; + UINT16 Samples[SamplesPerPacket]; + } PACKET_ENTRY; + + BOOL m_SoundDetectorArmed; + LONGLONG m_SoundDetectorData; + + LONGLONG m_qpcStartCapture; + LONGLONG m_nLastQueuedPacket; + + KSPIN_LOCK PacketPoolSpinLock; + LIST_ENTRY PacketPoolHead; + PACKET_ENTRY PacketPool[1 * SamplesPerSecond / SamplesPerPacket]; // Enough storage for 1 second of audio data + + KSPIN_LOCK PacketFifoSpinLock; + LIST_ENTRY PacketFifoHead; + +}; + +/////////////////////////////////////////////////////////////////////////////// +// CMiniportWaveRT +// +class CMiniportWaveRT : + public IMiniportWaveRT, + public IMiniportAudioEngineNode, + public IMiniportAudioSignalProcessing, + public CUnknown +{ +private: + ULONG m_ulLoopbackAllocated; + ULONG m_ulSystemAllocated; + ULONG m_ulOffloadAllocated; + DWORD m_dwCaptureAllocatedModes; + DWORD m_dwBiDiCaptureAllocatedModes; + DWORD m_dwSystemAllocatedModes; + + ULONG m_ulMaxSystemStreams; + ULONG m_ulMaxOffloadStreams; + ULONG m_ulMaxLoopbackStreams; + + // weak ref of running streams. + PCMiniportWaveRTStream * m_SystemStreams; + PCMiniportWaveRTStream * m_OffloadStreams; + PCMiniportWaveRTStream * m_LoopbackStreams; + + BOOL m_bGfxEnabled; + PBOOL m_pbMuted; + PLONG m_plVolumeLevel; + PLONG m_plPeakMeter; + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE m_pMixFormat; + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE m_pDeviceFormat; + PCFILTER_DESCRIPTOR m_FilterDesc; + PIN_DEVICE_FORMATS_AND_MODES * m_DeviceFormatsAndModes; + ULONG m_DeviceFormatsAndModesCount; + USHORT m_DeviceMaxChannels; + PDRMPORT m_pDrmPort; + DRMRIGHTS m_MixDrmRights; + ULONG m_ulMixDrmContentId; + CONSTRICTOR_OPTION m_LoopbackProtection; + + CKeywordDetector m_KeywordDetector; + + union { + PVOID m_DeviceContext; +#ifdef SYSVAD_BTH_BYPASS + PBTHHFPDEVICECOMMON m_BthHfpDevice; +#endif // SYSVAD_BTH_BYPASS + }; + +protected: + PADAPTERCOMMON m_pAdapterCommon; + ULONG m_DeviceFlags; + eDeviceType m_DeviceType; + PPORTEVENTS m_pPortEvents; + PENDPOINT_MINIPAIR m_pMiniportPair; + +public: + DECLARE_PROPERTYHANDLER(Get_SoundDetectorSupportedPatterns); + DECLARE_PROPERTYHANDLER(Set_SoundDetectorPatterns); + DECLARE_PROPERTYHANDLER(Get_SoundDetectorArmed); + DECLARE_PROPERTYHANDLER(Set_SoundDetectorArmed); + DECLARE_PROPERTYHANDLER(Get_SoundDetectorMatchResult); + + NTSTATUS EventHandler_SoundDetectorMatchDetected + ( + _In_ PPCEVENT_REQUEST EventRequest + ); + + NTSTATUS ValidateStreamCreate + ( + _In_ ULONG _Pin, + _In_ BOOLEAN _Capture, + _In_ GUID _SignalProcessingMode + ); + + NTSTATUS StreamCreated + ( + _In_ ULONG _Pin, + _In_ PCMiniportWaveRTStream _Stream + ); + + NTSTATUS StreamClosed + ( + _In_ ULONG _Pin, + _In_ PCMiniportWaveRTStream _Stream + ); + + NTSTATUS IsFormatSupported + ( + _In_ ULONG _ulPin, + _In_ BOOLEAN _bCapture, + _In_ PKSDATAFORMAT _pDataFormat + ); + + static NTSTATUS GetAttributesFromAttributeList + ( + _In_ const KSMULTIPLE_ITEM *_pAttributes, + _In_ size_t _Size, + _Out_ GUID* _pSignalProcessingMode + ); + +protected: + NTSTATUS UpdateDrmRights + ( + void + ); + + NTSTATUS SetLoopbackProtection + ( + _In_ CONSTRICTOR_OPTION ulProtectionOption + ); + +public: + DECLARE_STD_UNKNOWN(); + +#pragma code_seg("PAGE") + CMiniportWaveRT( + _In_ PUNKNOWN UnknownAdapter, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext + ) + :CUnknown(0), + m_ulMaxSystemStreams(0), + m_ulMaxOffloadStreams(0), + m_ulMaxLoopbackStreams(0), + m_DeviceType(MiniportPair->DeviceType), + m_DeviceContext(DeviceContext), + m_DeviceMaxChannels(MiniportPair->DeviceMaxChannels), + m_DeviceFormatsAndModes(MiniportPair->PinDeviceFormatsAndModes), + m_DeviceFormatsAndModesCount(MiniportPair->PinDeviceFormatsAndModesCount), + m_DeviceFlags(MiniportPair->DeviceFlags), + m_pMiniportPair(MiniportPair) + { + PAGED_CODE(); + + m_pAdapterCommon = (PADAPTERCOMMON)UnknownAdapter; // weak ref. + + if (MiniportPair->WaveDescriptor) + { + RtlCopyMemory(&m_FilterDesc, MiniportPair->WaveDescriptor, sizeof(m_FilterDesc)); + + // + // Get the max # of pin instances. + // + if (IsRenderDevice()) + { + if (IsOffloadSupported()) + { + if (m_FilterDesc.PinCount > KSPIN_WAVE_RENDER_SOURCE) + { + m_ulMaxSystemStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER_SINK_SYSTEM].MaxFilterInstanceCount; + m_ulMaxOffloadStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER_SINK_OFFLOAD].MaxFilterInstanceCount; + m_ulMaxLoopbackStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER_SINK_LOOPBACK].MaxFilterInstanceCount; + } + } + else + { + if (m_FilterDesc.PinCount > KSPIN_WAVE_RENDER2_SOURCE) + { + m_ulMaxSystemStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER2_SINK_SYSTEM].MaxFilterInstanceCount; + m_ulMaxLoopbackStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER2_SINK_LOOPBACK].MaxFilterInstanceCount; + } + } + } + } + +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + if (m_BthHfpDevice != NULL) + { + // This ref is released on dtor. + m_BthHfpDevice->AddRef(); // strong ref. + } + } +#endif // SYSVAD_BTH_BYPASS + } + +#pragma code_seg() + + ~CMiniportWaveRT(); + + IMP_IMiniportWaveRT; + IMP_IMiniportAudioEngineNode; + IMP_IMiniportAudioSignalProcessing; + + // Friends + friend class CMiniportWaveRTStream; + friend class CMiniportTopologySimple; + + friend NTSTATUS PropertyHandler_WaveFilter + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +public: + VOID DpcRoutine(LONGLONG PeformanceCounter, LONGLONG PerformanceFrequency) + { + m_KeywordDetector.DpcRoutine(PeformanceCounter, PerformanceFrequency); + } + + NTSTATUS PropertyHandlerEffectListRequest + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerProposedFormat + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerProposedFormat2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + PADAPTERCOMMON GetAdapterCommObj() + { + return m_pAdapterCommon; + }; +#pragma code_seg() + +#ifdef SYSVAD_BTH_BYPASS + NTSTATUS PropertyHandler_BthHfpAudioEffectsDiscoveryEffectsList + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); +#endif // SYSVAD_BTH_BYPASS + + //--------------------------------------------------------------------------------------------------------- + // volume + //--------------------------------------------------------------------------------------------------------- + NTSTATUS GetVolumeChannelCount + ( + _Out_ UINT32 * pulChannelCount + ); + + NTSTATUS GetVolumeSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelVolume + ( + _In_ UINT32 _uiChannel, + _Out_ LONG * _pVolume + ); + + NTSTATUS SetChannelVolume + ( + _In_ UINT32 _uiChannel, + _In_ LONG _Volume + ); + + //----------------------------------------------------------------------------- + // metering + //----------------------------------------------------------------------------- + NTSTATUS GetPeakMeterChannelCount + ( + _Out_ UINT32 * pulChannelCount + ); + + NTSTATUS GetPeakMeterSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelPeakMeter + ( + _In_ UINT32 _uiChannel, + _Out_ LONG * _plPeakMeter + ); + + //----------------------------------------------------------------------------- + // mute + //----------------------------------------------------------------------------- + NTSTATUS GetMuteChannelCount + ( + _Out_ UINT32 * pulChannelCount + ); + + NTSTATUS GetMuteSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelMute + ( + _In_ UINT32 _uiChannel, + _Out_ BOOL * _pbMute + ); + + NTSTATUS SetChannelMute + ( + _In_ UINT32 _uiChannel, + _In_ BOOL _bMute + ); + +private: +#pragma code_seg("PAGE") + //--------------------------------------------------------------------------- + // GetPinSupportedDeviceFormats + // + // Return supported formats for a given pin. + // + // Return value + // The number of KSDATAFORMAT_WAVEFORMATEXTENSIBLE items. + // + // Remarks + // Supported formats index array follows same order as filter's pin + // descriptor list. + // + _Post_satisfies_(return > 0) + ULONG GetPinSupportedDeviceFormats(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) KSDATAFORMAT_WAVEFORMATEXTENSIBLE **ppFormats) + { + PAGED_CODE(); + + ASSERT(m_DeviceFormatsAndModesCount > PinId); + ASSERT(m_DeviceFormatsAndModes[PinId].WaveFormats != NULL); + ASSERT(m_DeviceFormatsAndModes[PinId].WaveFormatsCount > 0); + + if (ppFormats != NULL) + { + *ppFormats = m_DeviceFormatsAndModes[PinId].WaveFormats; + } + + return m_DeviceFormatsAndModes[PinId].WaveFormatsCount; + } + + //--------------------------------------------------------------------------- + // GetAudioEngineSupportedDeviceFormats + // + // Return supported device formats for the audio engine node. + // + // Return value + // The number of KSDATAFORMAT_WAVEFORMATEXTENSIBLE items. + // + // Remarks + // Supported formats index array follows same order as filter's pin + // descriptor list. This routine assumes the engine formats are the + // last item in the filter's array of PIN_DEVICE_FORMATS_AND_MODES. + // + _Post_satisfies_(return > 0) + ULONG GetAudioEngineSupportedDeviceFormats(_Outptr_opt_result_buffer_(return) KSDATAFORMAT_WAVEFORMATEXTENSIBLE **ppFormats) + { + ULONG i; + + PAGED_CODE(); + + // By convention, the audio engine node's device formats are the last + // entry in the PIN_DEVICE_FORMATS_AND_MODES list. + + // Since this endpoint apparently supports offload, there must be at least a system, + // offload, and loopback pin, plus the entry for the device formats. + ASSERT(m_DeviceFormatsAndModesCount > 3); + + i = m_DeviceFormatsAndModesCount - 1; // Index of last list entry + + ASSERT(m_DeviceFormatsAndModes[i].PinType == NoPin); + ASSERT(m_DeviceFormatsAndModes[i].WaveFormats != NULL); + ASSERT(m_DeviceFormatsAndModes[i].WaveFormatsCount > 0); + + if (ppFormats != NULL) + { + *ppFormats = m_DeviceFormatsAndModes[i].WaveFormats; + } + + return m_DeviceFormatsAndModes[i].WaveFormatsCount; + } + + //--------------------------------------------------------------------------- + // GetPinSupportedDeviceModes + // + // Return mode information for a given pin. + // + // Return value + // The number of MODE_AND_DEFAULT_FORMAT items or 0 if none. + // + // Remarks + // Supported formats index array follows same order as filter's pin + // descriptor list. + // + _Success_(return != 0) + ULONG GetPinSupportedDeviceModes(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) _On_failure_(_Deref_post_null_) MODE_AND_DEFAULT_FORMAT **ppModes) + { + PMODE_AND_DEFAULT_FORMAT modes; + ULONG numModes; + + PAGED_CODE(); + + ASSERT(m_DeviceFormatsAndModesCount > PinId); + ASSERT((m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormatCount == 0) == (m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormat == NULL)); + + modes = m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormat; + numModes = m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormatCount; + +#ifdef SYSVAD_BTH_BYPASS + // Special handling for the SCO bypass endpoint, whose modes are determined at runtime + if (m_DeviceType == eBthHfpMicDevice) + { + ASSERT(m_BthHfpDevice != NULL); + if (m_BthHfpDevice->IsNRECSupported()) + { + modes = BthHfpMicPinSupportedDeviceModesNrec; + numModes = ARRAYSIZE(BthHfpMicPinSupportedDeviceModesNrec); + } + else + { + modes = BthHfpMicPinSupportedDeviceModesNoNrec; + numModes = ARRAYSIZE(BthHfpMicPinSupportedDeviceModesNoNrec); + } + } +#endif // SYSVAD_BTH_BYPASS + + if (ppModes != NULL) + { + if (numModes > 0) + { + *ppModes = modes; + } + else + { + // ensure that the returned pointer is NULL + // in the event of failure (SAL annotation above + // indicates that it must be NULL, and OACR sees a possibility + // that it might not be). + *ppModes = NULL; + } + } + + return numModes; + } +#pragma code_seg() + +protected: +#pragma code_seg("PAGE") + BOOL IsRenderDevice() + { + PAGED_CODE(); + return (m_DeviceType == eSpeakerDevice || + m_DeviceType == eSpeakerHpDevice || + m_DeviceType == eSpeakerHsDevice || + m_DeviceType == eHdmiRenderDevice || + m_DeviceType == eSpdifRenderDevice || + m_DeviceType == eBthHfpSpeakerDevice || + m_DeviceType == eHandsetSpeakerDevice) ? TRUE : FALSE; + } + + BOOL IsCellularDevice() + { + PAGED_CODE(); + return (m_DeviceType == eCellularDevice) ? TRUE : FALSE; + } + + BOOL IsOffloadSupported() + { + PAGED_CODE(); + return (m_DeviceFlags & ENDPOINT_OFFLOAD_SUPPORTED) ? TRUE : FALSE; + } + + BOOL IsSystemCapturePin(ULONG nPinId) + { + PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType; + PAGED_CODE(); + return (pinType == SystemCapturePin); + } + + BOOL IsCellularBiDiCapturePin(ULONG nPinId) + { + PAGED_CODE(); + return (m_DeviceFormatsAndModes[nPinId].PinType == TelephonyBidiPin); + } + + BOOL IsSystemRenderPin(ULONG nPinId) + { + PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType; + PAGED_CODE(); + return (pinType == SystemRenderPin); + } + + BOOL IsLoopbackPin(ULONG nPinId) + { + PAGED_CODE(); + return (m_DeviceFormatsAndModes[nPinId].PinType == RenderLoopbackPin); + } + + BOOL IsOffloadPin(ULONG nPinId) + { + PAGED_CODE(); + return (m_DeviceFormatsAndModes[nPinId].PinType == OffloadRenderPin); + } + + BOOL IsBridgePin(ULONG nPinId) + { + PAGED_CODE(); + return (m_DeviceFormatsAndModes[nPinId].PinType == BridgePin); + } + + // These three pins are the pins used by the audio engine for host, loopback, and offload. + ULONG GetSystemPinId() + { + PAGED_CODE(); + ASSERT(IsRenderDevice()); + ASSERT(!IsCellularDevice()); + return IsOffloadSupported() ? KSPIN_WAVE_RENDER_SINK_SYSTEM : KSPIN_WAVE_RENDER2_SINK_SYSTEM; + } + + + ULONG GetLoopbackPinId() + { + PAGED_CODE(); + ASSERT(IsRenderDevice()); + ASSERT(!IsCellularDevice()); + return IsOffloadSupported() ? KSPIN_WAVE_RENDER_SINK_LOOPBACK : KSPIN_WAVE_RENDER2_SINK_LOOPBACK; + } + + + ULONG GetOffloadPinId() + { + PAGED_CODE(); + ASSERT(IsRenderDevice()); + ASSERT(IsOffloadSupported()); + ASSERT(!IsCellularDevice()); + return KSPIN_WAVE_RENDER_SINK_OFFLOAD; + } +#pragma code_seg() + +#ifdef SYSVAD_BTH_BYPASS +public: +#pragma code_seg("PAGE") + BOOL IsBthHfpDevice() + { + PAGED_CODE(); + return (m_DeviceType == eBthHfpMicDevice || + m_DeviceType == eBthHfpSpeakerDevice) ? TRUE : FALSE; + } + + // Returns a weak ref to the Bluetooth HFP device. + PBTHHFPDEVICECOMMON GetBthHfpDevice() + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + PAGED_CODE(); + + if (IsBthHfpDevice()) + { + if (m_BthHfpDevice != NULL) + { + bthHfpDevice = m_BthHfpDevice; + } + } + + return bthHfpDevice; + } +#pragma code_seg() +#endif // SYSVAD_BTH_BYPASS +}; +typedef CMiniportWaveRT *PCMiniportWaveRT; + +#endif // _SYSVAD_MINWAVERT_H_ + diff --git a/audio/sysvad/EndpointsCommon/minwavertstream.cpp b/audio/sysvad/EndpointsCommon/minwavertstream.cpp new file mode 100644 index 00000000..a343efa7 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/minwavertstream.cpp @@ -0,0 +1,1553 @@ +#include <sysvad.h> +#include <limits.h> +#include <ks.h> +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "UnittestData.h" +#define MINWAVERTSTREAM_POOLTAG 'SRWM' + +#pragma warning (disable : 4127) + +//============================================================================= +// CMiniportWaveRTStream +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportWaveRTStream::~CMiniportWaveRTStream +( + void +) +/*++ + +Routine Description: + + Destructor for wavertstream + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + if (NULL != m_pMiniport) + { + if (m_bUnregisterStream) + { + m_pMiniport->StreamClosed(m_ulPin, this); + m_bUnregisterStream = FALSE; + } + + m_pMiniport->Release(); + m_pMiniport = NULL; + } + + if (m_pDpc) + { + ExFreePoolWithTag( m_pDpc, MINWAVERTSTREAM_POOLTAG ); + m_pDpc = NULL; + } + + if (m_pTimer) + { + ExFreePoolWithTag( m_pTimer, MINWAVERTSTREAM_POOLTAG ); + m_pTimer = NULL; + } + + if (m_pbMuted) + { + ExFreePoolWithTag( m_pbMuted, MINWAVERTSTREAM_POOLTAG ); + m_pbMuted = NULL; + } + + if (m_plVolumeLevel) + { + ExFreePoolWithTag( m_plVolumeLevel, MINWAVERTSTREAM_POOLTAG ); + m_plVolumeLevel = NULL; + } + + if (m_plPeakMeter) + { + ExFreePoolWithTag( m_plPeakMeter, MINWAVERTSTREAM_POOLTAG ); + m_plPeakMeter = NULL; + } + + if (m_pWfExt) + { + ExFreePoolWithTag( m_pWfExt, MINWAVERTSTREAM_POOLTAG ); + m_pWfExt = NULL; + } + if (m_pNotificationTimer) + { + KeCancelTimer(m_pNotificationTimer); + ExFreePoolWithTag(m_pNotificationTimer, MINWAVERTSTREAM_POOLTAG); + } + + // Since we just cancelled the notification timer, wait for all queued + // DPCs to complete before we free the notification DPC. + // + KeFlushQueuedDpcs(); + + if (m_pNotificationDpc) + { + ExFreePoolWithTag( m_pNotificationDpc, MINWAVERTSTREAM_POOLTAG ); + } + +#ifdef SYSVAD_BTH_BYPASS + ASSERT(m_ScoOpen == FALSE); +#endif // SYSVAD_BTH_BYPASS + + DPF_ENTER(("[CMiniportWaveRTStream::~CMiniportWaveRTStream]")); +} // ~CMiniportWaveRTStream + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRTStream::Init +( + _In_ PCMiniportWaveRT Miniport_, + _In_ PPORTWAVERTSTREAM PortStream_, + _In_ ULONG Pin_, + _In_ BOOLEAN Capture_, + _In_ PKSDATAFORMAT DataFormat_, + _In_ GUID SignalProcessingMode +) +/*++ + +Routine Description: + + Initializes the stream object. + +Arguments: + + Miniport_ - + + Pin_ - + + Capture_ - + + DataFormat - + + SignalProcessingMode - The driver uses the signalProcessingMode to configure + driver and/or hardware specific signal processing to be applied to this new + stream. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + PWAVEFORMATEX pWfEx = NULL; + NTSTATUS ntStatus = STATUS_SUCCESS; + + m_pMiniport = NULL; + m_ulPin = 0; + m_bUnregisterStream = FALSE; + m_bCapture = FALSE; + m_ulDmaBufferSize = 0; + m_pDmaBuffer = NULL; + m_ulNotificationsPerBuffer = 0; + m_KsState = KSSTATE_STOP; + m_pTimer = NULL; + m_pDpc = NULL; + m_llPacketCounter = 0; + m_ullPlayPosition = 0; + m_ullWritePosition = 0; + m_ullDmaTimeStamp = 0; + m_hnsElapsedTimeCarryForward = 0; + m_ulDmaMovementRate = 0; + m_bLfxEnabled = FALSE; + m_pbMuted = NULL; + m_plVolumeLevel = NULL; + m_plPeakMeter = NULL; + m_pWfExt = NULL; + m_ullLinearPosition = 0; + m_ulContentId = 0; + m_ulCurrentWritePosition = 0; + m_ulLastOsReadPacket = ULONG_MAX; + m_ulLastOsWritePacket = ULONG_MAX; + m_llEoSPosition = (-1); + m_IsCurrentWritePositionUpdated = 0; + m_SignalProcessingMode = SignalProcessingMode; + +#ifdef SYSVAD_BTH_BYPASS + m_ScoOpen = FALSE; +#endif // SYSVAD_BTH_BYPASS + + m_pPortStream = PortStream_; + InitializeListHead(&m_NotificationList); + m_ulNotificationIntervalMs = 0; + + m_pNotificationDpc = (PRKDPC)ExAllocatePoolWithTag( + NonPagedPoolNx, + sizeof(KDPC), + MINWAVERTSTREAM_POOLTAG); + if (!m_pNotificationDpc) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + m_pNotificationTimer = (PKTIMER)ExAllocatePoolWithTag( + NonPagedPoolNx, + sizeof(KTIMER), + MINWAVERTSTREAM_POOLTAG); + if (!m_pNotificationTimer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + KeInitializeDpc(m_pNotificationDpc, TimerNotifyRT, this); + KeInitializeTimerEx(m_pNotificationTimer, NotificationTimer); + + pWfEx = GetWaveFormatEx(DataFormat_); + if (NULL == pWfEx) + { + return STATUS_UNSUCCESSFUL; + } + + m_pMiniport = reinterpret_cast<CMiniportWaveRT*>(Miniport_); + if (m_pMiniport == NULL) + { + return STATUS_INVALID_PARAMETER; + } + m_pMiniport->AddRef(); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + m_ulPin = Pin_; + m_bCapture = Capture_; + m_ulDmaMovementRate = pWfEx->nAvgBytesPerSec; + + m_pDpc = (PRKDPC)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KDPC), MINWAVERTSTREAM_POOLTAG); + if (!m_pDpc) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + m_pWfExt = (PWAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(WAVEFORMATEX) + pWfEx->cbSize, MINWAVERTSTREAM_POOLTAG); + if (m_pWfExt == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory(m_pWfExt, pWfEx, sizeof(WAVEFORMATEX) + pWfEx->cbSize); + + m_pbMuted = (PBOOL)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(BOOL), MINWAVERTSTREAM_POOLTAG); + if (m_pbMuted == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_pbMuted, m_pWfExt->Format.nChannels * sizeof(BOOL)); + + m_plVolumeLevel = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); + if (m_plVolumeLevel == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_plVolumeLevel, m_pWfExt->Format.nChannels * sizeof(LONG)); + + m_plPeakMeter = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); + if (m_plPeakMeter == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_plPeakMeter, m_pWfExt->Format.nChannels * sizeof(LONG)); + + if (m_bCapture) + { + DWORD toneFrequency = 0; + + if (m_pMiniport->IsLoopbackPin(Pin_)) + { + // + // Loopbacks pins use a different frequency for test validation. + // + toneFrequency = 3000; // 3 kHz + } + else + { + // + // Init sine wave generator. To exercise the SignalProcessingMode parameter + // this sample driver selects the frequency based on the parameter. + // + toneFrequency = IsEqualGUID(SignalProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) ? 1000 : 2000; + } + + ntStatus = m_ToneGenerator.Init(toneFrequency, m_pWfExt); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + else if (!g_DoNotCreateDataFiles) + { + // + // Create an output file for the render data. + // + DPF(D_TERSE, ("SaveData %p", &m_SaveData)); + ntStatus = m_SaveData.SetDataFormat(DataFormat_); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = m_SaveData.Initialize(m_pMiniport->IsOffloadPin(Pin_)); + } + + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + + // + // Register this stream. + // + ntStatus = m_pMiniport->StreamCreated(m_ulPin, this); + if (NT_SUCCESS(ntStatus)) + { + m_bUnregisterStream = TRUE; + } + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRTStream::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface + +Arguments: + + Interface - GUID + + Object - interface pointer to be returned + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PMINIPORTWAVERTSTREAM(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTStream)) + { + *Object = PVOID(PMINIPORTWAVERTSTREAM(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTStreamNotification)) + { + *Object = PVOID(PMINIPORTWAVERTSTREAMNOTIFICATION(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTInputStream) && (this->m_bCapture)) + { + // This interface is supported only on capture streams + *Object = PVOID(PMINIPORTWAVERTINPUTSTREAM(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTOutputStream) && (!this->m_bCapture)) + { + // This interface is supported only on render streams + *Object = PVOID(PMINIPORTWAVERTOUTPUTSTREAM(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportStreamAudioEngineNode)) + { + *Object = (PVOID)(IMiniportStreamAudioEngineNode*)this; + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportStreamAudioEngineNode2)) + { + *Object = (PVOID)(IMiniportStreamAudioEngineNode2*)this; + } + else if (IsEqualGUIDAligned(Interface, IID_IDrmAudioStream)) + { + *Object = (PVOID)(IDrmAudioStream*)this; + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::AllocateBufferWithNotification +( + _In_ ULONG NotificationCount_, + _In_ ULONG RequestedSize_, + _Out_ PMDL *AudioBufferMdl_, + _Out_ ULONG *ActualSize_, + _Out_ ULONG *OffsetFromFirstPage_, + _Out_ MEMORY_CACHING_TYPE *CacheType_ +) +{ + PAGED_CODE(); + + ULONG ulBufferDurationMs = 0; + + if ( (0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign) ) + { + return STATUS_UNSUCCESSFUL; + } + + if ((NotificationCount_ == 0) || (RequestedSize_ % NotificationCount_ != 0)) + { + return STATUS_INVALID_PARAMETER; + } + + RequestedSize_ -= RequestedSize_ % (m_pWfExt->Format.nBlockAlign); + + if (!m_bCapture && !g_DoNotCreateDataFiles) + { + NTSTATUS ntStatus; + + ntStatus = m_SaveData.SetMaxWriteSize(RequestedSize_); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + + PHYSICAL_ADDRESS highAddress; + highAddress.HighPart = 0; + highAddress.LowPart = MAXULONG; + + PMDL pBufferMdl = m_pPortStream->AllocatePagesForMdl (highAddress, RequestedSize_); + + if (NULL == pBufferMdl) + { + return STATUS_UNSUCCESSFUL; + } + + // From MSDN: + // "Since the Windows audio stack does not support a mechanism to express memory access + // alignment requirements for buffers, audio drivers must select a caching type for mapped + // memory buffers that does not impose platform-specific alignment requirements. In other + // words, the caching type used by the audio driver for mapped memory buffers, must not make + // assumptions about the memory alignment requirements for any specific platform. + // + // This method maps the physical memory pages in the MDL into kernel-mode virtual memory. + // Typically, the miniport driver calls this method if it requires software access to the + // scatter-gather list for an audio buffer. In this case, the storage for the scatter-gather + // list must have been allocated by the IPortWaveRTStream::AllocatePagesForMdl or + // IPortWaveRTStream::AllocateContiguousPagesForMdl method. + // + // A WaveRT miniport driver should not require software access to the audio buffer itself." + // + m_pDmaBuffer = (BYTE*)m_pPortStream->MapAllocatedPages(pBufferMdl, MmCached); + m_ulNotificationsPerBuffer = NotificationCount_; + m_ulDmaBufferSize = RequestedSize_; + ulBufferDurationMs = (RequestedSize_ * 1000) / m_ulDmaMovementRate; + m_ulNotificationIntervalMs = ulBufferDurationMs / NotificationCount_; + + *AudioBufferMdl_ = pBufferMdl; + *ActualSize_ = RequestedSize_; + *OffsetFromFirstPage_ = 0; + *CacheType_ = MmCached; + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID CMiniportWaveRTStream::FreeBufferWithNotification +( + _In_ PMDL Mdl_, + _In_ ULONG Size_ +) +{ + UNREFERENCED_PARAMETER(Size_); + + PAGED_CODE(); + + if (Mdl_ != NULL) + { + if (m_pDmaBuffer != NULL) + { + m_pPortStream->UnmapAllocatedPages(m_pDmaBuffer, Mdl_); + m_pDmaBuffer = NULL; + } + + m_pPortStream->FreePagesFromMdl(Mdl_); + } + + m_ulDmaBufferSize = 0; + m_ulNotificationsPerBuffer = 0; + + return; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::RegisterNotificationEvent +( + _In_ PKEVENT NotificationEvent_ +) +{ + UNREFERENCED_PARAMETER(NotificationEvent_); + + PAGED_CODE(); + + NotificationListEntry *nleNew = (NotificationListEntry*)ExAllocatePoolWithTag( + NonPagedPoolNx, + sizeof(NotificationListEntry), + MINWAVERTSTREAM_POOLTAG); + if (NULL == nleNew) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + nleNew->NotificationEvent = NotificationEvent_; + + if (!IsListEmpty(&m_NotificationList)) + { + PLIST_ENTRY leCurrent = m_NotificationList.Flink; + while (leCurrent != &m_NotificationList) + { + NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry); + if (nleCurrent->NotificationEvent == NotificationEvent_) + { + RemoveEntryList( leCurrent ); + ExFreePoolWithTag( nleNew, MINWAVERTSTREAM_POOLTAG ); + return STATUS_UNSUCCESSFUL; + } + + leCurrent = leCurrent->Flink; + } + } + + InsertTailList(&m_NotificationList, &(nleNew->ListEntry)); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::UnregisterNotificationEvent +( + _In_ PKEVENT NotificationEvent_ +) +{ + UNREFERENCED_PARAMETER(NotificationEvent_); + + PAGED_CODE(); + + if (!IsListEmpty(&m_NotificationList)) + { + PLIST_ENTRY leCurrent = m_NotificationList.Flink; + while (leCurrent != &m_NotificationList) + { + NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry); + if (nleCurrent->NotificationEvent == NotificationEvent_) + { + RemoveEntryList( leCurrent ); + ExFreePoolWithTag( nleCurrent, MINWAVERTSTREAM_POOLTAG ); + return STATUS_SUCCESS; + } + + leCurrent = leCurrent->Flink; + } + } + + return STATUS_NOT_FOUND; +} + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetClockRegister +( + _Out_ PKSRTAUDIO_HWREGISTER Register_ +) +{ + UNREFERENCED_PARAMETER(Register_); + + PAGED_CODE(); + + return STATUS_NOT_IMPLEMENTED; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetPositionRegister +( + _Out_ PKSRTAUDIO_HWREGISTER Register_ +) +{ + UNREFERENCED_PARAMETER(Register_); + + PAGED_CODE(); + + return STATUS_NOT_IMPLEMENTED; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID CMiniportWaveRTStream::GetHWLatency +( + _Out_ PKSRTAUDIO_HWLATENCY Latency_ +) +{ + PAGED_CODE(); + + ASSERT(Latency_); + + Latency_->ChipsetDelay = 0; + Latency_->CodecDelay = 0; + Latency_->FifoSize = 0; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID CMiniportWaveRTStream::FreeAudioBuffer +( +_In_opt_ PMDL Mdl_, +_In_ ULONG Size_ +) +{ + UNREFERENCED_PARAMETER(Size_); + + PAGED_CODE(); + + if (Mdl_ != NULL) + { + if (m_pDmaBuffer != NULL) + { + m_pPortStream->UnmapAllocatedPages(m_pDmaBuffer, Mdl_); + m_pDmaBuffer = NULL; + } + + m_pPortStream->FreePagesFromMdl(Mdl_); + } + + m_ulDmaBufferSize = 0; + m_ulNotificationsPerBuffer = 0; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::AllocateAudioBuffer +( +_In_ ULONG RequestedSize_, +_Out_ PMDL *AudioBufferMdl_, +_Out_ ULONG *ActualSize_, +_Out_ ULONG *OffsetFromFirstPage_, +_Out_ MEMORY_CACHING_TYPE *CacheType_ +) +{ + PAGED_CODE(); + + if ((0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign)) + { + return STATUS_UNSUCCESSFUL; + } + + RequestedSize_ -= RequestedSize_ % (m_pWfExt->Format.nBlockAlign); + + PHYSICAL_ADDRESS highAddress; + highAddress.HighPart = 0; + highAddress.LowPart = MAXULONG; + + PMDL pBufferMdl = m_pPortStream->AllocatePagesForMdl(highAddress, RequestedSize_); + + if (NULL == pBufferMdl) + { + return STATUS_UNSUCCESSFUL; + } + + // From MSDN: + // "Since the Windows audio stack does not support a mechanism to express memory access + // alignment requirements for buffers, audio drivers must select a caching type for mapped + // memory buffers that does not impose platform-specific alignment requirements. In other + // words, the caching type used by the audio driver for mapped memory buffers, must not make + // assumptions about the memory alignment requirements for any specific platform. + // + // This method maps the physical memory pages in the MDL into kernel-mode virtual memory. + // Typically, the miniport driver calls this method if it requires software access to the + // scatter-gather list for an audio buffer. In this case, the storage for the scatter-gather + // list must have been allocated by the IPortWaveRTStream::AllocatePagesForMdl or + // IPortWaveRTStream::AllocateContiguousPagesForMdl method. + // + // A WaveRT miniport driver should not require software access to the audio buffer itself." + // + m_pDmaBuffer = (BYTE*)m_pPortStream->MapAllocatedPages(pBufferMdl, MmCached); + + m_ulDmaBufferSize = RequestedSize_; + m_ulNotificationsPerBuffer = 0; + + *AudioBufferMdl_ = pBufferMdl; + *ActualSize_ = RequestedSize_; + *OffsetFromFirstPage_ = 0; + *CacheType_ = MmCached; + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS CMiniportWaveRTStream::GetPosition +( + _Out_ KSAUDIO_POSITION *Position_ +) +{ + NTSTATUS ntStatus; + +#ifdef SYSVAD_BTH_BYPASS + if (m_ScoOpen) + { + ntStatus = GetScoStreamNtStatus(); + IF_FAILED_JUMP(ntStatus, Done); + } +#endif // SYSVAD_BTH_BYPASS + + // Return failure if this is the keyword detector pin + if (m_pMiniport->m_DeviceFormatsAndModes[m_ulPin].PinType == PINTYPE::KeywordCapturePin) + { + return STATUS_NOT_SUPPORTED; + } + + if (m_KsState == KSSTATE_RUN) + { + // + // Get the current time and update position. + // + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + UpdatePosition(ilQPC); + } + + Position_->PlayOffset = m_ullPlayPosition; + Position_->WriteOffset = m_ullWritePosition; + + ntStatus = STATUS_SUCCESS; + +#ifdef SYSVAD_BTH_BYPASS +Done: +#endif // SYSVAD_BTH_BYPASS + return ntStatus; +} + +//============================================================================= +// CMiniportWaveRTStream::GetReadPacket +// +// Returns information about the next packet for the OS to read. +// +// Return value +// +// Returns STATUS_OPERATION_IN_PROGRESS if no new packets are available and +// the next packet is in progress. +// +// IRQL - PASSIVE_LEVEL +// +// Remarks +// Although called at passive level, this routine is non-paged code because +// it is called in the streaming path where page faults should be avoided. +// +// ISSUE-2014/10/4 Will this work correctly across pause/play? +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::GetReadPacket +( + _Out_ ULONG *PacketNumber, + _Out_ DWORD *Flags, + _Out_ ULONG64 *PerformanceCounterValue, + _Out_ BOOL *MoreData +) +{ + NTSTATUS ntStatus; + ULONG availablePacketNumber; + ULONG droppedPackets; + + // The call must be from event driven mode + if(m_ulNotificationsPerBuffer == 0) + { + return STATUS_NOT_SUPPORTED; + } + + *Flags = 0; + + if (m_KsState < KSSTATE_PAUSE) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // If this is the keyword detector pin, then stream from the keyword FIFO + if (m_pMiniport->m_DeviceFormatsAndModes[m_ulPin].PinType == PINTYPE::KeywordCapturePin) + { + // FUTURE-2014/11/18 Drive this with packet counter + ntStatus = m_pMiniport->m_KeywordDetector.GetReadPacket(m_ulNotificationsPerBuffer, m_ulDmaBufferSize, m_pDmaBuffer, PacketNumber, PerformanceCounterValue, MoreData); + if (NT_SUCCESS(ntStatus)) + { + m_ulLastOsReadPacket = *PacketNumber; + } + return ntStatus; + } + + if (m_KsState == KSSTATE_RUN) + { + // + // Get the current time and update simulated position. + // + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + + UpdatePosition(ilQPC); + } + + // The 0-based number of the last completed packet + // FUTURE-2014/10/27 Update to allow different numbers of packets per WaveRT buffer + availablePacketNumber = LODWORD(m_llPacketCounter - 1); // Note this might be ULONG_MAX if called during the first packet + + // If no new packets are available... + if (availablePacketNumber == m_ulLastOsReadPacket) + { + return STATUS_DEVICE_NOT_READY; + } + + // If more than one packet has transferred since the last packet read by + // the OS, then those were dropped. That is, a glitch occurred. + droppedPackets = availablePacketNumber - m_ulLastOsReadPacket - 1; + if (droppedPackets > 0) + { + // Trace a glitch + } + + // Return next packet number to be read + *PacketNumber = availablePacketNumber; + + // Compute and return timestamp corresponding to start of the available packet. In a real hardware + // driver, the timestamp would be computed in a driver and hardware specific manner. In this sample + // driver, it is extrapolated from the sample driver's internal simulated position corrleation + // [m_ullLinearPosition @ m_ullDmaTimeStamp] and the sample's internal 64-bit packet counter, subtracting + // 1 from the packet counter to compute the time at the start of that last completed packet. + ULONGLONG linearPositionOfAvailablePacket = (m_llPacketCounter - 1) * (m_ulDmaBufferSize / m_ulNotificationsPerBuffer); + ULONGLONG deltaLinearPosition = m_ullLinearPosition - linearPositionOfAvailablePacket; + ULONGLONG deltaTimeInHns = deltaLinearPosition * 10000000 / m_ulDmaMovementRate; + ULONGLONG timeOfAvailablePacketInHns = m_ullDmaTimeStamp - deltaTimeInHns; + ULONGLONG timeOfAvailablePacketInQpc = timeOfAvailablePacketInHns * m_ullPerformanceCounterFrequency.QuadPart / 10000000; + + *PerformanceCounterValue = timeOfAvailablePacketInQpc; + + // No flags are defined yet + *Flags = 0; + + // This sample does not internally buffer data so there is never more data + // than revealed by the results from this routine. + *MoreData = FALSE; + + // Update the last packet read by the OS + m_ulLastOsReadPacket = availablePacketNumber; + +#if 0 + // For test, embed packet number and timestamp into first two LONGLONGs of the packet + LONG packetIndex = availablePacketNumber % m_ulNotificationsPerBuffer; + SIZE_T packetSize = m_ulDmaBufferSize / m_ulNotificationsPerBuffer; + BYTE *packetDataAsBytes = m_pDmaBuffer + (packetIndex * packetSize); + LONGLONG *packetDataAsLonglongs = (LONGLONG*)packetDataAsBytes; + for (int i = 0; i < packetSize / sizeof(LONGLONG); i++) + { + packetDataAsLonglongs[i] = i; + } + packetDataAsLonglongs[0] = availablePacketNumber; + packetDataAsLonglongs[1] = timeOfAvailablePacketInQpc; +#endif + + return STATUS_SUCCESS; +} + +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::SetWritePacket +( +_In_ ULONG PacketNumber, +_In_ DWORD Flags, +_In_ ULONG EosPacketLength +) +{ + NTSTATUS ntStatus; + + // The call must be from event driven mode + if (m_ulNotificationsPerBuffer == 0) + { + return STATUS_NOT_SUPPORTED; + } + + ULONG oldLastOsWritePacket = m_ulLastOsWritePacket; + + // This function should not be called once EoS has been set. + if (m_llEoSPosition >= 0) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // Update linear positions + if (m_KsState == KSSTATE_RUN) + { + // Get the current time and update simulated position. + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + UpdatePosition(ilQPC); + } + + // 1-based count of completed packets, 0-based packet number of current packet + LONGLONG currentPacket = m_llPacketCounter; + + // If not running, the current packet hasn't actually started transfering so OS should be writing + // to the current packet. If running, then the current packing is already transfering to hardware + // so the OS should write the packet after the current packet. + ULONG expectedPacket = LODWORD(currentPacket); + if (m_KsState == KSSTATE_RUN) + { + expectedPacket++; + } + + // Check if OS PacketNumber is behind or too far ahead of current packet + LONG deltaFromExpectedPacket = PacketNumber - expectedPacket; // Modulo arithemetic + if (deltaFromExpectedPacket < 0) + { + return STATUS_DATA_LATE_ERROR; + } + else if (deltaFromExpectedPacket > 0) + { + return STATUS_DATA_OVERRUN; + } + + ULONG packetSize = (m_ulDmaBufferSize / m_ulNotificationsPerBuffer); + ULONG packetIndex = PacketNumber % m_ulNotificationsPerBuffer; + ULONG ulCurrentWritePosition = packetIndex * packetSize; + + // Check if EOS flag was passed + if (Flags & KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM) + { + if (EosPacketLength > packetSize) + { + return STATUS_INVALID_PARAMETER; + } + else { + // EOS position will be after the total completed packets, plus the packet in progress, + // plus this EOS packet length + m_ulLastOsWritePacket = PacketNumber; + m_llEoSPosition = ((m_llPacketCounter + 1) * packetSize) + EosPacketLength; + ntStatus = SetStreamCurrentWritePositionForLastBuffer(ulCurrentWritePosition); + } + } + else + { + m_ulLastOsWritePacket = PacketNumber; + + // This function sets the current write position to the specified byte in the DMA buffer. + // Will check if the write position is smaller than the DMA buffer size. + // Will not return an error when the passed in parameter is 0. + // Will also check if this function was called with the same write position(in event mode only) + // Underruning will also be checked via timer mechanism + ntStatus = SetCurrentWritePositionInternal(ulCurrentWritePosition); + } + + if (!NT_SUCCESS(ntStatus)) + { + m_llEoSPosition = (-1); + m_ulLastOsWritePacket = oldLastOsWritePacket; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::GetOutputStreamPresentationPosition +( + _Out_ KSAUDIO_PRESENTATION_POSITION *pPresentationPosition +) +{ + ASSERT (pPresentationPosition); + + // The call must be from event driven mode + if(m_ulNotificationsPerBuffer == 0) + { + return STATUS_NOT_SUPPORTED; + } + + return GetPresentationPosition(pPresentationPosition); +} + +//============================================================================= +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::GetPacketCount +( + _Out_ ULONG *pPacketCount +) +{ + ASSERT(pPacketCount); + + // The call must be from event driven mode + if(m_ulNotificationsPerBuffer == 0) + { + return STATUS_NOT_SUPPORTED; + } + + if (m_KsState == KSSTATE_RUN) + { + // Get the current time and update simulated position. + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + UpdatePosition(ilQPC); + } + + *pPacketCount = LODWORD(m_llPacketCounter); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::SetState +( + _In_ KSSTATE State_ +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj(); + + // Spew an event for a pin state change request from portcls + //Event type: eMINIPORT_PIN_STATE + //Parameter 1: Current linear buffer position + //Parameter 2: Current WaveRtBufferWritePosition + //Parameter 3: Pin State 0->KS_STOP, 1->KS_ACQUIRE, 2->KS_PAUSE, 3->KS_RUN + //Parameter 4:0 + pAdapterComm->WriteEtwEvent(eMINIPORT_PIN_STATE, + 100, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, // replace with the previous WaveRtBufferWritePosition that the drive received + State_, // repalce with the correct "Data length completed" + 0); // always zero + + switch (State_) + { + case KSSTATE_STOP: + // Reset DMA + m_llPacketCounter = 0; + m_ullPlayPosition = 0; + m_ullWritePosition = 0; + m_ullLinearPosition = 0; + + // Reset OS read/write positions + m_ulLastOsReadPacket = ULONG_MAX; + m_ulCurrentWritePosition = 0; + m_ulLastOsWritePacket = ULONG_MAX; + m_llEoSPosition = (-1); + + // Wait until all work items are completed. + if (!m_bCapture && !g_DoNotCreateDataFiles) + { + m_SaveData.WaitAllWorkItems(); + } + +#ifdef SYSVAD_BTH_BYPASS + if (m_ScoOpen) + { + PBTHHFPDEVICECOMMON bthHfpDevice; + + ASSERT(m_pMiniport->IsBthHfpDevice()); + bthHfpDevice = m_pMiniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + // + // Close the SCO connection. + // + ntStatus = bthHfpDevice->StreamClose(); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("SetState: KSSTATE_STOP, StreamClose failed, 0x%x", ntStatus)); + } + + m_ScoOpen = FALSE; + } +#endif // SYSVAD_BTH_BYPASS + break; + + case KSSTATE_ACQUIRE: +#ifdef SYSVAD_BTH_BYPASS + if (m_pMiniport->IsBthHfpDevice()) + { + if (m_ScoOpen == FALSE) + { + PBTHHFPDEVICECOMMON bthHfpDevice; + + bthHfpDevice = m_pMiniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + // + // Open the SCO connection. + // + ntStatus = bthHfpDevice->StreamOpen(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetState: KSSTATE_ACQUIRE, StreamOpen failed, 0x%x", ntStatus)), + Done); + + m_ScoOpen = TRUE; + } + } +#endif // SYSVAD_BTH_BYPASS + break; + + case KSSTATE_PAUSE: + ULONGLONG ullPositionTemp; + + if (m_KsState > KSSTATE_PAUSE) + { + // + // Run -> Pause + // + if (m_pMiniport->m_DeviceFormatsAndModes[m_ulPin].PinType == PINTYPE::KeywordCapturePin) + { + m_pMiniport->m_KeywordDetector.Stop(); + } + + // Pause DMA + if (m_ulNotificationIntervalMs > 0) + { + KeCancelTimer(m_pNotificationTimer); + ExSetTimerResolution(0, FALSE); + KeFlushQueuedDpcs(); + } + } + + // This call updates the linear buffer position. + GetLinearBufferPosition(&ullPositionTemp, NULL); + break; + + case KSSTATE_RUN: + // Start DMA + LARGE_INTEGER ullPerfCounterTemp; + if (m_pMiniport->m_DeviceFormatsAndModes[m_ulPin].PinType == PINTYPE::KeywordCapturePin) + { + m_pMiniport->m_KeywordDetector.Run(); + } + ullPerfCounterTemp = KeQueryPerformanceCounter(&m_ullPerformanceCounterFrequency); + m_ullDmaTimeStamp = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ullPerfCounterTemp); + m_hnsElapsedTimeCarryForward = 0; + + if (m_ulNotificationIntervalMs > 0) + { + LARGE_INTEGER delay; + + delay.QuadPart = (-2) * (((LONGLONG)m_ulNotificationIntervalMs) * HNSTIME_PER_MILLISECOND); + + // ISSUE-2014/10/20 Set resolution based on packet size + ExSetTimerResolution(HNSTIME_PER_MILLISECOND, TRUE); + + KeSetTimerEx + ( + m_pNotificationTimer, + delay, + m_ulNotificationIntervalMs, + m_pNotificationDpc + ); + } + + break; + } + + m_KsState = State_; + +#ifdef SYSVAD_BTH_BYPASS +Done: +#endif // SYSVAD_BTH_BYPASS + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::SetFormat +( + _In_ KSDATAFORMAT *DataFormat_ +) +{ + UNREFERENCED_PARAMETER(DataFormat_); + + PAGED_CODE(); + + //if (!m_fCapture && !g_DoNotCreateDataFiles) + //{ + // ntStatus = m_SaveData.SetDataFormat(Format); + //} + + return STATUS_NOT_SUPPORTED; +} + +#pragma code_seg() + +//============================================================================= +#pragma code_seg() +VOID CMiniportWaveRTStream::UpdatePosition +( + _In_ LARGE_INTEGER ilQPC +) +{ + // Convert ticks to 100ns units. + LONGLONG hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ilQPC); + + // Calculate the time elapsed since the last call to GetPosition() or since the + // DMA engine started. Note that the division by 10000 to convert to milliseconds + // may cause us to lose some of the time, so we will carry the remainder forward + // to the next GetPosition() call. + // + ULONG TimeElapsedInMS = (ULONG)(hnsCurrentTime - m_ullDmaTimeStamp + m_hnsElapsedTimeCarryForward)/10000; + + // Carry forward the remainder of this division so we don't fall behind with our position too much. + // + m_hnsElapsedTimeCarryForward = (hnsCurrentTime - m_ullDmaTimeStamp + m_hnsElapsedTimeCarryForward) % 10000; + + // Calculate how many bytes in the DMA buffer would have been processed in the elapsed + // time. Note that the division by 1000 to convert to milliseconds may cause us to + // lose some bytes, so we will carry the remainder forward to the next GetPosition() call. + // + // need to divide by 1000 because m_ulDmaMovementRate is average bytes per sec. + ULONG ByteDisplacement = (m_ulDmaMovementRate * TimeElapsedInMS) / 1000; + + if (m_bCapture) + { + // Write sine wave to buffer. + WriteBytes(ByteDisplacement); + } + else if (!g_DoNotCreateDataFiles) + { + // Read from buffer and write to a file. + ReadBytes(ByteDisplacement); + + // If the last packet was rendered(read in the sample driver's case), send out an etw event. + if ( (m_llEoSPosition >= 0) + && ((m_llEoSPosition - m_ullLinearPosition) < ByteDisplacement)) + { + PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj(); + pAdapterComm->WriteEtwEvent(eMINIPORT_LAST_BUFFER_RENDERED, + m_ullLinearPosition + ByteDisplacement, // Current linear buffer position + m_ulCurrentWritePosition, // The very last WaveRtBufferWritePosition that the driver received + 0, + 0); + } + } + + // Increment the DMA position by the number of bytes displaced since the last + // call to UpdatePosition() and ensure we properly wrap at buffer length. + // + m_ullPlayPosition = m_ullWritePosition = + (m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize; + + // m_ullDmaTimeStamp is updated in both GetPostion and GetLinearPosition calls + // so m_ullLinearPosition needs to be updated accordingly here + // + m_ullLinearPosition += ByteDisplacement; + + // Update the DMA time stamp for the next call to GetPosition() + // + m_ullDmaTimeStamp = hnsCurrentTime; +} + +//============================================================================= +#pragma code_seg() +VOID CMiniportWaveRTStream::WriteBytes +( +_In_ ULONG ByteDisplacement +) +/*++ + +Routine Description: + +This function writes the audio buffer using a sine wave generator. + +Arguments: + +ByteDisplacement - # of bytes to process. + +--*/ +{ + ULONG bufferOffset = m_ullLinearPosition % m_ulDmaBufferSize; + + // Normally this will loop no more than once for a single wrap, but if + // many bytes have been displaced then this may loops many times. + while (ByteDisplacement > 0) + { + ULONG runWrite = min(ByteDisplacement, m_ulDmaBufferSize - bufferOffset); + m_ToneGenerator.GenerateSine(m_pDmaBuffer + bufferOffset, runWrite); + bufferOffset = (bufferOffset + runWrite) % m_ulDmaBufferSize; + ByteDisplacement -= runWrite; + } +} + +//============================================================================= +#pragma code_seg() +VOID CMiniportWaveRTStream::ReadBytes +( +_In_ ULONG ByteDisplacement +) +/*++ + +Routine Description: + +This function reads the audio buffer and saves the data in a file. + +Arguments: + +ByteDisplacement - # of bytes to process. + +--*/ +{ + ULONG bufferOffset = m_ullLinearPosition % m_ulDmaBufferSize; + + // Normally this will loop no more than once for a single wrap, but if + // many bytes have been displaced then this may loops many times. + while (ByteDisplacement > 0) + { + ULONG runWrite = min(ByteDisplacement, m_ulDmaBufferSize - bufferOffset); + m_SaveData.WriteData(m_pDmaBuffer + bufferOffset, runWrite); + bufferOffset = (bufferOffset + runWrite) % m_ulDmaBufferSize; + ByteDisplacement -= runWrite; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRTStream::SetContentId +( + _In_ ULONG contentId, + _In_ PCDRMRIGHTS drmRights +) +/*++ + +Routine Description: + + Sets DRM content Id for this stream. Also updates the Mixed content Id. + +Arguments: + + contentId - new content id + + drmRights - rights for this stream. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::SetContentId]")); + + NTSTATUS ntStatus; + ULONG ulOldContentId = contentId; + + m_ulContentId = contentId; + + // + // Miniport should create a mixed DrmRights. + // + ntStatus = m_pMiniport->UpdateDrmRights(); + + // + // Restore the passed-in content Id. + // + if (!NT_SUCCESS(ntStatus)) + { + m_ulContentId = ulOldContentId; + } + + // + // SYSVAD writes each stream seperately to disk. If the rights for this + // stream indicates that the stream is CopyProtected, stop writing to disk. + // + m_SaveData.Disable(drmRights->CopyProtect); + + // + // From MSDN: + // + // This sample doesn't forward protected content, but if your driver uses + // lower layer drivers or a different stack to properly work, please see the + // following info from MSDN: + // + // "Before allowing protected content to flow through a data path, the system + // verifies that the data path is secure. To do so, the system authenticates + // each module in the data path beginning at the upstream end of the data path + // and moving downstream. As each module is authenticated, that module gives + // the system information about the next module in the data path so that it + // can also be authenticated. To be successfully authenticated, a module's + // binary file must be signed as DRM-compliant. + // + // Two adjacent modules in the data path can communicate with each other in + // one of several ways. If the upstream module calls the downstream module + // through IoCallDriver, the downstream module is part of a WDM driver. In + // this case, the upstream module calls the DrmForwardContentToDeviceObject + // function to provide the system with the device object representing the + // downstream module. (If the two modules communicate through the downstream + // module's COM interface or content handlers, the upstream module calls + // DrmForwardContentToInterface or DrmAddContentHandlers instead.) + // + // DrmForwardContentToDeviceObject performs the same function as + // PcForwardContentToDeviceObject and IDrmPort2::ForwardContentToDeviceObject." + // + // Other supported DRM DDIs for down-level module validation are: + // DrmForwardContentToInterfaces and DrmAddContentHandlers. + // + // For more information, see MSDN's DRM Functions and Interfaces. + // + + return ntStatus; +} // SetContentId + +#ifdef SYSVAD_BTH_BYPASS + +//============================================================================= +#pragma code_seg() +NTSTATUS +CMiniportWaveRTStream::GetScoStreamNtStatus() +/*++ + +Routine Description: + + Checks if the Bluetooth SCO HFP connection is up, if not, an error is returned. + +Return Value: + + NT status code. + +--*/ +{ + DPF_ENTER(("[CMiniportWaveRTStream::GetScoStreamNtStatus]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_STATE; + + if (m_ScoOpen) + { + PBTHHFPDEVICECOMMON bthHfpDevice; + + ASSERT(m_pMiniport->IsBthHfpDevice()); + bthHfpDevice = m_pMiniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + if (bthHfpDevice->GetStreamStatus()) + { + ntStatus = STATUS_SUCCESS; + } + } + + return ntStatus; +} +#endif // SYSVAD_BTH_BYPASS + +//============================================================================= +#pragma code_seg() +void +TimerNotifyRT +( + _In_ PKDPC Dpc, + _In_opt_ PVOID DeferredContext, + _In_opt_ PVOID SA1, + _In_opt_ PVOID SA2 +) +{ + LARGE_INTEGER qpc; + LARGE_INTEGER qpcFrequency; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SA1); + UNREFERENCED_PARAMETER(SA2); + + _IRQL_limited_to_(DISPATCH_LEVEL); + + qpc = KeQueryPerformanceCounter(&qpcFrequency); + + CMiniportWaveRTStream* _this = (CMiniportWaveRTStream*)DeferredContext; + + if (NULL == _this) + { + return; + } + + InterlockedIncrement64(&_this->m_llPacketCounter); + +#ifdef SYSVAD_BTH_BYPASS + if (_this->m_ScoOpen) + { + if (!NT_SUCCESS(_this->GetScoStreamNtStatus())) + { + return; + } + } +#endif // SYSVAD_BTH_BYPASS + + _this->m_pMiniport->DpcRoutine(qpc.QuadPart, qpcFrequency.QuadPart); + + if (_this->m_KsState != KSSTATE_RUN) + { + return; + } + + PADAPTERCOMMON pAdapterComm = _this->m_pMiniport->GetAdapterCommObj(); + + // Simple buffer underrun detection. + if (!_this->IsCurrentWaveRTWritePositionUpdated()) + { + //Event type: eMINIPORT_GLITCH_REPORT + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: major glitch code: 1:WaveRT buffer is underrun + //Parameter 4: minor code for the glitch cause + pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT, + 100, // replace with the correct "Current linear buffer position" + _this->GetCurrentWaveRTWritePosition(), + 1, // WaveRT buffer is underrun + 0); + } + + if (!IsListEmpty(&_this->m_NotificationList)) + { + PLIST_ENTRY leCurrent = _this->m_NotificationList.Flink; + while (leCurrent != &_this->m_NotificationList) + { + NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry); + //Event type: eMINIPORT_BUFFER_COMPLETE + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: Data length completed + //Parameter 4:0 + pAdapterComm->WriteEtwEvent(eMINIPORT_BUFFER_COMPLETE, + 100, // replace with the correct "Current linear buffer position" + _this->GetCurrentWaveRTWritePosition(), + 300, // repalce with the correct "Data length completed" + 0); // always zero + + KeSetEvent(nleCurrent->NotificationEvent, 0, 0); + + leCurrent = leCurrent->Flink; + } + } +} +//============================================================================= + diff --git a/audio/sysvad/EndpointsCommon/minwavertstream.h b/audio/sysvad/EndpointsCommon/minwavertstream.h new file mode 100644 index 00000000..78be0e15 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/minwavertstream.h @@ -0,0 +1,261 @@ +/*++ + +Copyright (c) 1997-2010 Microsoft Corporation All Rights Reserved + +Module Name: + + minwavert.h + +Abstract: + + Definition of wavert miniport class. + +--*/ + +#ifndef _SYSVAD_MINWAVERTSTREAM_H_ +#define _SYSVAD_MINWAVERTSTREAM_H_ + +#include "savedata.h" +#include "tonegenerator.h" + +// +// Structure to store notifications events in a protected list +// +typedef struct _NotificationListEntry +{ + LIST_ENTRY ListEntry; + PKEVENT NotificationEvent; +} NotificationListEntry; + +KDEFERRED_ROUTINE TimerNotifyRT; + +//============================================================================= +// Referenced Forward +//============================================================================= +class CMiniportWaveRT; +typedef CMiniportWaveRT *PCMiniportWaveRT; + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CMiniportWaveRTStream +// +class CMiniportWaveRTStream : + public IDrmAudioStream, + public IMiniportWaveRTStreamNotification, + public IMiniportWaveRTInputStream, + public IMiniportWaveRTOutputStream, + public IMiniportStreamAudioEngineNode, + // IMiniportStreamAudioEngineNode2 is only available for WinBlue + // We might need #ifdef this for WinBlue to avoid any compilation error when building downlvel OS support. + public IMiniportStreamAudioEngineNode2, + public CUnknown +{ +protected: + PPORTWAVERTSTREAM m_pPortStream; + LIST_ENTRY m_NotificationList; + PKTIMER m_pNotificationTimer; + PRKDPC m_pNotificationDpc; + ULONG m_ulNotificationIntervalMs; + ULONG m_ulCurrentWritePosition; + LONG m_IsCurrentWritePositionUpdated; + +public: + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(CMiniportWaveRTStream); + ~CMiniportWaveRTStream(); + + IMP_IMiniportWaveRTStream; + IMP_IMiniportWaveRTStreamNotification; + IMP_IMiniportWaveRTInputStream; + IMP_IMiniportWaveRTOutputStream; + IMP_IMiniportStreamAudioEngineNode; + IMP_IMiniportStreamAudioEngineNode2; + IMP_IMiniportWaveRT; + IMP_IDrmAudioStream; + + NTSTATUS Init + ( + _In_ PCMiniportWaveRT Miniport, + _In_ PPORTWAVERTSTREAM Stream, + _In_ ULONG Channel, + _In_ BOOLEAN Capture, + _In_ PKSDATAFORMAT DataFormat, + _In_ GUID SignalProcessingMode + ); + + // Friends + friend class CMiniportWaveRT; + friend KDEFERRED_ROUTINE TimerNotifyRT; +protected: + CMiniportWaveRT* m_pMiniport; + ULONG m_ulPin; + BOOLEAN m_bCapture; + BOOLEAN m_bUnregisterStream; + ULONG m_ulDmaBufferSize; + BYTE* m_pDmaBuffer; + ULONG m_ulNotificationsPerBuffer; + KSSTATE m_KsState; + PKTIMER m_pTimer; + PRKDPC m_pDpc; + ULONGLONG m_ullPlayPosition; + ULONGLONG m_ullWritePosition; + ULONGLONG m_ullLinearPosition; + ULONG m_ulLastOsReadPacket; + ULONG m_ulLastOsWritePacket; + LONGLONG m_llEoSPosition; + LONGLONG m_llPacketCounter; + ULONGLONG m_ullDmaTimeStamp; + LARGE_INTEGER m_ullPerformanceCounterFrequency; + ULONGLONG m_hnsElapsedTimeCarryForward; + ULONG m_ulDmaMovementRate; + BOOL m_bLfxEnabled; + PBOOL m_pbMuted; + PLONG m_plVolumeLevel; + PLONG m_plPeakMeter; + PWAVEFORMATEXTENSIBLE m_pWfExt; + ULONG m_ulContentId; + CSaveData m_SaveData; + ToneGenerator m_ToneGenerator; + GUID m_SignalProcessingMode; + +#ifdef SYSVAD_BTH_BYPASS + BOOLEAN m_ScoOpen; +#endif // SYSVAD_BTH_BYPASS + +public: + + NTSTATUS GetVolumeChannelCount + ( + _Out_ UINT32 *puiChannelCount + ); + + NTSTATUS GetVolumeSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelVolume + ( + _In_ UINT32 _uiChannel, + _Out_ LONG *_pVolume + ); + + NTSTATUS SetChannelVolume + ( + _In_ UINT32 _uiChannel, + _In_ LONG _Volume + ); + + NTSTATUS GetPeakMeterChannelCount + ( + _Out_ UINT32 *puiChannelCount + ); + + NTSTATUS GetPeakMeterSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelPeakMeter + ( + _In_ UINT32 _uiChannel, + _Out_ LONG *_plPeakMeter + ); + + NTSTATUS GetMuteChannelCount + ( + _Out_ UINT32 *puiChannelCount + ); + + NTSTATUS GetMuteSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelMute + ( + _In_ UINT32 _uiChannel, + _Out_ BOOL *_pbMute + ); + + NTSTATUS SetChannelMute + ( + _In_ UINT32 _uiChannel, + _In_ BOOL _bMute + ); + + //presentation + NTSTATUS GetPresentationPosition + ( + _Out_ KSAUDIO_PRESENTATION_POSITION *_pPresentationPosition + ); + + NTSTATUS SetCurrentWritePosition + ( + _In_ ULONG ulCurrentWritePosition + ); + + public: + NTSTATUS SetLoopbackProtection + ( + _In_ CONSTRICTOR_OPTION ulProtectionOption + ); + + ULONG GetCurrentWaveRTWritePosition() + { + return m_ulCurrentWritePosition; + }; + + // To support simple underrun validation. + BOOL IsCurrentWaveRTWritePositionUpdated() + { + return InterlockedExchange(&m_IsCurrentWritePositionUpdated, 0) ? TRUE : FALSE; + }; + + GUID GetSignalProcessingMode() + { + return m_SignalProcessingMode; + } + +private: + + // Helper functions. + VOID WriteBytes + ( + _In_ ULONG ByteDisplacement + ); + + VOID ReadBytes + ( + _In_ ULONG ByteDisplacement + ); + + VOID UpdatePosition + ( + _In_ LARGE_INTEGER ilQPC + ); + + NTSTATUS SetCurrentWritePositionInternal + ( + _In_ ULONG ulCurrentWritePosition + ); + + NTSTATUS GetLinearBufferPosition + ( + _Out_ ULONGLONG *pullLinearBufferPosition, + _Out_ LARGE_INTEGER *_pliQPCTime + ); + +#ifdef SYSVAD_BTH_BYPASS + NTSTATUS GetScoStreamNtStatus(); +#endif // SYSVAD_BTH_BYPASS + +}; +typedef CMiniportWaveRTStream *PCMiniportWaveRTStream; +#endif // _SYSVAD_MINWAVERTSTREAM_H_ + diff --git a/audio/sysvad/EndpointsCommon/simple.h b/audio/sysvad/EndpointsCommon/simple.h new file mode 100644 index 00000000..55913cdc --- /dev/null +++ b/audio/sysvad/EndpointsCommon/simple.h @@ -0,0 +1,170 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + simple.h + +Abstract: + + Node and Pin numbers and other common definitions for simple sample. + +--*/ + +#ifndef _SYSVAD_SIMPLE_H_ +#define _SYSVAD_SIMPLE_H_ + +// Name Guid +// {946A7B1A-EBBC-422a-A81F-F07C8D40D3B4} +#define STATIC_NAME_SYSVAD_SIMPLE\ + 0x946a7b1a, 0xebbc, 0x422a, 0xa8, 0x1f, 0xf0, 0x7c, 0x8d, 0x40, 0xd3, 0xb4 +DEFINE_GUIDSTRUCT("946A7B1A-EBBC-422a-A81F-F07C8D40D3B4", NAME_SYSVAD_SIMPLE); +#define NAME_SYSVAD_SIMPLE DEFINE_GUIDNAMED(NAME_SYSVAD_SIMPLE) + +//---------------------------------------------------- +// New defines for the render endpoints. +//---------------------------------------------------- + +// Default pin instances. +#define MAX_INPUT_SYSTEM_STREAMS 1 +#define MAX_INPUT_OFFLOAD_STREAMS 3 +#define MAX_OUTPUT_LOOPBACK_STREAMS 1 + +// Wave pins - offloading supported. +enum +{ + KSPIN_WAVE_RENDER_SINK_SYSTEM = 0, + KSPIN_WAVE_RENDER_SINK_OFFLOAD, + KSPIN_WAVE_RENDER_SINK_LOOPBACK, + KSPIN_WAVE_RENDER_SOURCE +}; + +// Wave Topology nodes - offloading supported. +enum +{ + KSNODE_WAVE_AUDIO_ENGINE = 0 +}; + +// Wave pins - offloading is NOT supported. +enum +{ + KSPIN_WAVE_RENDER2_SINK_SYSTEM = 0, + KSPIN_WAVE_RENDER2_SINK_LOOPBACK, + KSPIN_WAVE_RENDER2_SOURCE +}; + +// Wave pins - cellular +enum +{ + KSPIN_WAVE_BIDI = 0, + KSPIN_WAVE_BIDI_BRIDGE +}; + +// Wave Topology nodes - offloading is NOT supported. +enum +{ + KSNODE_WAVE_SUM = 0, + KSNODE_WAVE_VOLUME, + KSNODE_WAVE_MUTE, + KSNODE_WAVE_PEAKMETER +}; + +// Topology pins. +enum +{ + KSPIN_TOPO_WAVEOUT_SOURCE = 0, + KSPIN_TOPO_LINEOUT_DEST, +}; + +// Topology pins - cellular +enum +{ + KSPIN_TOPO_BIDI1 = 0, + KSPIN_TOPO_BIDI1_BRIDGE, + KSPIN_TOPO_BIDI2, + KSPIN_TOPO_BIDI2_BRIDGE +}; + +// Topology nodes. +enum +{ + KSNODE_TOPO_WAVEOUT_VOLUME = 0, + KSNODE_TOPO_WAVEOUT_MUTE, + KSNODE_TOPO_WAVEOUT_PEAKMETER +}; + +//---------------------------------------------------- +// New defines for the capture endpoints. +//---------------------------------------------------- + +// Default pin instances. +#define MAX_INPUT_STREAMS 1 // Number of capture streams. + +// Wave pins +enum +{ + KSPIN_WAVE_BRIDGE = 0, + KSPIN_WAVEIN_HOST, + KSPIN_WAVEIN_KEYWORD, +}; + +// Wave pins - FM +enum +{ + KSPIN_WAVE_FMRX = 0, + KSPIN_WAVE_FMRX_BRIDGE +}; + +// Wave Topology nodes. +enum +{ + KSNODE_WAVE_ADC = 0 +}; + +// topology pins. +enum +{ + KSPIN_TOPO_MIC_ELEMENTS, + KSPIN_TOPO_BRIDGE +}; + +// topology nodes. +enum +{ + KSNODE_TOPO_VOLUME, + KSNODE_TOPO_MUTE, + KSNODE_TOPO_PEAKMETER +}; + +// Topology pins - FM +enum +{ + KSPIN_TOPO_FMRX, + KSPIN_TOPO_FMRX_BRIDGE +}; + +// data format attribute range definitions. +static +KSATTRIBUTE PinDataRangeSignalProcessingModeAttribute = +{ + sizeof(KSATTRIBUTE), + 0, + STATICGUIDOF(KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE), +}; + +static +PKSATTRIBUTE PinDataRangeAttributes[] = +{ + &PinDataRangeSignalProcessingModeAttribute, +}; + +static +KSATTRIBUTE_LIST PinDataRangeAttributeList = +{ + ARRAYSIZE(PinDataRangeAttributes), + PinDataRangeAttributes, +}; + +#endif // _SYSVAD_SIMPLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/speakerhptopo.cpp b/audio/sysvad/EndpointsCommon/speakerhptopo.cpp new file mode 100644 index 00000000..96e899a0 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerhptopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhptopo.cpp + +Abstract: + + Implementation of topology miniport for the speaker (external: headphone). + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "speakerhptopo.h" +#include "speakerhptoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHpJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHpJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(SpeakerHpJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = SpeakerHpJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHpJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHpJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(SpeakerHpJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = SpeakerHpJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHpTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHpTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_SpeakerHpJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_SpeakerHpJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_SpeakerHpTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/EndpointsCommon/speakerhptopo.h b/audio/sysvad/EndpointsCommon/speakerhptopo.h new file mode 100644 index 00000000..9974859f --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerhptopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhptopo.h + +Abstract: + + Declaration of topology miniport for the speaker (external: headphone). + +--*/ + +#ifndef _SYSVAD_SPEAKERHPTOPO_H_ +#define _SYSVAD_SPEAKERHPTOPO_H_ + +NTSTATUS PropertyHandler_SpeakerHpTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_SPEAKERHPTOPO_H_ diff --git a/audio/sysvad/EndpointsCommon/speakerhptoptable.h b/audio/sysvad/EndpointsCommon/speakerhptoptable.h new file mode 100644 index 00000000..870fd02d --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerhptoptable.h @@ -0,0 +1,157 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhptoptable.h + +Abstract: + + Declaration of topology tables for the speaker (external: headphone). + +--*/ + +#ifndef _SYSVAD_SPEAKERHPTOPTABLE_H_ +#define _SYSVAD_SPEAKERHPTOPTABLE_H_ + +//============================================================================= +static +KSJACK_DESCRIPTION SpeakerHpJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + JACKDESC_RGB(179, 201, 140), + eConnTypeCombination, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + TRUE +}; + +//============================================================================= +static +KSDATARANGE SpeakerHpTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpeakerHpTopoPinDataRangePointersBridge[] = +{ + &SpeakerHpTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerHpTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerHpTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerHpTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN - topology filter out-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerHpTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerHpTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADPHONES, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpeakerHpJackDescriptions[] = +{ + NULL, + &SpeakerHpJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR SpeakerHpTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerHpTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerHpTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerHpTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHpTopoFilter, PropertiesSpeakerHpTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerHpTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerHpTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerHpTopoMiniportPins), // PinCount + SpeakerHpTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpeakerHpTopoMiniportConnections), // ConnectionCount + SpeakerHpTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_SPEAKERHPTOPTABLE_H_ + + diff --git a/audio/sysvad/EndpointsCommon/speakerhpwavtable.h b/audio/sysvad/EndpointsCommon/speakerhpwavtable.h new file mode 100644 index 00000000..2b5b61ed --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerhpwavtable.h @@ -0,0 +1,854 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhpwavtable.h + +Abstract: + + Declaration of wave miniport tables for the speaker (external: headphone). + +--*/ + +#ifndef _SYSVAD_SPEAKERHPWAVTABLE_H_ +#define _SYSVAD_SPEAKERHPWAVTABLE_H_ + + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + +#define SPEAKERHP_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPEAKERHP_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHP_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHP_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHP_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKERHP_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPEAKERHP_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHP_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHP_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHP_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKERHP_OFFLOAD_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKERHP_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHP_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHP_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHP_LOOPBACK_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKERHP_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKERHP_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHP_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHP_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHP_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKERHP_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPEAKERHP_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define SPEAKERHP_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define SPEAKERHP_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHpAudioEngineSupportedDeviceFormats[] = +{ + { // 0 : First entry in this table is the default format for the audio engine + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHpHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHpOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHpLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpeakerHpHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &SpeakerHpHostPinSupportedDeviceFormats[0].DataFormat // 24KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + } +}; + +static +MODE_AND_DEFAULT_FORMAT SpeakerHpOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHpOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHpOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpeakerHpPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpeakerHpHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHpHostPinSupportedDeviceFormats), + SpeakerHpHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHpHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + SpeakerHpOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHpOffloadPinSupportedDeviceFormats), + SpeakerHpOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHpOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + SpeakerHpLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHpLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + SpeakerHpAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHpAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpeakerHpPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHP_HOST_MAX_CHANNELS, + SPEAKERHP_HOST_MIN_BITS_PER_SAMPLE, + SPEAKERHP_HOST_MAX_BITS_PER_SAMPLE, + SPEAKERHP_HOST_MIN_SAMPLE_RATE, + SPEAKERHP_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHP_OFFLOAD_MAX_CHANNELS, + SPEAKERHP_OFFLOAD_MIN_BITS_PER_SAMPLE, + SPEAKERHP_OFFLOAD_MAX_BITS_PER_SAMPLE, + SPEAKERHP_OFFLOAD_MIN_SAMPLE_RATE, + SPEAKERHP_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHP_LOOPBACK_MAX_CHANNELS, + SPEAKERHP_LOOPBACK_MIN_BITS_PER_SAMPLE, + SPEAKERHP_LOOPBACK_MAX_BITS_PER_SAMPLE, + SPEAKERHP_LOOPBACK_MIN_SAMPLE_RATE, + SPEAKERHP_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpeakerHpPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpeakerHpPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpeakerHpPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&SpeakerHpPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpeakerHpPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&SpeakerHpPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE SpeakerHpPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpeakerHpPinDataRangePointersBridge[] = +{ + &SpeakerHpPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesSpeakerHpOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHpOffloadPin, PropertiesSpeakerHpOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerHpWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + SPEAKERHP_MAX_INPUT_SYSTEM_STREAMS, + SPEAKERHP_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHpPinDataRangePointersStream), + SpeakerHpPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + SPEAKERHP_MAX_INPUT_OFFLOAD_STREAMS, + SPEAKERHP_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationSpeakerHpOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHpPinDataRangePointersOffloadStream), + SpeakerHpPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + SPEAKERHP_MAX_OUTPUT_LOOPBACK_STREAMS, + SPEAKERHP_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHpPinDataRangePointersLoopbackStream), + SpeakerHpPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHpPinDataRangePointersBridge), + SpeakerHpPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR SpeakerHpWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpeakerHpWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerHpWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHpWaveFilter, PropertiesSpeakerHpWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerHpWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerHpWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerHpWaveMiniportPins), // PinCount + SpeakerHpWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpeakerHpWaveMiniportNodes), // NodeCount + SpeakerHpWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(SpeakerHpWaveMiniportConnections), // ConnectionCount + SpeakerHpWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_SPEAKERHPWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/speakertopo.cpp b/audio/sysvad/EndpointsCommon/speakertopo.cpp new file mode 100644 index 00000000..cca4dac9 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakertopo.cpp @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertopo.cpp + +Abstract: + + Implementation of topology miniport for the speaker (internal). + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "speakertopo.h" +#include "speakertoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription( + PropertyRequest, + ARRAYSIZE(SpeakerJackDescriptions), + SpeakerJackDescriptions + ); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2( + PropertyRequest, + ARRAYSIZE(SpeakerJackDescriptions), + SpeakerJackDescriptions, + 0 // jack capabilities + ); + } + } + + return ntStatus; +} // PropertyHandler_SpeakerTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/EndpointsCommon/speakertopo.h b/audio/sysvad/EndpointsCommon/speakertopo.h new file mode 100644 index 00000000..1ea21374 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakertopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertopo.h + +Abstract: + + Declaration of topology miniport for the speaker (internal). + +--*/ + +#ifndef _SYSVAD_SPEAKERTOPO_H_ +#define _SYSVAD_SPEAKERTOPO_H_ + +NTSTATUS PropertyHandler_SpeakerTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_SPEAKERTOPO_H_ diff --git a/audio/sysvad/EndpointsCommon/speakertoptable.h b/audio/sysvad/EndpointsCommon/speakertoptable.h new file mode 100644 index 00000000..50f58c7b --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakertoptable.h @@ -0,0 +1,155 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertoptable.h + +Abstract: + + Declaration of topology tables. + +--*/ + +#ifndef _SYSVAD_SPEAKERTOPTABLE_H_ +#define _SYSVAD_SPEAKERTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE SpeakerTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpeakerTopoPinDataRangePointersBridge[] = +{ + &SpeakerTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_SPEAKER, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION SpeakerJackDescSpeakers = +{ + KSAUDIO_SPEAKER_STEREO, + 0xB3C98C, // Color spec for green + eConnTypeUnknown, + eGeoLocFront, + eGenLocPrimaryBox, + ePortConnIntegratedDevice, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpeakerJackDescriptions[] = +{ + NULL, + &SpeakerJackDescSpeakers +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR SpeakerTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerTopoFilter, PropertiesSpeakerTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerTopoMiniportPins), // PinCount + SpeakerTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpeakerTopoMiniportConnections), // ConnectionCount + SpeakerTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_SPEAKERTOPTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/speakerwavtable.h b/audio/sysvad/EndpointsCommon/speakerwavtable.h new file mode 100644 index 00000000..3322b0a7 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerwavtable.h @@ -0,0 +1,863 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerwavtable.h + +Abstract: + + Declaration of wave miniport tables for the render endpoints. + +--*/ + +#ifndef _SYSVAD_SPEAKERWAVTABLE_H_ +#define _SYSVAD_SPEAKERWAVTABLE_H_ + +#include "SysVadShared.h" + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + +#define SPEAKER_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPEAKER_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKER_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPEAKER_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKER_OFFLOAD_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKER_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_LOOPBACK_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKER_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKER_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPEAKER_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define SPEAKER_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define SPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerAudioEngineSupportedDeviceFormats[] = +{ + { // 0 : First entry in this table is the default format for the audio engine + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpeakerHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &SpeakerHostPinSupportedDeviceFormats[0].DataFormat // 24KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + } +}; + +static +MODE_AND_DEFAULT_FORMAT SpeakerOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpeakerPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpeakerHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHostPinSupportedDeviceFormats), + SpeakerHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + SpeakerOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerOffloadPinSupportedDeviceFormats), + SpeakerOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + SpeakerLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + SpeakerAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpeakerPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKER_HOST_MAX_CHANNELS, + SPEAKER_HOST_MIN_BITS_PER_SAMPLE, + SPEAKER_HOST_MAX_BITS_PER_SAMPLE, + SPEAKER_HOST_MIN_SAMPLE_RATE, + SPEAKER_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKER_OFFLOAD_MAX_CHANNELS, + SPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE, + SPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE, + SPEAKER_OFFLOAD_MIN_SAMPLE_RATE, + SPEAKER_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKER_LOOPBACK_MAX_CHANNELS, + SPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE, + SPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE, + SPEAKER_LOOPBACK_MIN_SAMPLE_RATE, + SPEAKER_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpeakerPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&SpeakerPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList), + +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&SpeakerPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE SpeakerPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersBridge[] = +{ + &SpeakerPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesSpeakerOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerOffloadPin, PropertiesSpeakerOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + SPEAKER_MAX_INPUT_SYSTEM_STREAMS, + SPEAKER_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersStream), + SpeakerPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + SPEAKER_MAX_INPUT_OFFLOAD_STREAMS, + SPEAKER_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationSpeakerOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersOffloadStream), + SpeakerPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + SPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + SPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersLoopbackStream), + SpeakerPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersBridge), + SpeakerPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR SpeakerWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpeakerWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_SysVAD, + KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } + +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerWaveFilter, PropertiesSpeakerWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerWaveMiniportPins), // PinCount + SpeakerWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpeakerWaveMiniportNodes), // NodeCount + SpeakerWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(SpeakerWaveMiniportConnections), // ConnectionCount + SpeakerWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_SPEAKERWAVTABLE_H_ + diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.cpp b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.cpp new file mode 100644 index 00000000..9bcdedee --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.cpp @@ -0,0 +1,171 @@ +// KeywordDetectorContosoAdapter.cpp : Defines the exported functions for the DLL application. +// + +// ISSUE-2015/1/14-frankye review all error codes + +#include "stdafx.h" + +#include <strsafe.h> +#include <mfapi.h> + +#include <initguid.h> +#include "KeywordDetectorContosoAdapter.h" +#include "ContosoKeywordDetector.h" +#include <wrl.h> + +using namespace Microsoft::WRL; + +class KeywordDetectorContosoAdapter : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IKeywordDetectorOemAdapter> +{ +public: + KeywordDetectorContosoAdapter() + { + } + + STDMETHODIMP KeywordDetectorContosoAdapter::GetCapabilities( + BOOL* SupportsUserModels, + KEYWORDID** KeywordIds, + ULONG* NumKeywords, + LANGID** LangIds, + ULONG* NumLanguages, + IMFMediaType* *ppMediaType) + { + HRESULT hr; + ComPtr<IMFMediaType> spMFType; + const WAVEFORMATEX waveFormat = { WAVE_FORMAT_PCM, 1, 16000, 32000, 2, 16, 0 }; + + *SupportsUserModels = FALSE; + + *KeywordIds = (KEYWORDID *)CoTaskMemAlloc(sizeof(KEYWORDID)); + if (*KeywordIds == nullptr) + { + return E_OUTOFMEMORY; + } + **KeywordIds = KwVoiceAssistant; + *NumKeywords = 1; + *LangIds = (LANGID *)CoTaskMemAlloc(sizeof(LANGID)); + if (*LangIds == nullptr) + { + CoTaskMemFree(*KeywordIds); + return E_OUTOFMEMORY; + } + **LangIds = 0x409; + *NumLanguages = 1; + + hr = MFCreateMediaType(spMFType.GetAddressOf()); + if (SUCCEEDED(hr)) + { + hr = MFInitMediaTypeFromWaveFormatEx(spMFType.Get(), &waveFormat, sizeof(waveFormat)); + if (SUCCEEDED(hr)) + { + spMFType.CopyTo(ppMediaType); + hr = S_OK; + } + } + + return hr; + } + + STDMETHODIMP VerifyUserKeyword( + IStream* ModelData, + KEYWORDID KeywordId, + LANGID LangId, + LONG KeywordEndBytePos, + IMFMediaBuffer* UserRecording) + { + UNREFERENCED_PARAMETER(ModelData); + UNREFERENCED_PARAMETER(KeywordId); + UNREFERENCED_PARAMETER(LangId); + UNREFERENCED_PARAMETER(KeywordEndBytePos); + UNREFERENCED_PARAMETER(UserRecording); + + return E_NOTIMPL; + } + + STDMETHODIMP ComputeAndAddUserModelData( + IStream* ModelData, + KEYWORDSELECTOR KeywordSelector, + LONG *KeywordEndBytePos, + IMFMediaBuffer **UserRecordings, + ULONG NumUserRecordings) + { + UNREFERENCED_PARAMETER(ModelData); + UNREFERENCED_PARAMETER(KeywordSelector); + UNREFERENCED_PARAMETER(KeywordEndBytePos); + UNREFERENCED_PARAMETER(UserRecordings); + UNREFERENCED_PARAMETER(NumUserRecordings); + + return E_NOTIMPL; + } + + STDMETHODIMP BuildArmingPatternData( + IStream *ModelData, + KEYWORDSELECTOR *KeywordSelectors, + ULONG NumKeywordSelectors, + SOUNDDETECTOR_PATTERNHEADER **ppPatternData) + { + CONTOSO_KEYWORDCONFIGURATION *pPatternData = nullptr; + + UNREFERENCED_PARAMETER(ModelData); + + if (NumKeywordSelectors > 1) + { + return E_INVALIDARG; + } + if ((KeywordSelectors[0].KeywordId != KwVoiceAssistant) || + (KeywordSelectors[0].LangId != 0x0409)) + { + return E_INVALIDARG; + } + + pPatternData = (CONTOSO_KEYWORDCONFIGURATION*)CoTaskMemAlloc(sizeof(CONTOSO_KEYWORDCONFIGURATION)); + if (pPatternData == nullptr) + { + return E_OUTOFMEMORY; + } + + pPatternData->Header.Size = sizeof(*pPatternData); + pPatternData->Header.PatternType = CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER; + pPatternData->ContosoDetectorConfigurationData = 0x12345678; + + *ppPatternData = &pPatternData->Header; + pPatternData = nullptr; + + return S_OK; + } + + STDMETHODIMP ParseDetectionResultData( + IStream *ModelData, + SOUNDDETECTOR_PATTERNHEADER *Result, // ISSUE-2015/1/14-frankye should be const + KEYWORDID* KeywordId, + LANGID* LangId, + BOOL *pIsUserMatch, + ULONG64 *KeywordStartPerformanceCounterValue, + ULONG64 *KeywordEndPerformanceCounterValue) + { + const CONTOSO_KEYWORDDETECTIONRESULT *contosoResult; + + UNREFERENCED_PARAMETER(ModelData); + + if (Result->PatternType != CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER || Result->Size < sizeof(CONTOSO_KEYWORDDETECTIONRESULT)) + { + return E_INVALIDARG; + } + + contosoResult = (CONTOSO_KEYWORDDETECTIONRESULT*)Result; + + // Do something with the result data to determine return parameters + // PostProcessResult(contosoResult->ContosoDetectorResultData); + + *KeywordId = KwVoiceAssistant; + *LangId = 0x0409; + *pIsUserMatch = FALSE; + + *KeywordStartPerformanceCounterValue = 0; + *KeywordEndPerformanceCounterValue = 0; + + return S_OK; + } +}; + +CoCreatableClass(KeywordDetectorContosoAdapter); diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.def b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.def new file mode 100644 index 00000000..ddeb3eac --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.def @@ -0,0 +1,6 @@ +LIBRARY + +EXPORTS + DllGetActivationFactory PRIVATE + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.idl b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.idl new file mode 100644 index 00000000..47431ce7 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.idl @@ -0,0 +1,16 @@ +import "oaidl.idl"; +import "ocidl.idl"; + +import "KeywordDetectorOemAdapter.idl"; + +[uuid(9D3E6826-CB8E-4D86-8B14-89F0D7EFCD01), version(1.0)] +library KeywordDetectorContosoAdapterLib +{ + // The class's uuid (i.e. the COM CLSID) must match that of the pattern + // type GUID returned by the audio driver + [uuid(6F7DBCC1-202E-498D-99C5-61C36C4EB2DC), version(1.0)] + coclass KeywordDetectorContosoAdapter + { + [default] interface IKeywordDetectorOemAdapter; + } +}; diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.rc b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.rc new file mode 100644 index 00000000..6ce78022 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.rc @@ -0,0 +1,13 @@ +#if 0 +Copyright (c) Microsoft Corporation. All Rights Reserved +#endif + +#include "winres.h" +#include <ntverp.h> +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Contoso keyword detector adapter" +#define VER_INTERNALNAME_STR "KeywordDetectorContosoAdapter" +#define VER_ORIGINALFILENAME_STR "KeywordDetectorContosoAdapter.dll" +#include <Common.ver> +
\ No newline at end of file diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj new file mode 100644 index 00000000..29fa6d04 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj @@ -0,0 +1,232 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{92E28772-ADE3-4367-B679-6DCF6FFA75F1}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <SupportsPackaging>false</SupportsPackaging> + <RequiresPackageProject>true</RequiresPackageProject> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{B1EE584B-A24D-4186-9759-A08E50D9F301}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>KeywordDetectorContosoAdapter</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>KeywordDetectorContosoAdapter</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>KeywordDetectorContosoAdapter</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>KeywordDetectorContosoAdapter</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='false'">MultiThreaded</RuntimeLibrary> + <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='true'">MultiThreadedDebug</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;mfplat.lib;runtimeobject.lib</AdditionalDependencies> + <ModuleDefinitionFile>KeywordDetectorContosoAdapter.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='false'">MultiThreaded</RuntimeLibrary> + <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='true'">MultiThreadedDebug</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;mfplat.lib;runtimeobject.lib</AdditionalDependencies> + <ModuleDefinitionFile>KeywordDetectorContosoAdapter.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='false'">MultiThreaded</RuntimeLibrary> + <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='true'">MultiThreadedDebug</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;mfplat.lib;runtimeobject.lib</AdditionalDependencies> + <ModuleDefinitionFile>KeywordDetectorContosoAdapter.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='false'">MultiThreaded</RuntimeLibrary> + <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='true'">MultiThreadedDebug</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;mfplat.lib;runtimeobject.lib</AdditionalDependencies> + <ModuleDefinitionFile>KeywordDetectorContosoAdapter.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="dllmain.cpp" /> + <ClCompile Include="KeywordDetectorContosoAdapter.cpp" /> + <ClCompile Include="stdafx.cpp" /> + <Midl Include="KeywordDetectorContosoAdapter.idl" /> + <ResourceCompile Include="KeywordDetectorContosoAdapter.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj.Filters b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj.Filters new file mode 100644 index 00000000..470f3402 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj.Filters @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{B50B7E60-1411-4963-BE4B-B214F230A656}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{0BDF6F65-E222-4DE0-B90F-7DE3C57089E8}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{F589D76A-014F-48A8-A294-56C98193C5A1}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="dllmain.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="KeywordDetectorContosoAdapter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="stdafx.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <Midl Include="KeywordDetectorContosoAdapter.idl"> + <Filter>Source Files</Filter> + </Midl> + <None Include="KeywordDetectorContosoAdapter.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="KeywordDetectorContosoAdapter.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/KeywordDetectorAdapter/dllmain.cpp b/audio/sysvad/KeywordDetectorAdapter/dllmain.cpp new file mode 100644 index 00000000..c29f4464 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/dllmain.cpp @@ -0,0 +1,33 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" +#include <wrl\module.h> + +using namespace Microsoft::WRL; + +#if !defined(__WRL_CLASSIC_COM__) +STDAPI DllGetActivationFactory(_In_ HSTRING activatibleClassId, _COM_Outptr_ IActivationFactory** factory) +{ + return Module<InProc>::GetModule().GetActivationFactory(activatibleClassId, factory); +} +#endif + +#if !defined(__WRL_WINRT_STRICT__) +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv) +{ + return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv); +} +#endif + +STDAPI DllCanUnloadNow() +{ + return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE; +} + +STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hinst); + } + return TRUE; +} diff --git a/audio/sysvad/KeywordDetectorAdapter/stdafx.cpp b/audio/sysvad/KeywordDetectorAdapter/stdafx.cpp new file mode 100644 index 00000000..3c8b5956 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// KeywordDetectorContosoAdapter.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/audio/sysvad/KeywordDetectorAdapter/stdafx.h b/audio/sysvad/KeywordDetectorAdapter/stdafx.h new file mode 100644 index 00000000..b8ae2aec --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include <windows.h> + + + +// TODO: reference additional headers your program requires here diff --git a/audio/sysvad/KeywordDetectorAdapter/targetver.h b/audio/sysvad/KeywordDetectorAdapter/targetver.h new file mode 100644 index 00000000..87c0086d --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include <SDKDDKVer.h> diff --git a/audio/sysvad/Package/package.VcxProj b/audio/sysvad/Package/package.VcxProj new file mode 100644 index 00000000..7bd46cd4 --- /dev/null +++ b/audio/sysvad/Package/package.VcxProj @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\KeywordDetectorAdapter\KeywordDetectorContosoAdapter.vcxproj"> + <Project>{92E28772-ADE3-4367-B679-6DCF6FFA75F1}</Project> + </ProjectReference> + <ProjectReference Include="..\PhoneAudioSample\PhoneAudioSample.vcxproj"> + <Project>{43FF11E5-B4C4-409A-AE4B-13342918B6F8}</Project> + </ProjectReference> + <ProjectReference Include="..\SwapAPO\APO\SwapAPO.vcxproj"> + <Project>{2A1B7375-D9CB-4067-AEDE-180D75B5934D}</Project> + </ProjectReference> + <ProjectReference Include="..\SwapAPO\PropPageExtensions\PropPageExt.vcxproj"> + <Project>{76DBA957-38F6-47ED-A5C0-AD587D08FF12}</Project> + </ProjectReference> + <ProjectReference Include="..\TabletAudioSample\TabletAudioSample.vcxproj"> + <Project>{29A5DD25-AB56-4DEE-96AB-21EC2926A300}</Project> + </ProjectReference> + </ItemGroup> + <PropertyGroup Label="PropertySheets"> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Utility</ConfigurationType> + <DriverType>Package</DriverType> + <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck> + <Configuration>Debug</Configuration> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Globals"> + <ProjectGuid>{5F7004D7-03BD-44AA-9FDD-6677B2C93469}</ProjectGuid> + <SampleGuid>{24733FBE-11F1-4558-97A6-983B6BB36797}</SampleGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup /> + <PropertyGroup> + <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor> + <ImportToStore>False</ImportToStore> + <InstallMode>None</InstallMode> + <HardwareIdString /> + <CommandLine /> + <ScriptPath /> + <DeployFiles /> + <ScriptName /> + <ScriptDeviceQuery>%PathToInf%</ScriptDeviceQuery> + <EnableVerifier>False</EnableVerifier> + <AllDrivers>False</AllDrivers> + <VerifyProjectOutput>True</VerifyProjectOutput> + <VerifyDrivers /> + <VerifyFlags>133563</VerifyFlags> + </PropertyGroup> + <ItemDefinitionGroup> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/Package/package.VcxProj.Filters b/audio/sysvad/Package/package.VcxProj.Filters new file mode 100644 index 00000000..6f0874bd --- /dev/null +++ b/audio/sysvad/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{64F96D78-EC33-4578-89CD-4D57E4661BB1}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{D77D2DFB-9D29-470D-8D22-893952C6C47D}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{3C34DD80-8AEE-4672-BEF4-BF6971E63C85}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{1028BDCB-C3D9-4F31-845E-AA1B26F6E4BF}</UniqueIdentifier> + </Filter> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/PhoneAudioSample/CellularWave.cpp b/audio/sysvad/PhoneAudioSample/CellularWave.cpp new file mode 100644 index 00000000..1a999c1e --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/CellularWave.cpp @@ -0,0 +1,685 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellularwave.cpp + +Abstract: + + Implementation of wavert miniport for cellular + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "CellularWave.h" +#include "CellularTopo.h" +#include "CellularToptable.h" + +//============================================================================= +// CCellularMiniportWaveRT +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateCellularMiniportWaveRT +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Create the wavert miniport. + +Arguments: + + Unknown - + + RefClsId - + + UnknownOuter - + + PoolType - + + UnkownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(UnknownOuter); + + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + CMiniportWaveRT *obj = new (PoolType, MINWAVERT_POOLTAG) CCellularMiniportWaveRT + ( + UnknownAdapter, + MiniportPair, + DeviceContext + ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +CCellularMiniportWaveRT::~CCellularMiniportWaveRT +( + void +) +/*++ + +Routine Description: + + Destructor for wavert miniport + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::~CCellularMiniportWaveRT]")); + +} // ~CCellularMiniportWaveRT + + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CCellularMiniportWaveRT::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for CCellularMiniportWaveRT + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IAdapterPowerManagement)) + { + *Object = PVOID(PADAPTERPOWERMANAGEMENT(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return CMiniportWaveRT::NonDelegatingQueryInterface(Interface, Object); +} + +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_CellularWaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CCellularMiniportWaveRT* pWaveHelper = reinterpret_cast<CCellularMiniportWaveRT*>(PropertyRequest->MajorTarget); + + if (pWaveHelper == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + pWaveHelper->AddRef(); + + if (pWaveHelper->m_DeviceType == eCellularDevice && + IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_TelephonyControl)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_TELEPHONY_PROVIDERID: + ntStatus = pWaveHelper->PropertyHandler_TelephonyProviderId(PropertyRequest); + break; + + case KSPROPERTY_TELEPHONY_CALLINFO: + ntStatus = pWaveHelper->PropertyHandler_TelephonyCallInfo(PropertyRequest); + break; + + case KSPROPERTY_TELEPHONY_CALLCONTROL: + case KSPROPERTY_TELEPHONY_PROVIDERCHANGE: + ntStatus = pWaveHelper->PropertyHandler_TelephonyCallState(PropertyRequest); + break; + + case KSPROPERTY_TELEPHONY_CALLHOLD: + ntStatus = pWaveHelper->PropertyHandler_TelephonyCallHold(PropertyRequest); + break; + + case KSPROPERTY_TELEPHONY_MUTE_TX: + ntStatus = pWaveHelper->PropertyHandler_TelephonyMuteTx(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandler_CellularWaveFilter: Invalid Device Request]")); + } + } + else + { + ntStatus = PropertyHandler_WaveFilter(PropertyRequest); + } + + pWaveHelper->Release(); + + return ntStatus; +} // PropertyHandler_CellularWaveFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyProviderId +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyProviderId]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = sizeof(ULONG); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ULONG* providerId = (ULONG*)PropertyRequest->Value; + // Return supported TelephonyProviderId (executor id). + *providerId = m_TelephonyProviderId; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyProviderId + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyCallInfo +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyCallInfo]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = sizeof(KSTELEPHONY_CALLINFO); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSTELEPHONY_CALLINFO TelephonyCallInfo = static_cast<PKSTELEPHONY_CALLINFO>(PropertyRequest->Value); + // Return current CallType and CallState information. CallType information is not valid when the CallState is disabled. + TelephonyCallInfo->CallType = m_TelephonyCallType; + TelephonyCallInfo->CallState = m_TelephonyCallState; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyCallInfo + +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::UpdateTopologyJackState +( + _In_ ULONG TelephonyId, + _In_ BOOL NewState +) +{ + PAGED_CODE (); + NTSTATUS ntStatus = STATUS_SUCCESS; + IUnknown *pUnknown = NULL; + ICellularTopology *pCellularTopology = NULL; + + // GetFilters returns the topology port, then topology miniport, then wave port, then wave miniport. + // we only need the topology miniport. + ntStatus = m_pAdapterCommon->GetFilters(m_pMiniportPair, NULL, &pUnknown, NULL, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pUnknown->QueryInterface(IID_ICellularTopology, (PVOID *)&pCellularTopology); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pCellularTopology->UpdateTopologyJackState(TelephonyId, NewState); + } + } + + SAFE_RELEASE(pCellularTopology); + SAFE_RELEASE(pUnknown); + + return STATUS_SUCCESS; +} +#pragma code_seg() + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyCallState +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyCallState]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = 0; + + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_CALLCONTROL) + { + cbMinSize = sizeof(KSTELEPHONY_CALLCONTROL); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_PROVIDERCHANGE) + { + cbMinSize = sizeof(KSTELEPHONY_PROVIDERCHANGE); + } + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_CALLCONTROL) + { + PKSTELEPHONY_CALLCONTROL TelephonyCallControl = static_cast<PKSTELEPHONY_CALLCONTROL>(PropertyRequest->Value); + + // KSPROPERTY_TELEPHONY_CALLCONTROL has info about CallType and CallControlOp. TELEPHONY_CALLCONTROLOP_ENABLE will start + // cellular call from audio driver perspective, update jack state for associated cellular bidi endpoint to active, + // save call type and update the call state to Enabled. TELEPHONY_CALLCONTROLOP_DISABLE will terminate cellular call + // from audio driver perspective, update jack state for associated cellular bidi endpoint to unplugged and update + // call state to Disabled. Call type is ignored in this case. + switch (TelephonyCallControl->CallControlOp) + { + case TELEPHONY_CALLCONTROLOP_DISABLE: + ASSERT(m_TelephonyCallState != TELEPHONY_CALLSTATE_DISABLED); + m_TelephonyCallState = TELEPHONY_CALLSTATE_DISABLED; + UpdateTopologyJackState(m_TelephonyProviderId, FALSE); + // Release our idle power management requirement + m_pAdapterCommon->SetIdlePowerManagement(m_pMiniportPair, TRUE); + ntStatus = STATUS_SUCCESS; + break; + + case TELEPHONY_CALLCONTROLOP_ENABLE: + ASSERT(m_TelephonyCallState != TELEPHONY_CALLSTATE_ENABLED); + m_TelephonyCallType = TelephonyCallControl->CallType; + m_TelephonyCallState = TELEPHONY_CALLSTATE_ENABLED; + UpdateTopologyJackState(m_TelephonyProviderId, TRUE); + // When we're in a call, we need idle power management disabled. + m_pAdapterCommon->SetIdlePowerManagement(m_pMiniportPair, FALSE); + ntStatus = STATUS_SUCCESS; + break; + + default: + ntStatus = STATUS_INVALID_PARAMETER; + break; + } + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_PROVIDERCHANGE) + { + PKSTELEPHONY_PROVIDERCHANGE TelephonyProviderChange = static_cast<PKSTELEPHONY_PROVIDERCHANGE>(PropertyRequest->Value); + + // KSPROPERTY_TELEPHONY_PROVIDERCHANGE has info about CallType and ProviderChangeOp. TELEPHONY_PROVIDERCHANGEOP_BEGIN will indicate + // start of SRVCC to audio driver. Audio driver will then start the SRVCC and update the call state to ProviderTransition. + // TELEPHONY_PROVIDERCHANGEOP_END will indicate end of SRVCC to audio driver and driver will end SRVCC process and update + // call state to Enabled. TELEPHONY_PROVIDERCHANGEOP_CANCEL will indicate SRVCC was aborted and audio driver will revert back to + // pre SRVCC call. + switch (TelephonyProviderChange->ProviderChangeOp) + { + case TELEPHONY_PROVIDERCHANGEOP_END: + m_TelephonyCallState = TELEPHONY_CALLSTATE_ENABLED; + ntStatus = STATUS_SUCCESS; + break; + + case TELEPHONY_PROVIDERCHANGEOP_BEGIN: + m_PreviousTelephonyCallType = m_TelephonyCallType; + m_TelephonyCallType = TelephonyProviderChange->CallType; + m_TelephonyCallState = TELEPHONY_CALLSTATE_PROVIDERTRANSITION; + ntStatus = STATUS_SUCCESS; + break; + + case TELEPHONY_PROVIDERCHANGEOP_CANCEL: + m_TelephonyCallType = m_PreviousTelephonyCallType; + m_TelephonyCallState = TELEPHONY_CALLSTATE_ENABLED; + ntStatus = STATUS_SUCCESS; + break; + + default: + ntStatus = STATUS_INVALID_PARAMETER; + break; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyCallState + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyCallHold +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyCallHold]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = sizeof(BOOL); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + BOOL* bTelephonyHold = (BOOL*) PropertyRequest->Value; + // Return if the call is on hold or not + *bTelephonyHold = m_bTelephonyHold; + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + BOOL* bTelephonyHold = (BOOL*) PropertyRequest->Value; + m_bTelephonyHold = *bTelephonyHold; + + // If this property is TRUE, put the call on hold and update the call state to hold. + // If this property is FALSE, take the call out of hold and update the call state to enabled. + m_TelephonyCallState = (*bTelephonyHold) ? TELEPHONY_CALLSTATE_HOLD : TELEPHONY_CALLSTATE_ENABLED; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyCallHold + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyMuteTx +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyMuteTx]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = sizeof(BOOL); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + BOOL* bTxMute = (BOOL*) PropertyRequest->Value; + // Return if the cellular Tx is on muted or not + *bTxMute = m_bTelephonyMuteTx; + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + BOOL* bTxMute = (BOOL*) PropertyRequest->Value; + // If this property is TRUE, mute cellular Tx data. However, it should not mute the data that was injected to KSNODETYPE_TELEPHONY_TX. + // If this property is FALSE, unmute cellular Tx data. + m_bTelephonyMuteTx = *bTxMute; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyMuteTx + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CCellularMiniportWaveRT::PowerChangeState +( + _In_ POWER_STATE NewState +) +{ + DPF_ENTER(("[CCellularMiniportWaveRT::PowerChangeState]")); + UNREFERENCED_PARAMETER(NewState); + + /* + Turn on and off cellular specific hardware here, if applicable. + */ + +} // PowerStateChange + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CCellularMiniportWaveRT::QueryDeviceCapabilities +( + _Inout_updates_bytes_(sizeof(DEVICE_CAPABILITIES)) PDEVICE_CAPABILITIES PowerDeviceCaps +) +{ + UNREFERENCED_PARAMETER(PowerDeviceCaps); + + /* + Unused at the miniport layer. Any device capabilities should be reported in CAdapterCommon + */ + + return (STATUS_SUCCESS); +} // QueryDeviceCapabilities + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CCellularMiniportWaveRT::QueryPowerChangeState +( + _In_ POWER_STATE NewStateQuery +) +{ + UNREFERENCED_PARAMETER(NewStateQuery); + + DPF_ENTER(("[CCellularMiniportWaveRT::QueryPowerChangeState]")); + + // if we're in a call state other than disabled, then we need the hardware turned on. + if (TELEPHONY_CALLSTATE_DISABLED != m_TelephonyCallState) + { + return STATUS_RESOURCE_IN_USE; + } + + return STATUS_SUCCESS; +} // QueryPowerChangeState + + diff --git a/audio/sysvad/PhoneAudioSample/CellularWave.h b/audio/sysvad/PhoneAudioSample/CellularWave.h new file mode 100644 index 00000000..1704ff0c --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/CellularWave.h @@ -0,0 +1,126 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellularwave.h + +Abstract: + + Definition of wavert miniport class for cellular + +--*/ + +#include "MinWaveRT.h" + +#ifndef _SYSVAD_CELLULARMINWAVERT_H_ +#define _SYSVAD_CELLULARMINWAVERT_H_ + +NTSTATUS +CreateCellularMiniportWaveRT +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +); + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CCellularMiniportWaveRT +// +class CCellularMiniportWaveRT : + public CMiniportWaveRT, + public IAdapterPowerManagement +{ +private: + ULONG m_TelephonyProviderId; + TELEPHONY_CALLTYPE m_TelephonyCallType; + TELEPHONY_CALLTYPE m_PreviousTelephonyCallType; + TELEPHONY_CALLSTATE m_TelephonyCallState; + BOOL m_bTelephonyHold; + BOOL m_bTelephonyMuteTx; + +public: + DECLARE_STD_UNKNOWN(); + IMP_IAdapterPowerManagement; + + CCellularMiniportWaveRT( + _In_ PUNKNOWN UnknownAdapter, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext + ) + :CMiniportWaveRT(UnknownAdapter, MiniportPair, DeviceContext), + m_TelephonyProviderId(0), + m_TelephonyCallType(TELEPHONY_CALLTYPE_CIRCUITSWITCHED), + m_PreviousTelephonyCallType(TELEPHONY_CALLTYPE_CIRCUITSWITCHED), + m_TelephonyCallState(TELEPHONY_CALLSTATE_DISABLED), + m_bTelephonyHold(FALSE), + m_bTelephonyMuteTx(FALSE) + { + if (IsCellularDevice()) + { + if (m_DeviceFlags & ENDPOINT_CELLULAR_PROVIDER1) + { + m_TelephonyProviderId = 0; + } + else if (m_DeviceFlags & ENDPOINT_CELLULAR_PROVIDER2) + { + m_TelephonyProviderId = 1; + } + } + } + + ~CCellularMiniportWaveRT(); + + friend NTSTATUS PropertyHandler_CellularWaveFilter + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +public: + NTSTATUS PropertyHandler_TelephonyProviderId + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_TelephonyCallInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_TelephonyCallState + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_TelephonyCallHold + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_TelephonyMuteTx + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +private: + NTSTATUS UpdateTopologyJackState + ( + _In_ ULONG TelephonyId, + _In_ BOOL NewState + ); +}; +typedef CCellularMiniportWaveRT *PCCellularMiniportWaveRT; + +#endif // _SYSVAD_CELLULARMINWAVERT_H_ + diff --git a/audio/sysvad/PhoneAudioSample/Cellulartopo.cpp b/audio/sysvad/PhoneAudioSample/Cellulartopo.cpp new file mode 100644 index 00000000..250a03ba --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/Cellulartopo.cpp @@ -0,0 +1,973 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellulartopo.cpp + +Abstract: + + Implementation of topology miniport for the cellular endpoint. + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "cellulartopo.h" +#include "cellulartoptable.h" +#include <ntstrsafe.h> + + +#pragma code_seg("PAGE") + + +// table of valid render/capture combinations for cellular routing. +// speaker and mic (speakerphone) +// headset speaker and headset mic (wired headset) +// headset speaker and mic (wired headset without microphone) +// handset speaker and handset mic (handset) +// HFP speaker and HFP mic (bluetooth hands free) +// special host routing pair +KSTOPOLOGY_ENDPOINTIDPAIR g_EndpointList[] = +{ + { HANDSET_SPEAKER_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, HANDSET_MIC_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { SPEAKER_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, MIC_ARRAY1_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { SPEAKER_HEADSET_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, MIC_HEADSET_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { SPEAKER_HEADSET_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, MIC_ARRAY1_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { HFP_SPEAKER_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, HFP_MIC_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { HOSTRENDER_TOPONAME, HOSTRENDER_PIN, HOSTCAPTURE_TOPONAME, HOSTCAPTURE_PIN } +}; + +//============================================================================= +NTSTATUS +CreateCellularMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CCellularMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CCellularMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} // CreateCellularMiniportTopology + +CCellularMiniportTopology::CCellularMiniportTopology +( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels +) +: CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels), + m_CellularVolume(0) +{ + PAGED_CODE(); + + memcpy(&m_CellularRenderEndpoint, &(g_EndpointList[0].RenderEndpoint), sizeof(m_CellularRenderEndpoint)); + memcpy(&m_CellularCaptureEndpoint, &(g_EndpointList[0].CaptureEndpoint), sizeof(m_CellularCaptureEndpoint)); +} + + +//============================================================================= +CCellularMiniportTopology::~CCellularMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportTopology::~CCellularMiniportTopology]")); +} // ~CCellularMiniportTopology + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CCellularMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CCellularMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CCellularMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CCellularMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_ICellularTopology)) + { + *Object = PVOID(PCELLULARTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + ULONG cJackDescriptions = ARRAYSIZE(CellularJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = CellularJackDescriptions; + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(CellularJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = CellularJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::PropertyHandlerTelephonyEndpointIdPair +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_TelephonyTopology, KSPROPERTY_TELEPHONY_ENDPOINTIDPAIR ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerTelephonyEndpointIdPair]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = STATUS_INVALID_PARAMETER; + ULONG ulExpectedSize = (sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(g_EndpointList)); + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_SET |KSPROPERTY_TYPE_GET; + PropDesc->DescriptionSize = ulExpectedSize; + PropDesc->PropTypeSet.Set = KSPROPSETID_TelephonyTopology; + PropDesc->PropTypeSet.Id = KSPROPERTY_TELEPHONY_ENDPOINTIDPAIR; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // buffer is big enough to hold the full data, add that + if (PropertyRequest->ValueSize >= ulExpectedSize) + { + PKSPROPERTY_MEMBERSHEADER MembersHeader = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + MembersHeader->MembersFlags = KSPROPERTY_MEMBER_VALUES; + MembersHeader->MembersSize = sizeof(KSTOPOLOGY_ENDPOINTIDPAIR); + MembersHeader->MembersCount = SIZEOF_ARRAY(g_EndpointList); + MembersHeader->Flags = 0; + + memcpy(MembersHeader + 1, g_EndpointList, sizeof(g_EndpointList)); + + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = ulExpectedSize; + } + else + { + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags. + *(PULONG(PropertyRequest->Value)) = KSPROPERTY_TYPE_SET |KSPROPERTY_TYPE_GET; + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = ulExpectedSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + else + { + ULONG cbNeeded = sizeof(KSTOPOLOGY_ENDPOINTIDPAIR); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSTOPOLOGY_ENDPOINTIDPAIR EndpointIdPair = static_cast<PKSTOPOLOGY_ENDPOINTIDPAIR>(PropertyRequest->Value); + + // Return endpoints (render and capture) info where cellular audio is being routed to. + memcpy(&(EndpointIdPair->RenderEndpoint), &m_CellularRenderEndpoint, sizeof(m_CellularRenderEndpoint)); + memcpy(&(EndpointIdPair->CaptureEndpoint), &m_CellularCaptureEndpoint, sizeof(m_CellularCaptureEndpoint)); + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + PKSTOPOLOGY_ENDPOINTIDPAIR EndpointIdPair = static_cast<PKSTOPOLOGY_ENDPOINTIDPAIR>(PropertyRequest->Value); + + // verify the requested combination is in the list of valid endpoint combinations, fail with + // invalid parameter if not found. + ntStatus = STATUS_INVALID_PARAMETER; + for (int i = 0; i < SIZEOF_ARRAY(g_EndpointList); i++) + { + if (g_EndpointList[i].RenderEndpoint.PinId == EndpointIdPair->RenderEndpoint.PinId && + 0 == _wcsnicmp(g_EndpointList[i].RenderEndpoint.TopologyName, EndpointIdPair->RenderEndpoint.TopologyName, MAX_PATH) && + g_EndpointList[i].CaptureEndpoint.PinId == EndpointIdPair->CaptureEndpoint.PinId && + 0 == _wcsnicmp(g_EndpointList[i].CaptureEndpoint.TopologyName, EndpointIdPair->CaptureEndpoint.TopologyName, MAX_PATH)) + { + if (HOSTRENDER_PIN == EndpointIdPair->RenderEndpoint.PinId && + 0 == _wcsnicmp(HOSTRENDER_TOPONAME, EndpointIdPair->RenderEndpoint.TopologyName, MAX_PATH) && + HOSTCAPTURE_PIN == EndpointIdPair->CaptureEndpoint.PinId && + 0 == _wcsnicmp(HOSTCAPTURE_TOPONAME, EndpointIdPair->CaptureEndpoint.TopologyName, MAX_PATH)) + { + // This is an indication that cellular audio is being routed through OS (For example, user plugs in USB headset + // for cellular audio) and driver must stop rendering cellular audio to any physical audio endpoint. Driver must + // also stop capturing data from any physical audio endpoint for cellular. This will update audio routing for + // all the active cellular calls. + } + else + { + // Route cellular audio to these new endpoints (render and capture). This will update audio routing for + // all the active cellular calls. + } + + memcpy(&m_CellularRenderEndpoint, &(EndpointIdPair->RenderEndpoint), sizeof(m_CellularRenderEndpoint)); + memcpy(&m_CellularCaptureEndpoint, &(EndpointIdPair->CaptureEndpoint), sizeof(m_CellularCaptureEndpoint)); + + // we found the entry, so return success + ntStatus = STATUS_SUCCESS; + break; + } + } + + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::PropertyHandlerTelephonyVolume +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_TelephonyTopology, KSPROPERTY_TELEPHONY_VOLUME ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerTelephonyVolume]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupportVolume + ( + PropertyRequest, + 1 // Telephony audio is only ever 1 channel + ); + + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG); + + if (NT_SUCCESS(ntStatus) && PropertyRequest->ValueSize >= cbFullProperty) + { + // Access proper values + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + // Telephony volume is only ever single channel, so the + // MULTICHANNEL and UNIFORM flags are meaningless + Members->Flags = 0; + + // Telephony Values that may be different from standard volume values + Range->Bounds.SignedMaximum = TELEPHONY_VOLUME_MAXIMUM; + Range->Bounds.SignedMinimum = TELEPHONY_VOLUME_MINIMUM; + Range->SteppingDelta = TELEPHONY_VOLUME_STEPPING; + } + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // volume value is a LONG + 0 // there is no channel associated with telephony volume + ); + + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + LONG* volume = (LONG*)PropertyRequest->Value; + // Return current cellular volume (Incall volume). + *volume = m_CellularVolume; + PropertyRequest->ValueSize = sizeof(LONG); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + LONG* volume = (LONG*)PropertyRequest->Value; + + LONG normalizedVolume = + VALUE_NORMALIZE_IN_RANGE_EX + ( + *volume, + TELEPHONY_VOLUME_MINIMUM, + TELEPHONY_VOLUME_MAXIMUM, + TELEPHONY_VOLUME_STEPPING + ); + if (normalizedVolume == *volume) + { + // Update the cellular volume (Incall volume). This will update cellular volume for all the active cellular calls. + m_CellularVolume = *volume; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_CellularTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_CellularTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCCellularMiniportTopology pMiniport = (PCCellularMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_TelephonyTopology)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_ENDPOINTIDPAIR) + { + ntStatus = pMiniport->PropertyHandlerTelephonyEndpointIdPair(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_VOLUME) + { + ntStatus = pMiniport->PropertyHandlerTelephonyVolume(PropertyRequest); + } + } + + + return ntStatus; +} // PropertyHandler_CellularTopoFilter + +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CCellularMiniportTopology::UpdateTopologyJackState +( + _In_ ULONG TelephonyId, + _In_ BOOL NewState +) +{ + PAGED_CODE (); + + struct JackState + { + ULONG TelephonyId; + ULONG PinId; + PKSJACK_DESCRIPTION JackControl; + }; + + JackState Jacks[] = + { + { + 0, + KSPIN_TOPO_BIDI1, + &CellularJackDesc + }, + { + 1, + KSPIN_TOPO_BIDI2, + &CellularJackDesc2 + } + }; + + // Query the registry and update the jack state for all of the pins. + for (int i = 0; i < SIZEOF_ARRAY(Jacks); i++) + { + if (Jacks[i].TelephonyId == TelephonyId) + { + Jacks[i].JackControl->IsConnected = NewState; + + // we have multiple pins which share the same jack state, + // notify for every pin using that jack state + GenerateEventList( + (GUID*)&KSEVENTSETID_PinCapsChange, // event set. NULL is a wild card for all events. + KSEVENT_PINCAPS_JACKINFOCHANGE, // event ID. + TRUE, // use pid ID. + Jacks[i].PinId, // pin ID. + FALSE, // do not use node ID. + ULONG(-1)); // node ID, not used. + } + } + + return STATUS_SUCCESS; +} + +#pragma code_seg() +NTSTATUS CCellularMiniportWaveRT_EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + CCellularMiniportTopology* miniport = reinterpret_cast<CCellularMiniportTopology*>(EventRequest->MajorTarget); + return miniport->EventHandler_JackState(EventRequest); +} + +NTSTATUS CCellularMiniportTopology::EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + if (EventRequest->Verb == PCEVENT_VERB_ADD) + { + m_PortEvents->AddEventToEventList(EventRequest->EventEntry); + } + + return STATUS_SUCCESS; +} + + diff --git a/audio/sysvad/PhoneAudioSample/Cellulartopo.h b/audio/sysvad/PhoneAudioSample/Cellulartopo.h new file mode 100644 index 00000000..bcd4aba7 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/Cellulartopo.h @@ -0,0 +1,159 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellulartopo.h + +Abstract: + + Declaration of topology miniport for the cellular endpoint. + +--*/ + +#ifndef _SYSVAD_CELLULARTOPO_H_ +#define _SYSVAD_CELLULARTOPO_H_ + +#include "basetopo.h" + +// make sure all of these names matches with KSNAME_* in the inf's [Strings] section +#define SPEAKER_TOPONAME L"TopologySpeaker" +#define SPEAKER_HEADSET_TOPONAME L"TopologySpeakerHeadset" +#define MIC_HEADSET_TOPONAME L"TopologyMicHeadset" +#define MIC_ARRAY1_TOPONAME L"TopologyMicArray1" +#define HANDSET_SPEAKER_TOPONAME L"TopologyHandsetSpeaker" +#define HANDSET_MIC_TOPONAME L"TopologyHandsetMic" + +// from bthhfpminipairs.h, static name of bluetooth speaker and mic topologies +#define HFP_SPEAKER_TOPONAME L"TopologyBthHfpSpeaker" +#define HFP_MIC_TOPONAME L"TopologyBthHfpMic" + +// Special routing endpoints +#define HOSTRENDER_TOPONAME L"HostRender" +#define HOSTRENDER_PIN 0 +#define HOSTCAPTURE_TOPONAME L"HostCapture" +#define HOSTCAPTURE_PIN 0 + +// Telephony Volume Stepping information +#define TELEPHONY_VOLUME_MAXIMUM 0 // 0 dB +#define TELEPHONY_VOLUME_MINIMUM (-30 * 0x10000) // -30 dB +#define TELEPHONY_VOLUME_STEPPING (3 * 0x10000) // 10 steps + + +//============================================================================= +// Defines +//============================================================================= +// {3829F9C3-F341-4B82-976F-A635EB3CEA0D} +DEFINE_GUID(IID_ICellularTopology, +0x3829f9c3, 0xf341, 0x4b82, 0x97, 0x6f, 0xa6, 0x35, 0xeb, 0x3c, 0xea, 0xd); + + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// ICellularTopology +// +DECLARE_INTERFACE_(ICellularTopology, IMiniportTopology) +{ + STDMETHOD_(NTSTATUS, UpdateTopologyJackState) + ( + THIS_ + _In_ ULONG TelephonyId, + _In_ BOOL NewState + ) PURE; +}; + +typedef ICellularTopology *PCELLULARTOPOLOGY; + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CCellularMiniportTopology +// + +class CCellularMiniportTopology : + public CMiniportTopologySYSVAD, + public ICellularTopology, + public CUnknown +{ + private: + KSTOPOLOGY_ENDPOINTID m_CellularRenderEndpoint; + KSTOPOLOGY_ENDPOINTID m_CellularCaptureEndpoint; + ULONG m_CellularVolume; + + public: + DECLARE_STD_UNKNOWN(); + CCellularMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ); + + ~CCellularMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackSinkInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerTelephonyEndpointIdPair + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerTelephonyVolume + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS EventHandler_JackState + ( + _In_ PPCEVENT_REQUEST _pEventRequest + ); + + STDMETHODIMP_(NTSTATUS) UpdateTopologyJackState + ( + _In_ ULONG TelephonyId, + _In_ BOOL NewState + ); +}; + +typedef CCellularMiniportTopology *PCCellularMiniportTopology; + +NTSTATUS +CreateCellularMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_CellularTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_CELLULARTOPO_H_ + diff --git a/audio/sysvad/PhoneAudioSample/Cellulartoptable.h b/audio/sysvad/PhoneAudioSample/Cellulartoptable.h new file mode 100644 index 00000000..9b6766ab --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/Cellulartoptable.h @@ -0,0 +1,255 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellulartoptable.h + +Abstract: + + Declaration of topology tables for the cellular endpoint. + +--*/ + +#ifndef _SYSVAD_CELLULARTOPTABLE_H_ +#define _SYSVAD_CELLULARTOPTABLE_H_ + +// {896BC350-BF57-4039-A6B6-64DCEC3AEEB8} +DEFINE_GUID(CELLULARBIDI1_CUSTOM_NAME, +0x896bc350, 0xbf57, 0x4039, 0xa6, 0xb6, 0x64, 0xdc, 0xec, 0x3a, 0xee, 0xb8); + +// {80CC64DA-166A-4A2C-ADF7-1464C49D3B0E} +DEFINE_GUID(CELLULARBIDI2_CUSTOM_NAME, +0x80cc64da, 0x166a, 0x4a2c, 0xad, 0xf7, 0x14, 0x64, 0xc4, 0x9d, 0x3b, 0xe); + +//============================================================================= +static +KSDATARANGE CellularTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE CellularTopoPinDataRangePointersBridge[] = +{ + &CellularTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR CellularTopoMiniportPins[] = +{ + // KSPIN_TOPO_BIDI1 + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(CellularTopoPinDataRangePointersBridge), // DataRangesCount + CellularTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_TELEPHONY_BIDI, // Category + &CELLULARBIDI1_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_BIDI1_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(CellularTopoPinDataRangePointersBridge), // DataRangesCount + CellularTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_BIDI2 + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(CellularTopoPinDataRangePointersBridge), // DataRangesCount + CellularTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_TELEPHONY_BIDI, // Category + &CELLULARBIDI2_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_BIDI2_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(CellularTopoPinDataRangePointersBridge), // DataRangesCount + CellularTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION CellularJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0x0000, // no color + eConnTypeUnknown, + eGeoLocNotApplicable, + eGenLocInternal, + ePortConnUnknown, + TRUE // NOTE: cellular jacks should be "unplugged" at boot, and only changed to active during a call. + // However, for convienience during development, the jack state is set to active at boot for this milestone. +}; + +static +KSJACK_DESCRIPTION CellularJackDesc2 = +{ + KSAUDIO_SPEAKER_MONO, + 0x0000, // no color + eConnTypeUnknown, + eGeoLocNotApplicable, + eGenLocInternal, + ePortConnUnknown, + TRUE // NOTE: cellular jacks should be "unplugged" at boot, and only changed to active during a call. + // However, for convienience during development, the jack state is set to active at boot for this milestone. +}; + + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION CellularJackDescriptions[] = +{ + &CellularJackDesc, + NULL, + &CellularJackDesc2, + NULL +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR CellularTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_BIDI1, PCFILTER_NODE, KSPIN_TOPO_BIDI1_BRIDGE}, + { PCFILTER_NODE, KSPIN_TOPO_BIDI2, PCFILTER_NODE, KSPIN_TOPO_BIDI2_BRIDGE} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesCellularTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularTopoFilter + }, + { + &KSPROPSETID_TelephonyTopology, + KSPROPERTY_TELEPHONY_ENDPOINTIDPAIR, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularTopoFilter + }, + { + &KSPROPSETID_TelephonyTopology, + KSPROPERTY_TELEPHONY_VOLUME, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularTopoFilter + } +}; + +NTSTATUS CCellularMiniportWaveRT_EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +); + +static PCEVENT_ITEM JackInfoChangeEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed + KSEVENT_PINCAPS_JACKINFOCHANGE, // Jack Info Changes + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + CCellularMiniportWaveRT_EventHandler_JackState + } +}; + +/***************************************************************************** + * PropertiesTopologyFilter + ***************************************************************************** + * Automation table for jack descripton/detection. + */ +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationCellularTopoFilter, PropertiesCellularTopoFilter, JackInfoChangeEvent); + + +//============================================================================= +static +PCFILTER_DESCRIPTOR CellularTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationCellularTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(CellularTopoMiniportPins), // PinCount + CellularTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(CellularTopoMiniportConnections), // ConnectionCount + CellularTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_CELLULARTOPTABLE_H_ + + diff --git a/audio/sysvad/PhoneAudioSample/Cellularwavtable.h b/audio/sysvad/PhoneAudioSample/Cellularwavtable.h new file mode 100644 index 00000000..aeb91e74 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/Cellularwavtable.h @@ -0,0 +1,286 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellularwavtable.h + +Abstract: + + Declaration of wave miniport tables for the cellular endpoint. + +--*/ + +#ifndef _SYSVAD_CELLULARWAVTABLE_H_ +#define _SYSVAD_CELLULARWAVTABLE_H_ + + +#define CELLULAR_DEVICE_MAX_CHANNELS 1 // Max Channels. + +#define CELLULAR_CAPTURE_MAX_CHANNELS 1 // Max Channels. +#define CELLULAR_CAPTURE_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define CELLULAR_CAPTURE_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define CELLULAR_CAPTURE_MIN_SAMPLE_RATE 16000 // Min Sample Rate +#define CELLULAR_CAPTURE_MAX_SAMPLE_RATE 16000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define CELLULAR_MAX_INPUT_SYSTEM_STREAMS 1 // Raw stream + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE CellularCapturePinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT CellularCapturePinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &CellularCapturePinSupportedDeviceFormats[SIZEOF_ARRAY(CellularCapturePinSupportedDeviceFormats) - 1].DataFormat + } +}; + +//============================================================================= +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES CellularPinDeviceFormatsAndModes[] = +{ + { // BIDI + TelephonyBidiPin, + CellularCapturePinSupportedDeviceFormats, + SIZEOF_ARRAY(CellularCapturePinSupportedDeviceFormats), + CellularCapturePinSupportedDeviceModes, + SIZEOF_ARRAY(CellularCapturePinSupportedDeviceModes) + }, + { + BridgePin, + NULL, + 0, + NULL, + 0, + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO CellularPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + CELLULAR_CAPTURE_MAX_CHANNELS, + CELLULAR_CAPTURE_MIN_BITS_PER_SAMPLE, + CELLULAR_CAPTURE_MAX_BITS_PER_SAMPLE, + CELLULAR_CAPTURE_MIN_SAMPLE_RATE, + CELLULAR_CAPTURE_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE CellularPinDataRangePointersBiDiStream[] = +{ + PKSDATARANGE(&CellularPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +//============================================================================= +static +KSDATARANGE CellularPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE CellularPinDataRangePointersBridge[] = +{ + &CellularPinDataRangesBridge[0] +}; + + +//============================================================================= +static +PCPIN_DESCRIPTOR CellularWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_BIDI + { + CELLULAR_MAX_INPUT_SYSTEM_STREAMS, + CELLULAR_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(CellularPinDataRangePointersBiDiStream), + CellularPinDataRangePointersBiDiStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_BIDI_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(CellularPinDataRangePointersBridge), + CellularPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + } +}; + +//============================================================================= +// +// Note: +// +// ----- +// | | +// BiDi pin 1 <--| |<-- 0 BiDi bridge pin +// ----- + +static +PCCONNECTION_DESCRIPTOR CellularWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BIDI_BRIDGE, PCFILTER_NODE, KSPIN_WAVE_BIDI } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesCellularWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_PROVIDERID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_CALLINFO, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_CALLCONTROL, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_PROVIDERCHANGE, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_CALLHOLD, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_MUTE_TX, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationCellularWaveFilter, PropertiesCellularWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR CellularWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationCellularWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(CellularWaveMiniportPins), // PinCount + CellularWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(CellularWaveMiniportConnections), // ConnectionCount + CellularWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_CELLULARWAVTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/FMTopTable.h b/audio/sysvad/PhoneAudioSample/FMTopTable.h new file mode 100644 index 00000000..ae8ddf9e --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMTopTable.h @@ -0,0 +1,199 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmtoptable.h + +Abstract: + + Declaration of topology tables for the fm endpoint. + +--*/ + +#ifndef _SYSVAD_FMTOPTABLE_H_ +#define _SYSVAD_FMTOPTABLE_H_ + +// {344EC7D7-F209-4FC4-B6C1-B324E8309E8D} +DEFINE_GUID(FMRX_CUSTOM_NAME, + 0x344ec7d7, 0xf209, 0x4fc4, 0xb6, 0xc1, 0xb3, 0x24, 0xe8, 0x30, 0x9e, 0x8d); + +//============================================================================= +static +KSDATARANGE FmTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE FmTopoPinDataRangePointersBridge[] = +{ + &FmTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR FmTopoMiniportPins[] = +{ + // KSPIN_TOPO_FMRX + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(FmTopoPinDataRangePointersBridge), // DataRangesCount + FmTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_FM_RX, // Category + &FMRX_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_FMRX_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(FmTopoPinDataRangePointersBridge), // DataRangesCount + FmTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, +}; + +//============================================================================= +static +KSJACK_DESCRIPTION FmRxJackDesc = +{ + KSAUDIO_SPEAKER_STEREO, + 0x0000, // no color + eConnTypeOtherAnalog, + eGeoLocNotApplicable, + eGenLocInternal, + ePortConnUnknown, + FALSE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION FmJackDescriptions[] = +{ + &FmRxJackDesc, + NULL, +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR FmTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_FMRX, PCFILTER_NODE, KSPIN_TOPO_FMRX_BRIDGE}, +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesFmTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, + { + &KSPROPSETID_FMRXTopology, + KSPROPERTY_FMRX_ENDPOINTID, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, + { + &KSPROPSETID_FMRXTopology, + KSPROPERTY_FMRX_VOLUME, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, + { + &KSPROPSETID_FMRXTopology, + KSPROPERTY_FMRX_ANTENNAENDPOINTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, +}; + +NTSTATUS CFmMiniportWaveRT_EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +); + +static PCEVENT_ITEM FmRxJackInfoChangeEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed + KSEVENT_PINCAPS_JACKINFOCHANGE, // Jack Info Changes + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + CFmMiniportWaveRT_EventHandler_JackState + } +}; + +/***************************************************************************** + * PropertiesTopologyFilter + ***************************************************************************** + * Automation table for jack descripton/detection. + */ +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationFmTopoFilter, PropertiesFmTopoFilter, FmRxJackInfoChangeEvent); + + +//============================================================================= +static +PCFILTER_DESCRIPTOR FmTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationFmTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(FmTopoMiniportPins), // PinCount + FmTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(FmTopoMiniportConnections), // ConnectionCount + FmTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_FMTOPTABLE_H_ + + diff --git a/audio/sysvad/PhoneAudioSample/FMTopo.cpp b/audio/sysvad/PhoneAudioSample/FMTopo.cpp new file mode 100644 index 00000000..9c43168f --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMTopo.cpp @@ -0,0 +1,985 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmtopo.cpp + +Abstract: + + Implementation of topology miniport for the fm endpoint. + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "fmtopo.h" +#include "fmtoptable.h" +#include <ntstrsafe.h> + + +#pragma code_seg("PAGE") + +// Table of valid render endpoints for FM +KSTOPOLOGY_ENDPOINTID g_EndpointList[] = +{ + { SPEAKER_TOPONAME, KSPIN_TOPO_LINEOUT_DEST }, + { SPEAKER_HEADSET_TOPONAME, KSPIN_TOPO_LINEOUT_DEST }, + { HOSTRENDER_TOPONAME, HOSTRENDER_PIN } +}; + +KSTOPOLOGY_ENDPOINTID g_AntennaEndpoint = +{ + SPEAKER_HEADSET_TOPONAME, KSPIN_TOPO_LINEOUT_DEST +}; + +//============================================================================= +NTSTATUS +CreateFmMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CFmMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CFmMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} // CreateFmMiniportTopology + +CFmMiniportTopology::CFmMiniportTopology +( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels +) +: CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels), + m_FmRxVolume(FMRX_VOLUME_MAXIMUM) +{ + PAGED_CODE(); + + memcpy(&m_FmRxTargetEndpoint, &g_EndpointList[0], sizeof(m_FmRxTargetEndpoint)); +} + + +//============================================================================= +CFmMiniportTopology::~CFmMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CFmMiniportTopology::~CFmMiniportTopology]")); +} // ~CFmMiniportTopology + +//============================================================================= +NTSTATUS +CFmMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CFmMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CFmMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CFmMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CFmMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IFmTopology)) + { + *Object = PVOID(PFMTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + ULONG cJackDescriptions = ARRAYSIZE(FmJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = FmJackDescriptions; + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerJackDescription2 +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + +PropertyRequest - + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(FmJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = FmJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM)+sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI + 1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerFmRxEndpointId +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_FMRXTopology, KSPROPERTY_FMRX_ENDPOINTID ) + +Arguments: + +PropertyRequest + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerFmRxEndpointId]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = STATUS_INVALID_PARAMETER; + ULONG ulExpectedSize = (sizeof(KSPROPERTY_DESCRIPTION)+sizeof(KSPROPERTY_MEMBERSHEADER)+sizeof(g_EndpointList)); + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET; + PropDesc->DescriptionSize = ulExpectedSize; + PropDesc->PropTypeSet.Set = KSPROPSETID_FMRXTopology; + PropDesc->PropTypeSet.Id = KSPROPERTY_FMRX_ENDPOINTID; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // buffer is big enough to hold the full data, add that + if (PropertyRequest->ValueSize >= ulExpectedSize) + { + PKSPROPERTY_MEMBERSHEADER MembersHeader = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + MembersHeader->MembersFlags = KSPROPERTY_MEMBER_VALUES; + MembersHeader->MembersSize = sizeof(KSTOPOLOGY_ENDPOINTID); + MembersHeader->MembersCount = SIZEOF_ARRAY(g_EndpointList); + MembersHeader->Flags = 0; + + memcpy(MembersHeader + 1, g_EndpointList, sizeof(g_EndpointList)); + + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = ulExpectedSize; + } + else + { + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags. + *(PULONG(PropertyRequest->Value)) = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET; + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = ulExpectedSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(KSTOPOLOGY_ENDPOINTID), + 0 + ); + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSTOPOLOGY_ENDPOINTID EndpointId = static_cast<PKSTOPOLOGY_ENDPOINTID>(PropertyRequest->Value); + + memcpy(EndpointId, &m_FmRxTargetEndpoint, sizeof(m_FmRxTargetEndpoint)); + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + PKSTOPOLOGY_ENDPOINTID EndpointId = static_cast<PKSTOPOLOGY_ENDPOINTID>(PropertyRequest->Value); + + ntStatus = STATUS_INVALID_PARAMETER; + for (int i = 0; i < SIZEOF_ARRAY(g_EndpointList); i++) + { + if (g_EndpointList[i].PinId == EndpointId->PinId && + 0 == _wcsnicmp(g_EndpointList[i].TopologyName, EndpointId->TopologyName, MAX_PATH)) + { + memcpy(&m_FmRxTargetEndpoint, EndpointId, sizeof(m_FmRxTargetEndpoint)); + + if (HOSTRENDER_PIN == EndpointId->PinId && + 0 == _wcsnicmp(HOSTRENDER_TOPONAME, EndpointId->TopologyName, MAX_PATH)) + { + // This is an indication that FM audio is being routed through OS (For example, user plugs in + // USB headset for FM audio) and driver must stop rendering FM audio to any physical audio endpoint. + } + else + { + // Route FM audio to this new endpoint. + } + ntStatus = STATUS_SUCCESS; + break; + } + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerFmRxVolume +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_FMRXTopology, KSPROPERTY_FMRX_VOLUME ) + +Arguments: + +PropertyRequest + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerFmRxVolume]")); + + UNREFERENCED_PARAMETER(PropertyRequest); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupportVolume + ( + PropertyRequest, + 1 // FM audio technically supports Stereo, but we only support a single volume + ); + + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION)+ + sizeof(KSPROPERTY_MEMBERSHEADER)+ + sizeof(KSPROPERTY_STEPPING_LONG); + + if (NT_SUCCESS(ntStatus) && PropertyRequest->ValueSize >= cbFullProperty) + { + // Access proper values + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + Members->Flags = 0; + + Range->Bounds.SignedMaximum = FMRX_VOLUME_MAXIMUM; + Range->Bounds.SignedMinimum = FMRX_VOLUME_MINIMUM; + Range->SteppingDelta = FMRX_VOLUME_STEPPING; + } + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // volume value is a LONG + 0 // No support for channel since this is a property + // on the topo filter, not a volume node. + ); + + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + LONG* volume = (LONG*)PropertyRequest->Value; + // Return current FM volume + *volume = m_FmRxVolume; + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + LONG* volume = (LONG*)PropertyRequest->Value; + + LONG normalizedVolume = + VALUE_NORMALIZE_IN_RANGE_EX + ( + *volume, + FMRX_VOLUME_MINIMUM, + FMRX_VOLUME_MAXIMUM, + FMRX_VOLUME_STEPPING + ); + if (normalizedVolume == *volume) + { + m_FmRxVolume = *volume; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerFmRxAntennaEndpointId +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_FMRXTopology, KSPROPERTY_FMRX_ANTENNAENDPOINTID ) + +Arguments: + +PropertyRequest + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerFmRxAntennaEndpoint]")); + + UNREFERENCED_PARAMETER(PropertyRequest); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(KSTOPOLOGY_ENDPOINTID), + 0 + ); + + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSTOPOLOGY_ENDPOINTID pEndpointId = (PKSTOPOLOGY_ENDPOINTID)PropertyRequest->Value; + memcpy(pEndpointId, &g_AntennaEndpoint, sizeof(*pEndpointId)); + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_FmRxTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_FmRxTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCFmMiniportTopology pMiniport = (PCFmMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_FMRXTopology)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_FMRX_ENDPOINTID) + { + ntStatus = pMiniport->PropertyHandlerFmRxEndpointId(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_FMRX_VOLUME) + { + ntStatus = pMiniport->PropertyHandlerFmRxVolume(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_FMRX_ANTENNAENDPOINTID) + { + ntStatus = pMiniport->PropertyHandlerFmRxAntennaEndpointId(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_FmRxTopoFilter + +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CFmMiniportTopology::UpdateTopologyJackState +( + _In_ BOOL NewState +) +{ + PAGED_CODE (); + + FmRxJackDesc.IsConnected = NewState; + + // we have multiple pins which share the same jack state, + // notify for every pin using that jack state + GenerateEventList( + (GUID*)&KSEVENTSETID_PinCapsChange, // event set. NULL is a wild card for all events. + KSEVENT_PINCAPS_JACKINFOCHANGE, // event ID. + TRUE, // use pid ID. + KSPIN_TOPO_FMRX, // pin ID. + FALSE, // do not use node ID. + ULONG(-1)); // node ID, not used. + + return STATUS_SUCCESS; +} + +#pragma code_seg() +NTSTATUS CFmMiniportWaveRT_EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + CFmMiniportTopology* miniport = reinterpret_cast<CFmMiniportTopology*>(EventRequest->MajorTarget); + return miniport->EventHandler_JackState(EventRequest); +} + +NTSTATUS CFmMiniportTopology::EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + if (EventRequest->Verb == PCEVENT_VERB_ADD) + { + m_PortEvents->AddEventToEventList(EventRequest->EventEntry); + } + + return STATUS_SUCCESS; +} + + diff --git a/audio/sysvad/PhoneAudioSample/FMTopo.h b/audio/sysvad/PhoneAudioSample/FMTopo.h new file mode 100644 index 00000000..ae279bf1 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMTopo.h @@ -0,0 +1,152 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + FMtopo.h + +Abstract: + + Declaration of topology miniport for the fm endpoint. + +--*/ + +#ifndef _SYSVAD_FMTOPO_H_ +#define _SYSVAD_FMTOPO_H_ + +#include "basetopo.h" + +// make sure all of these names matches with KSNAME_* in the inf's [Strings] section +#define SPEAKER_TOPONAME L"TopologySpeaker" +#define SPEAKER_HEADSET_TOPONAME L"TopologySpeakerHeadset" + +// Special routing endpoint +#define HOSTRENDER_TOPONAME L"HostRender" +#define HOSTRENDER_PIN 0 + +// FM Volume Stepping information +#define FMRX_VOLUME_MAXIMUM 0 // 0 dB +#define FMRX_VOLUME_MINIMUM (-96 * 0x10000) // -96 dB +#define FMRX_VOLUME_STEPPING (0x8000) // 0.5 dB steps + + +//============================================================================= +// Defines +//============================================================================= +// {949F42F1-A5F5-4FBD-B4BB-C543ACC24AE0} +DEFINE_GUID(IID_IFmTopology, + 0x949f42f1, 0xa5f5, 0x4fbd, 0xb4, 0xbb, 0xc5, 0x43, 0xac, 0xc2, 0x4a, 0xe0); + + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// IFmTopology +// +DECLARE_INTERFACE_(IFmTopology, IMiniportTopology) +{ + STDMETHOD_(NTSTATUS, UpdateTopologyJackState) + ( + THIS_ + _In_ BOOL NewState + ) PURE; +}; + +typedef IFmTopology *PFMTOPOLOGY; + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CFmMiniportTopology +// + +class CFmMiniportTopology : + public CMiniportTopologySYSVAD, + public IFmTopology, + public CUnknown +{ + private: + KSTOPOLOGY_ENDPOINTID m_FmRxTargetEndpoint; + KSTOPOLOGY_ENDPOINTID m_FmRxAntennaEndpoint; + ULONG m_FmRxVolume; + + public: + DECLARE_STD_UNKNOWN(); + CFmMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ); + + ~CFmMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackSinkInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerFmRxEndpointId + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerFmRxVolume + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerFmRxAntennaEndpointId + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS EventHandler_JackState + ( + _In_ PPCEVENT_REQUEST _pEventRequest + ); + + STDMETHODIMP_(NTSTATUS) UpdateTopologyJackState + ( + _In_ BOOL NewState + ); +}; + +typedef CFmMiniportTopology *PCFmMiniportTopology; + +NTSTATUS +CreateFmMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_FmRxTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_FMTOPO_H_ + diff --git a/audio/sysvad/PhoneAudioSample/FMWavTable.h b/audio/sysvad/PhoneAudioSample/FMWavTable.h new file mode 100644 index 00000000..a549e06d --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMWavTable.h @@ -0,0 +1,249 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmwavtable.h + +Abstract: + + Declaration of wave miniport tables for the fm endpoint. + + Note: the FM in this sample shows how to do h/w loopback + for non-offloaded devices. + +--*/ + +#ifndef _SYSVAD_FMWAVTABLE_H_ +#define _SYSVAD_FMWAVTABLE_H_ + + +#define FMRX_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define FMRX_MAX_CHANNELS 2 // Max Channels. +#define FMRX_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define FMRX_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define FMRX_MIN_SAMPLE_RATE 48000 // Min Sample Rate +#define FMRX_MAX_SAMPLE_RATE 48000 // Max Sample Rate +#define FMRX_MAX_INPUT_SYSTEM_STREAMS 1 // Raw stream + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE FmRxPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT FmRxPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &FmRxPinSupportedDeviceFormats[SIZEOF_ARRAY(FmRxPinSupportedDeviceFormats) - 1].DataFormat + } +}; + +//============================================================================= +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES FmRxPinDeviceFormatsAndModes[] = +{ + { // RX + SystemCapturePin, + FmRxPinSupportedDeviceFormats, + SIZEOF_ARRAY(FmRxPinSupportedDeviceFormats), + FmRxPinSupportedDeviceModes, + SIZEOF_ARRAY(FmRxPinSupportedDeviceModes) + }, + { + BridgePin, + NULL, + 0, + NULL, + 0, + }, +}; + +//============================================================================= +static +KSDATARANGE_AUDIO FmPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + FMRX_MAX_CHANNELS, + FMRX_MIN_BITS_PER_SAMPLE, + FMRX_MAX_BITS_PER_SAMPLE, + FMRX_MIN_SAMPLE_RATE, + FMRX_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE FmPinDataRangePointersStream[] = +{ + PKSDATARANGE(&FmPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +//============================================================================= +static +KSDATARANGE FmPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE FmPinDataRangePointersBridge[] = +{ + &FmPinDataRangesBridge[0] +}; + + +//============================================================================= +static +PCPIN_DESCRIPTOR FmWaveMiniportPins[] = +{ + // Wave In Streaming Pin KSPIN_WAVE_FMRX + { + FMRX_MAX_INPUT_SYSTEM_STREAMS, + FMRX_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(FmPinDataRangePointersStream), + FmPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave In Bridge Pin KSPIN_WAVE_FMRX_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(FmPinDataRangePointersBridge), + FmPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +// +// ----- +// | | +// FM RX bridge pin 1 -->| |--> 0 FM RX pin +// | | +// ----- + +static +PCCONNECTION_DESCRIPTOR FmWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_FMRX_BRIDGE, PCFILTER_NODE, KSPIN_WAVE_FMRX }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesFmWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + + { + &KSPROPSETID_FMRXControl, + KSPROPERTY_FMRX_STATE, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationFmWaveFilter, PropertiesFmWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR FmWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationFmWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(FmWaveMiniportPins), // PinCount + FmWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(FmWaveMiniportConnections), // ConnectionCount + FmWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_FMWAVTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/FMWave.cpp b/audio/sysvad/PhoneAudioSample/FMWave.cpp new file mode 100644 index 00000000..4d571f57 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMWave.cpp @@ -0,0 +1,389 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmwave.cpp + +Abstract: + + Implementation of wavert miniport for fm + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "FmWave.h" +#include "FmTopo.h" +#include "FmToptable.h" + +//============================================================================= +// CFmMiniportWaveRT +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateFmMiniportWaveRT +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Create the wavert miniport. + +Arguments: + + Unknown - + + RefClsId - + + UnknownOuter - + + PoolType - + + UnkownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(UnknownOuter); + + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + CMiniportWaveRT *obj = new (PoolType, MINWAVERT_POOLTAG) CFmMiniportWaveRT + ( + UnknownAdapter, + MiniportPair, + DeviceContext + ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +CFmMiniportWaveRT::~CFmMiniportWaveRT +( + void +) +/*++ + +Routine Description: + + Destructor for wavert miniport + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CFmMiniportWaveRT::~CFmMiniportWaveRT]")); + +} // ~CFmMiniportWaveRT + + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CFmMiniportWaveRT::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for CFmMiniportWaveRT + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IAdapterPowerManagement)) + { + *Object = PVOID(PADAPTERPOWERMANAGEMENT(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return CMiniportWaveRT::NonDelegatingQueryInterface(Interface, Object); +} + +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_FmRxWaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CFmMiniportWaveRT* pWaveHelper = reinterpret_cast<CFmMiniportWaveRT*>(PropertyRequest->MajorTarget); + + if (pWaveHelper == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + pWaveHelper->AddRef(); + + if (pWaveHelper->m_DeviceType == eFmRxDevice && + IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_FMRXControl)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_FMRX_STATE: + ntStatus = pWaveHelper->PropertyHandler_FmRxState(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandler_FmRxWaveFilter: Invalid Device Request")); + } + } + else + { + ntStatus = PropertyHandler_WaveFilter(PropertyRequest); + } + + pWaveHelper->Release(); + + return ntStatus; +} // PropertyHandler_FmRxWaveFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CFmMiniportWaveRT::PropertyHandler_FmRxState +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CFmMiniportWaveRT::PropertyHandler_FmRxState]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(BOOL), + 0 + ); + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + BOOL* fmrxState = (BOOL*)PropertyRequest->Value; + *fmrxState = m_bFmRxState; + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + BOOL fmrxNewState = *(BOOL*)PropertyRequest->Value; + if (fmrxNewState == m_bFmRxState) + { + ntStatus = STATUS_SUCCESS; + } + else + { + // Turn on FM hardware + m_bFmRxState = fmrxNewState; + + // NOTE: Set the Jack state to Active ONLY if the driver supports + // recording FM data. If the driver doesn't support recording FM + // data, the jack state should never be changed to Active + ntStatus = UpdateTopologyJackState(m_bFmRxState); + + // When we're using FM, we need idle power management disabled. + // passing true here enables power management, false disables. + // when FM is enabled (true), we want to pass false do disable idle + // power management + m_pAdapterCommon->SetIdlePowerManagement(m_pMiniportPair, m_bFmRxState?FALSE:TRUE); + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_FmRxState + + + +#pragma code_seg("PAGE") +NTSTATUS +CFmMiniportWaveRT::UpdateTopologyJackState +( + _In_ BOOL NewState +) +{ + PAGED_CODE (); + NTSTATUS ntStatus = STATUS_SUCCESS; + IUnknown *pUnknown = NULL; + IFmTopology *pFmTopology = NULL; + + // GetFilters returns the topology port, then topology miniport, then wave port, then wave miniport. + // we only need the topology miniport. + ntStatus = m_pAdapterCommon->GetFilters(m_pMiniportPair, NULL, &pUnknown, NULL, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pUnknown->QueryInterface(IID_IFmTopology, (PVOID *)&pFmTopology); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pFmTopology->UpdateTopologyJackState(NewState); + } + } + + SAFE_RELEASE(pFmTopology); + SAFE_RELEASE(pUnknown); + + return STATUS_SUCCESS; +} +#pragma code_seg() + + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CFmMiniportWaveRT::PowerChangeState +( + _In_ POWER_STATE NewState +) +{ + DPF_ENTER(("[CFmMiniportWaveRT::PowerChangeState]")); + UNREFERENCED_PARAMETER(NewState); + + /* + Turn on and off fm specific hardware here, if applicable. + */ + +} // PowerStateChange + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CFmMiniportWaveRT::QueryDeviceCapabilities +( + _Inout_updates_bytes_(sizeof(DEVICE_CAPABILITIES)) PDEVICE_CAPABILITIES PowerDeviceCaps +) +{ + UNREFERENCED_PARAMETER(PowerDeviceCaps); + + /* + Unused at the miniport layer. Any device capabilities should be reported in CAdapterCommon + */ + + return (STATUS_SUCCESS); +} // QueryDeviceCapabilities + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CFmMiniportWaveRT::QueryPowerChangeState +( + _In_ POWER_STATE NewStateQuery +) +{ + UNREFERENCED_PARAMETER(NewStateQuery); + + DPF_ENTER(("[CFmMiniportWaveRT::QueryPowerChangeState]")); + + // if we're in a call state other than disabled, then we need the hardware turned on + if (m_bFmRxState) + { + return STATUS_RESOURCE_IN_USE; + } + + return STATUS_SUCCESS; +} // QueryPowerChangeState + + diff --git a/audio/sysvad/PhoneAudioSample/FMWave.h b/audio/sysvad/PhoneAudioSample/FMWave.h new file mode 100644 index 00000000..1134b680 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMWave.h @@ -0,0 +1,84 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmwave.h + +Abstract: + + Definition of wavert miniport class for fm + +--*/ + +#include "MinWaveRT.h" + +#ifndef _SYSVAD_FMMINWAVERT_H_ +#define _SYSVAD_FMMINWAVERT_H_ + +NTSTATUS +CreateFmMiniportWaveRT +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +); + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CFmMiniportWaveRT +// +class CFmMiniportWaveRT : + public CMiniportWaveRT, + public IAdapterPowerManagement +{ +private: + BOOL m_bFmRxState; + +public: + DECLARE_STD_UNKNOWN(); + IMP_IAdapterPowerManagement; + + CFmMiniportWaveRT( + _In_ PUNKNOWN UnknownAdapter, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext + ) + :CMiniportWaveRT(UnknownAdapter, MiniportPair, DeviceContext), + m_bFmRxState(FALSE) + { + } + + ~CFmMiniportWaveRT(); + + friend NTSTATUS PropertyHandler_FmRxWaveFilter + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +public: + NTSTATUS PropertyHandler_FmRxState + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +private: + NTSTATUS UpdateTopologyJackState + ( + _In_ BOOL NewState + ); +}; +typedef CFmMiniportWaveRT *PCFmMiniportWaveRT; + +#endif // _SYSVAD_FMMINWAVERT_H_ + diff --git a/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj b/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj new file mode 100644 index 00000000..02fe961a --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj @@ -0,0 +1,300 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{43FF11E5-B4C4-409A-AE4B-13342918B6F8}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <SupportsPackaging>false</SupportsPackaging> + <RequiresPackageProject>true</RequiresPackageProject> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{05A9D8CD-F200-4641-A6EE-EF0F678D00EA}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>PhoneAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>PhoneAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>PhoneAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>PhoneAudioSample</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\adapter.cpp" /> + <ClCompile Include="..\basetopo.cpp" /> + <ClCompile Include="..\common.cpp" /> + <ClCompile Include="..\hw.cpp" /> + <ClCompile Include="..\kshelper.cpp" /> + <ClCompile Include="..\savedata.cpp" /> + <ClCompile Include="..\tonegenerator.cpp" /> + <ClCompile Include="cellulartopo.cpp" /> + <ClCompile Include="cellularwave.cpp" /> + <ClCompile Include="fmtopo.cpp" /> + <ClCompile Include="fmwave.cpp" /> + <ClCompile Include="handsetmictopo.cpp" /> + <ClCompile Include="handsetspeakertopo.cpp" /> + <ClCompile Include="michstopo.cpp" /> + <ClCompile Include="speakerhstopo.cpp" /> + <ResourceCompile Include="..\sysvad.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj.Filters b/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj.Filters new file mode 100644 index 00000000..369dd396 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj.Filters @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{25674444-F6A7-49F9-A19E-92D6B2B8C1CE}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{9F8DB742-298E-4030-84A4-9B1F2C92C863}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{38FD0692-2121-4F6C-BA1E-76CCDF3C0FF9}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{E6A2DFAA-E516-4CB2-86BA-6A2985FEEEAB}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\adapter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\basetopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\common.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\hw.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\kshelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\savedata.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\tonegenerator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="cellulartopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="cellularwave.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="fmtopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="fmwave.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="handsetmictopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="handsetspeakertopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="michstopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="speakerhstopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="..\sysvad.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/PhoneAudioSample/handsetmictopo.cpp b/audio/sysvad/PhoneAudioSample/handsetmictopo.cpp new file mode 100644 index 00000000..d4854a90 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetmictopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetmictopo.cpp + +Abstract: + + Implementation of topology miniport for the handset mic (internal). + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "handsetmictopo.h" +#include "handsetmictoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_HandsetMicJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HandsetMicJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(HandsetMicJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HandsetMicJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_HandsetMicJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HandsetMicJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(HandsetMicJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HandsetMicJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_HandsetMicTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HandsetMicTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_HandsetMicJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_HandsetMicJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_HandsetMicTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/PhoneAudioSample/handsetmictopo.h b/audio/sysvad/PhoneAudioSample/handsetmictopo.h new file mode 100644 index 00000000..07c8ba27 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetmictopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetmictopo.h + +Abstract: + + Declaration of topology miniport for the handset mic (internal). + +--*/ + +#ifndef _SYSVAD_HANDSETMICTOPO_H_ +#define _SYSVAD_HANDSETMICTOPO_H_ + +NTSTATUS PropertyHandler_HandsetMicTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_HANDSETMICTOPO_H_ diff --git a/audio/sysvad/PhoneAudioSample/handsetmictoptable.h b/audio/sysvad/PhoneAudioSample/handsetmictoptable.h new file mode 100644 index 00000000..5fd70010 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetmictoptable.h @@ -0,0 +1,245 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetmictopotable.h + +Abstract: + + Declaration of topology table for the handset mic (internal) + +--*/ + +#ifndef _SYSVAD_HANDSETMICTOPTABLE_H_ +#define _SYSVAD_HANDSETMICTOPTABLE_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_HandsetMicTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +//============================================================================= +static +KSDATARANGE HandsetMicTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE HandsetMicTopoPinDataRangePointersBridge[] = +{ + &HandsetMicTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR HandsetMicTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HandsetMicTopoPinDataRangePointersBridge),// DataRangesCount + HandsetMicTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HANDSET, // Category + NULL, // Name + 0 // Reserved + } + }, + + // KSPIN - topology filter out-in + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HandsetMicTopoPinDataRangePointersBridge),// DataRangesCount + HandsetMicTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION HandsetMicJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0x0000, // no color + eConnTypeUnknown, + eGeoLocTop, + eGenLocInternal, + ePortConnIntegratedDevice, + TRUE +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION HandsetMicJackDescriptions[] = +{ + &HandsetMicJackDesc, + NULL +}; + +//============================================================================= +static +PCPROPERTY_ITEM HandsetMicPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicVolume, HandsetMicPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM HandsetMicPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicMute, HandsetMicPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM HandsetMicPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicPeakMeter, HandsetMicPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR HandsetMicTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationHandsetMicVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationHandsetMicMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationHandsetMicPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR HandsetMicMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM HandsetMicPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HandsetMicTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HandsetMicTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicTopoFilter, HandsetMicPropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HandsetMicTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHandsetMicTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HandsetMicTopoMiniportPins), // PinCount + HandsetMicTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(HandsetMicTopologyNodes), // NodeCount + HandsetMicTopologyNodes, // Nodes + SIZEOF_ARRAY(HandsetMicMiniportConnections), // ConnectionCount + HandsetMicMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_HANDSETMICTOPTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/handsetmicwavtable.h b/audio/sysvad/PhoneAudioSample/handsetmicwavtable.h new file mode 100644 index 00000000..5f9929f6 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetmicwavtable.h @@ -0,0 +1,441 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetmicwavtable.h + +Abstract: + + Declaration of wave miniport tables for the handset microphone. + +--*/ + +#ifndef _SYSVAD_HANDSETMICWAVTABLE_H_ +#define _SYSVAD_HANDSETMICWAVTABLE_H_ + +// +// Handset microphone (internal) range. +// +#define HANDSETMIC_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define HANDSETMIC_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define HANDSETMIC_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define HANDSETMIC_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define HANDSETMIC_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define HANDSETMIC_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HandsetMicPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 22050, + 44100, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 24000, + 48000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 32000, + 64000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 6 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 7 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT HandsetMicPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &HandsetMicPinSupportedDeviceFormats[SIZEOF_ARRAY(HandsetMicPinSupportedDeviceFormats) - 1].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &HandsetMicPinSupportedDeviceFormats[SIZEOF_ARRAY(HandsetMicPinSupportedDeviceFormats) - 1].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH, + &HandsetMicPinSupportedDeviceFormats[2].DataFormat, // 16KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &HandsetMicPinSupportedDeviceFormats[4].DataFormat, // 24KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES HandsetMicPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + HandsetMicPinSupportedDeviceFormats, + SIZEOF_ARRAY(HandsetMicPinSupportedDeviceFormats), + HandsetMicPinSupportedDeviceModes, + SIZEOF_ARRAY(HandsetMicPinSupportedDeviceModes) + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO HandsetMicPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HANDSETMIC_DEVICE_MAX_CHANNELS, + HANDSETMIC_MIN_BITS_PER_SAMPLE_PCM, + HANDSETMIC_MAX_BITS_PER_SAMPLE_PCM, + HANDSETMIC_MIN_SAMPLE_RATE, + HANDSETMIC_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE HandsetMicPinDataRangePointersStream[] = +{ + PKSDATARANGE(&HandsetMicPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE HandsetMicPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE HandsetMicPinDataRangePointersBridge[] = +{ + &HandsetMicPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR HandsetMicWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetMicPinDataRangePointersBridge), + HandsetMicPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + HANDSETMIC_MAX_INPUT_STREAMS, + HANDSETMIC_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetMicPinDataRangePointersStream), + HandsetMicPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR HandsetMicWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR HandsetMicWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHandsetMicWaveFilter[] = +{ + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicWaveFilter, PropertiesHandsetMicWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HandsetMicWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHandsetMicWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HandsetMicWaveMiniportPins), // PinCount + HandsetMicWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(HandsetMicWaveMiniportNodes), // NodeCount + HandsetMicWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(HandsetMicWaveMiniportConnections), // ConnectionCount + HandsetMicWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_HANDSETMICWAVTABLE_H_ diff --git a/audio/sysvad/PhoneAudioSample/handsetspeakertopo.cpp b/audio/sysvad/PhoneAudioSample/handsetspeakertopo.cpp new file mode 100644 index 00000000..9e72ce85 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetspeakertopo.cpp @@ -0,0 +1,561 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetspeakertopo.cpp + +Abstract: + + Implementation of topology miniport for the handsetspeaker endpoint. + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "handsetspeakertopo.h" +#include "handsetspeakertoptable.h" +#include <ntstrsafe.h> + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CreateHandsetSpeakerMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CHandsetSpeakerMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CHandsetSpeakerMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} // CreateHandsetSpeakerMiniportTopology + +//============================================================================= +CHandsetSpeakerMiniportTopology::~CHandsetSpeakerMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CHandsetSpeakerMiniportTopology::~CHandsetSpeakerMiniportTopology]")); +} // ~CHandsetSpeakerMiniportTopology + +//============================================================================= +NTSTATUS +CHandsetSpeakerMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CHandsetSpeakerMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CHandsetSpeakerMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CHandsetSpeakerMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CHandsetSpeakerMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CHandsetSpeakerMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + ULONG cJackDescriptions = ARRAYSIZE(HandsetSpeakerJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HandsetSpeakerJackDescriptions; + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CHandsetSpeakerMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(HandsetSpeakerJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HandsetSpeakerJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_HandsetSpeakerTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HandsetSpeakerTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCHandsetSpeakerMiniportTopology pMiniport = (PCHandsetSpeakerMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_HandsetSpeakerTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/PhoneAudioSample/handsetspeakertopo.h b/audio/sysvad/PhoneAudioSample/handsetspeakertopo.h new file mode 100644 index 00000000..7b220f70 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetspeakertopo.h @@ -0,0 +1,86 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetspeakertopo.h + +Abstract: + + Declaration of topology miniport for the handsetspeaker endpoint. + +--*/ + +#ifndef _SYSVAD_HANDSETSPEAKERTOPO_H_ +#define _SYSVAD_HANDSETSPEAKERTOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CHandsetSpeakerMiniportTopology +// + +class CHandsetSpeakerMiniportTopology : + public CMiniportTopologySYSVAD, + public IMiniportTopology, + public CUnknown +{ + public: + DECLARE_STD_UNKNOWN(); + CHandsetSpeakerMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ) + : CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels) + {} + + ~CHandsetSpeakerMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackSinkInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); +}; + +typedef CHandsetSpeakerMiniportTopology *PCHandsetSpeakerMiniportTopology; + + +NTSTATUS +CreateHandsetSpeakerMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_HandsetSpeakerTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_HANDSETSPEAKERTOPO_H_ + diff --git a/audio/sysvad/PhoneAudioSample/handsetspeakertoptable.h b/audio/sysvad/PhoneAudioSample/handsetspeakertoptable.h new file mode 100644 index 00000000..68873e61 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetspeakertoptable.h @@ -0,0 +1,156 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetspeakertoptable.h + +Abstract: + + Declaration of topology tables for the handsetspeaker endpoint. + +--*/ + +#ifndef _SYSVAD_HANDSETSPEAKERTOPTABLE_H_ +#define _SYSVAD_HANDSETSPEAKERTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE HandsetSpeakerTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE HandsetSpeakerTopoPinDataRangePointersBridge[] = +{ + &HandsetSpeakerTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR HandsetSpeakerTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HandsetSpeakerTopoPinDataRangePointersBridge), // DataRangesCount + HandsetSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HandsetSpeakerTopoPinDataRangePointersBridge), // DataRangesCount + HandsetSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HANDSET, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION HandsetSpeakerJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0x0000, // no color + eConnTypeUnknown, + eGeoLocBottom, + eGenLocInternal, + ePortConnIntegratedDevice, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION HandsetSpeakerJackDescriptions[] = +{ + NULL, + &HandsetSpeakerJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR HandsetSpeakerTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHandsetSpeakerTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HandsetSpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HandsetSpeakerTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerTopoFilter, PropertiesHandsetSpeakerTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HandsetSpeakerTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHandsetSpeakerTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HandsetSpeakerTopoMiniportPins), // PinCount + HandsetSpeakerTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(HandsetSpeakerTopoMiniportConnections), // ConnectionCount + HandsetSpeakerTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_HANDSETSPEAKERTOPTABLE_H_ + + diff --git a/audio/sysvad/PhoneAudioSample/handsetspeakerwavtable.h b/audio/sysvad/PhoneAudioSample/handsetspeakerwavtable.h new file mode 100644 index 00000000..4a1cfbb3 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetspeakerwavtable.h @@ -0,0 +1,649 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetspeakerwavtable.h + +Abstract: + + Declaration of wave miniport tables for the handsetspeaker endpoint. + + Note: the HANDSETSPEAKER in this sample shows how to do h/w loopback + for non-offloaded devices. + +--*/ + +#ifndef _SYSVAD_HANDSETSPEAKERWAVTABLE_H_ +#define _SYSVAD_HANDSETSPEAKERWAVTABLE_H_ + + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + + +#define HANDSETSPEAKER_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define HANDSETSPEAKER_HOST_MAX_CHANNELS 2 // Max Channels. +#define HANDSETSPEAKER_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HANDSETSPEAKER_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HANDSETSPEAKER_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define HANDSETSPEAKER_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define HANDSETSPEAKER_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define HANDSETSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HANDSETSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HANDSETSPEAKER_LOOPBACK_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define HANDSETSPEAKER_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define HANDSETSPEAKER_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define HANDSETSPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HANDSETSPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HANDSETSPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define HANDSETSPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define HANDSETSPEAKER_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define HANDSETSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HandsetSpeakerHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HandsetSpeakerLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT HandsetSpeakerHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &HandsetSpeakerHostPinSupportedDeviceFormats[3].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &HandsetSpeakerHostPinSupportedDeviceFormats[3].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &HandsetSpeakerHostPinSupportedDeviceFormats[0].DataFormat, // 24KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES HandsetSpeakerPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + HandsetSpeakerHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(HandsetSpeakerHostPinSupportedDeviceFormats), + HandsetSpeakerHostPinSupportedDeviceModes, + SIZEOF_ARRAY(HandsetSpeakerHostPinSupportedDeviceModes) + }, + { + RenderLoopbackPin, + HandsetSpeakerLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(HandsetSpeakerLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO HandsetSpeakerPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HANDSETSPEAKER_HOST_MAX_CHANNELS, + HANDSETSPEAKER_HOST_MIN_BITS_PER_SAMPLE, + HANDSETSPEAKER_HOST_MAX_BITS_PER_SAMPLE, + HANDSETSPEAKER_HOST_MIN_SAMPLE_RATE, + HANDSETSPEAKER_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HANDSETSPEAKER_LOOPBACK_MAX_CHANNELS, + HANDSETSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE, + HANDSETSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE, + HANDSETSPEAKER_LOOPBACK_MIN_SAMPLE_RATE, + HANDSETSPEAKER_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE HandsetSpeakerPinDataRangePointersStream[] = +{ + PKSDATARANGE(&HandsetSpeakerPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE HandsetSpeakerPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&HandsetSpeakerPinDataRangesStream[1]) +}; + +//============================================================================= +static +KSDATARANGE HandsetSpeakerPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE HandsetSpeakerPinDataRangePointersBridge[] = +{ + &HandsetSpeakerPinDataRangesBridge[0] +}; + + +//============================================================================= +static +PCPIN_DESCRIPTOR HandsetSpeakerWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_SYSTEM + { + HANDSETSPEAKER_MAX_INPUT_SYSTEM_STREAMS, + HANDSETSPEAKER_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetSpeakerPinDataRangePointersStream), + HandsetSpeakerPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_LOOPBACK + { + HANDSETSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + HANDSETSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetSpeakerPinDataRangePointersLoopbackStream), + HandsetSpeakerPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER2_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetSpeakerPinDataRangePointersBridge), + HandsetSpeakerPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM HandsetSpeakerPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerVolume, HandsetSpeakerPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM HandsetSpeakerPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerMute, HandsetSpeakerPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM HandsetSpeakerPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerPeakMeter, HandsetSpeakerPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR HandsetSpeakerWaveMiniportNodes[] = +{ + // KSNODE_WAVE_SUM + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_SUM, // Type + NULL // Name + }, + // KSNODE_WAVE_VOLUME + { + 0, // Flags + &AutomationHandsetSpeakerVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_WAVE_VOLUME // Name + }, + // KSNODE_WAVE_MUTE + { + 0, // Flags + &AutomationHandsetSpeakerMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_WAVE_MUTE // Name + }, + // KSNODE_WAVE_PEAKMETER + { + 0, // Flags + &AutomationHandsetSpeakerPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_WAVE_SUM == 0 ); +C_ASSERT( KSNODE_WAVE_VOLUME == 1 ); +C_ASSERT( KSNODE_WAVE_MUTE == 2 ); +C_ASSERT( KSNODE_WAVE_PEAKMETER == 3 ); + + +//============================================================================= +// +// Note: +// 1) A sum node can be used to 'sum' multiple streams associated to different +// instances of the same pin. +// 2) Any node output has an implicit 'tee' node associated with it. Thus in the +// scenario below it is ok for the peak meter node to have two identical outputs. +// +// ---------------------------------------------------------- +// | | +// System Pin 0 | |-----| |--------| |------| |-----------| |--> 1 Loopback Pin +// [instances:0...n]-->| -->| Sum |--->| Volume |-->| Mute |-->| PeakMeter |--> | +// | |-----| |--------| |------| |-----------| |--> 2 KSPIN_WAVE_RENDER2_SOURCE +// | | +// ---------------------------------------------------------- + +static +PCCONNECTION_DESCRIPTOR HandsetSpeakerWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER2_SINK_SYSTEM, KSNODE_WAVE_SUM, 1 }, + { KSNODE_WAVE_SUM, 0, KSNODE_WAVE_VOLUME, 1 }, + { KSNODE_WAVE_VOLUME, 0, KSNODE_WAVE_MUTE, 1 }, + { KSNODE_WAVE_MUTE, 0, KSNODE_WAVE_PEAKMETER, 1 }, + { KSNODE_WAVE_PEAKMETER, 2, PCFILTER_NODE, KSPIN_WAVE_RENDER2_SINK_LOOPBACK }, + { KSNODE_WAVE_PEAKMETER, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER2_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHandsetSpeakerWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerWaveFilter, PropertiesHandsetSpeakerWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HandsetSpeakerWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHandsetSpeakerWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HandsetSpeakerWaveMiniportPins), // PinCount + HandsetSpeakerWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(HandsetSpeakerWaveMiniportNodes), // NodeCount + HandsetSpeakerWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(HandsetSpeakerWaveMiniportConnections), // ConnectionCount + HandsetSpeakerWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_HANDSETSPEAKERWAVTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/michstopo.cpp b/audio/sysvad/PhoneAudioSample/michstopo.cpp new file mode 100644 index 00000000..0662cd97 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/michstopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + michstopo.cpp + +Abstract: + + Implementation of topology miniport for the mic (external: headset). + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "michstopo.h" +#include "michstoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_MicHsJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicHsJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(MicHsJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = MicHsJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicHsJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicHsJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(MicHsJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = MicHsJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicHsTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicHsTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_MicHsJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_MicHsJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_MicHsTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/PhoneAudioSample/michstopo.h b/audio/sysvad/PhoneAudioSample/michstopo.h new file mode 100644 index 00000000..b8f9b106 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/michstopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + michstopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: headset). + +--*/ + +#ifndef _SYSVAD_MICHSTOPO_H_ +#define _SYSVAD_MICHSTOPO_H_ + +NTSTATUS PropertyHandler_MicHsTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_MICHSTOPO_H_ diff --git a/audio/sysvad/PhoneAudioSample/michstoptable.h b/audio/sysvad/PhoneAudioSample/michstoptable.h new file mode 100644 index 00000000..d3d57dd2 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/michstoptable.h @@ -0,0 +1,246 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + michstopotable.h + +Abstract: + + Declaration of topology table for the mic (external: headset) + +--*/ + +#ifndef _SYSVAD_MICHSTOPTABLE_H_ +#define _SYSVAD_MICHSTOPTABLE_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_MicHsTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +//============================================================================= +static +KSDATARANGE MicHsTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicHsTopoPinDataRangePointersBridge[] = +{ + &MicHsTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicHsTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicHsTopoPinDataRangePointersBridge),// DataRangesCount + MicHsTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_MICROPHONE, // Category + NULL, // Name + 0 // Reserved + } + }, + + // KSPIN - topology filter out-in + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicHsTopoPinDataRangePointersBridge),// DataRangesCount + MicHsTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION MicHsJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + JACKDESC_RGB(179, 201, 140), + eConnTypeCombination, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + FALSE // NOTE: For convenience, wired headset jacks will be "unplugged" at boot. + // However, we need to introduce a test hook to toggle jack state of this and other endpoints. +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION MicHsJackDescriptions[] = +{ + &MicHsJackDesc, + NULL +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicHsPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsVolume, MicHsPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicHsPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsMute, MicHsPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicHsPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsPeakMeter, MicHsPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicHsTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicHsVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicHsMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicHsPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicHsMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicHsPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicHsTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicHsTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsTopoFilter, MicHsPropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicHsTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicHsTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicHsTopoMiniportPins), // PinCount + MicHsTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicHsTopologyNodes), // NodeCount + MicHsTopologyNodes, // Nodes + SIZEOF_ARRAY(MicHsMiniportConnections), // ConnectionCount + MicHsMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICHSTOPTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/michswavtable.h b/audio/sysvad/PhoneAudioSample/michswavtable.h new file mode 100644 index 00000000..5d0ab11f --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/michswavtable.h @@ -0,0 +1,441 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + michswavtable.h + +Abstract: + + Declaration of wave miniport tables for the mic (external: headset). + +--*/ + +#ifndef _SYSVAD_MICHSWAVTABLE_H_ +#define _SYSVAD_MICHSWAVTABLE_H_ + +// +// Mic in (external: headset) range. +// +#define MICHS_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define MICHS_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define MICHS_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define MICHS_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define MICHS_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define MICHS_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicHsPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 22050, + 44100, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 24000, + 48000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 32000, + 64000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 6 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 7 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicHsPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicHsPinSupportedDeviceFormats[SIZEOF_ARRAY(MicHsPinSupportedDeviceFormats) - 1].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &MicHsPinSupportedDeviceFormats[SIZEOF_ARRAY(MicHsPinSupportedDeviceFormats) - 1].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH, + &MicHsPinSupportedDeviceFormats[2].DataFormat, // 16KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &MicHsPinSupportedDeviceFormats[4].DataFormat, // 24KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES MicHsPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicHsPinSupportedDeviceFormats, + SIZEOF_ARRAY(MicHsPinSupportedDeviceFormats), + MicHsPinSupportedDeviceModes, + SIZEOF_ARRAY(MicHsPinSupportedDeviceModes) + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO MicHsPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICHS_DEVICE_MAX_CHANNELS, + MICHS_MIN_BITS_PER_SAMPLE_PCM, + MICHS_MAX_BITS_PER_SAMPLE_PCM, + MICHS_MIN_SAMPLE_RATE, + MICHS_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE MicHsPinDataRangePointersStream[] = +{ + PKSDATARANGE(&MicHsPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE MicHsPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicHsPinDataRangePointersBridge[] = +{ + &MicHsPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicHsWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicHsPinDataRangePointersBridge), + MicHsPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + MICHS_MAX_INPUT_STREAMS, + MICHS_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicHsPinDataRangePointersStream), + MicHsPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicHsWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicHsWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesMicHsWaveFilter[] = +{ + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsWaveFilter, PropertiesMicHsWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicHsWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicHsWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicHsWaveMiniportPins), // PinCount + MicHsWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicHsWaveMiniportNodes), // NodeCount + MicHsWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicHsWaveMiniportConnections), // ConnectionCount + MicHsWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_MICHSWAVTABLE_H_ diff --git a/audio/sysvad/PhoneAudioSample/minipairs.h b/audio/sysvad/PhoneAudioSample/minipairs.h new file mode 100644 index 00000000..3f7e9f24 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/minipairs.h @@ -0,0 +1,504 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minipairs.h + +Abstract: + + Local audio endpoint filter definitions. + +--*/ + +#ifndef _SYSVAD_MINIPAIRS_H_ +#define _SYSVAD_MINIPAIRS_H_ + +#include "speakertopo.h" +#include "speakertoptable.h" +#include "speakerwavtable.h" + +#include "speakerhstopo.h" +#include "speakerhstoptable.h" +#include "speakerhswavtable.h" + +#include "michstopo.h" +#include "michstoptable.h" +#include "michswavtable.h" + +#include "micarraytopo.h" +#include "micarray1toptable.h" +#include "micarraywavtable.h" + +#include "cellulartopo.h" +#include "cellularwave.h" +#include "cellulartoptable.h" +#include "cellularwavtable.h" + +#include "fmtopo.h" +#include "fmwave.h" +#include "fmtoptable.h" +#include "fmwavtable.h" + +#include "handsetspeakertopo.h" +#include "handsetspeakertoptable.h" +#include "handsetspeakerwavtable.h" + +#include "handsetmictopo.h" +#include "handsetmictoptable.h" +#include "handsetmicwavtable.h" + + +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +// +// Render miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for speaker (internal) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerMiniports = +{ + eSpeakerDevice, + SPEAKER_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateMiniportTopologySYSVAD, + &SpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpeaker", // make sure this name matches with KSNAME_WaveSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpeakerWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + SPEAKER_DEVICE_MAX_CHANNELS, + SpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerPinDeviceFormatsAndModes), + SpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for speaker (external:headset) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerHsTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerHsMiniports = +{ + eSpeakerHsDevice, + SPEAKER_HEADSET_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateMiniportTopologySYSVAD, + &SpeakerHsTopoMiniportFilterDescriptor, // use the speaker headset topo filter descriptor, not headset. + 0, NULL, // Interface properties + L"WaveSpeakerHeadset", // make sure this name matches with KSNAME_WaveSpeakerHeadset in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpeakerHsWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + SPEAKERHS_DEVICE_MAX_CHANNELS, + SpeakerHsPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerHsPinDeviceFormatsAndModes), + SpeakerHsTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerHsTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +// +// headset microphone miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic in * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicHsTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicHsMiniports = +{ + eMicHsDevice, + MIC_HEADSET_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateMiniportTopologySYSVAD, + &MicHsTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicHeadset", // make sure this name matches with KSNAME_WaveMicHeadset in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicHsWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICHS_DEVICE_MAX_CHANNELS, + MicHsPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicHsPinDeviceFormatsAndModes), + MicHsTopologyPhysicalConnections, + SIZEOF_ARRAY(MicHsTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + + +// +// Capture miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic array 1 (front) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray1TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray1Miniports = +{ + eMicArrayDevice1, + L"TopologyMicArray1", // make sure this name matches with KSNAME_TopologyMicArray1 in the inf's [Strings] section + CreateMicArrayMiniportTopology, + &MicArray1TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray1", // make sure this name matches with KSNAME_WaveMicArray1 in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicArrayWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICARRAY_DEVICE_MAX_CHANNELS, + MicArrayPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArrayPinDeviceFormatsAndModes), + MicArray1TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray1TopologyPhysicalConnections), + ENDPOINT_SOUNDDETECTOR_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for cellular endpoint * +* * +* +------+ +------+ * +* | Wave1| | Topo | * +* | | | | * +* | | | | * +* | | | | * +* System BiDi <--|0 1|<---------------|1 0|<-- BIDI * +* | | | | * +* +------+ | | * +* | | * +* +------+ | | * +* | Wave2| | | * +* | | | | * +* | | | | * +* | | | | * +* System BiDi <--|0 1|<---------------|3 2|<-- BIDI * +* | | | | * +* +------+ +------+ * +* * +*********************************************************************/ + +static +PHYSICALCONNECTIONTABLE CellularTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BIDI1_BRIDGE, // TopologyOut + KSPIN_WAVE_BIDI_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR CellularMiniports = +{ + eCellularDevice, + L"TopologyCellular", // make sure this name matches with KSNAME_TopologyCellular in the inf's [Strings] section + CreateCellularMiniportTopology, + &CellularTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveCellular", // make sure this name matches with KSNAME_WaveCellular in the inf's [Strings] section + CreateCellularMiniportWaveRT, + &CellularWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + CELLULAR_DEVICE_MAX_CHANNELS, + CellularPinDeviceFormatsAndModes, + SIZEOF_ARRAY(CellularPinDeviceFormatsAndModes), + CellularTopologyPhysicalConnections, + SIZEOF_ARRAY(CellularTopologyPhysicalConnections), + ENDPOINT_CELLULAR_PROVIDER1 +}; + + +static +PHYSICALCONNECTIONTABLE CellularTopologyPhysicalConnections2[] = +{ + { + KSPIN_TOPO_BIDI2_BRIDGE, // TopologyOut + KSPIN_WAVE_BIDI_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR CellularMiniports2 = +{ + eCellularDevice, + L"TopologyCellular", // make sure this name matches with KSNAME_TopologyCellular in the inf's [Strings] section + CreateCellularMiniportTopology, + &CellularTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveCellular2", // make sure this name matches with KSNAME_WaveCellular in the inf's [Strings] section + CreateCellularMiniportWaveRT, + &CellularWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + CELLULAR_DEVICE_MAX_CHANNELS, + CellularPinDeviceFormatsAndModes, + SIZEOF_ARRAY(CellularPinDeviceFormatsAndModes), + CellularTopologyPhysicalConnections2, + SIZEOF_ARRAY(CellularTopologyPhysicalConnections2), + ENDPOINT_CELLULAR_PROVIDER2 +}; + +/********************************************************************* +* Topology/Wave bridge connection for handset speaker endpoint * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 1|---> Loopback | | * +* | | | | * +* | 2|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE HandsetSpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER2_SOURCE, // WaveOut (no offloading) + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR HandsetSpeakerMiniports = +{ + eHandsetSpeakerDevice, + HANDSET_SPEAKER_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateHandsetSpeakerMiniportTopology, + &HandsetSpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveHandsetSpeaker", // make sure this name matches with KSNAME_WaveHandsetSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &HandsetSpeakerWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + HANDSETSPEAKER_DEVICE_MAX_CHANNELS, + HandsetSpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(HandsetSpeakerPinDeviceFormatsAndModes), + HandsetSpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(HandsetSpeakerTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +// +// handset microphone miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic in * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE HandsetMicTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR HandsetMicMiniports = +{ + eHandsetMicDevice, + HANDSET_MIC_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateMiniportTopologySYSVAD, + &HandsetMicTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveHandsetMic", // make sure this name matches with KSNAME_WaveHandsetMic in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &HandsetMicWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + HANDSETMIC_DEVICE_MAX_CHANNELS, + HandsetMicPinDeviceFormatsAndModes, + SIZEOF_ARRAY(HandsetMicPinDeviceFormatsAndModes), + HandsetMicTopologyPhysicalConnections, + SIZEOF_ARRAY(HandsetMicTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +/********************************************************************* +* Topology/Wave bridge connection for FM RX * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* FM RX --->|0 1|===>|1 0|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE FmRxTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_FMRX_BRIDGE, // TopologyOut + KSPIN_WAVE_FMRX_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR FmRxMiniports = +{ + eFmRxDevice, + L"TopologyFmRx", // make sure this name matches with KSNAME_TopologyFmRx + CreateFmMiniportTopology, + &FmTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveFmRx", // make sure this name matches with KSNAME_WaveFmRx in the inf's [Strings] section + CreateFmMiniportWaveRT, + &FmWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + FMRX_DEVICE_MAX_CHANNELS, + FmRxPinDeviceFormatsAndModes, + SIZEOF_ARRAY(FmRxPinDeviceFormatsAndModes), + FmRxTopologyPhysicalConnections, + SIZEOF_ARRAY(FmRxTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +//============================================================================= +// +// Render miniport pairs. NOTE: the split of render and capture is arbitrary and +// unnessary, this array could contain capture endpoints. +// +static +PENDPOINT_MINIPAIR g_RenderEndpoints[] = +{ + &SpeakerMiniports, + &SpeakerHsMiniports, + &HandsetSpeakerMiniports, + &CellularMiniports, // SIM 1 + &CellularMiniports2 // SIM 2 +}; + +#define g_cRenderEndpoints (SIZEOF_ARRAY(g_RenderEndpoints)) + +//============================================================================= +// +// Capture miniport pairs. NOTE: the split of render and capture is arbitrary and +// unnessary, this array could contain render endpoints. +// +static +PENDPOINT_MINIPAIR g_CaptureEndpoints[] = +{ + &MicHsMiniports, + &MicArray1Miniports, + &HandsetMicMiniports, + &FmRxMiniports +}; + +#define g_cCaptureEndpoints (SIZEOF_ARRAY(g_CaptureEndpoints)) + +//============================================================================= +// +// Total miniports = # endpoints * 2 (topology + wave). +// +#define g_MaxMiniports ((g_cRenderEndpoints + g_cCaptureEndpoints) * 2) + +#endif // _SYSVAD_MINIPAIRS_H_ + diff --git a/audio/sysvad/PhoneAudioSample/speakerhstopo.cpp b/audio/sysvad/PhoneAudioSample/speakerhstopo.cpp new file mode 100644 index 00000000..4d69ac04 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/speakerhstopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhstopo.cpp + +Abstract: + + Implementation of topology miniport for the speaker (external: headset). + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "speakerhstopo.h" +#include "speakerhstoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHsJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHsJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(SpeakerHsJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = SpeakerHsJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHsJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHsJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(SpeakerHsJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = SpeakerHsJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHsTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHsTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_SpeakerHsJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_SpeakerHsJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_SpeakerHsTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/PhoneAudioSample/speakerhstopo.h b/audio/sysvad/PhoneAudioSample/speakerhstopo.h new file mode 100644 index 00000000..ea7c760f --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/speakerhstopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhstopo.h + +Abstract: + + Declaration of topology miniport for the speaker (external: headset). + +--*/ + +#ifndef _SYSVAD_SPEAKERHSTOPO_H_ +#define _SYSVAD_SPEAKERHSTOPO_H_ + +NTSTATUS PropertyHandler_SpeakerHsTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_SPEAKERHSTOPO_H_ diff --git a/audio/sysvad/PhoneAudioSample/speakerhstoptable.h b/audio/sysvad/PhoneAudioSample/speakerhstoptable.h new file mode 100644 index 00000000..15d2e86c --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/speakerhstoptable.h @@ -0,0 +1,157 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhstoptable.h + +Abstract: + + Declaration of topology tables for the speaker (external: headset). + +--*/ + +#ifndef _SYSVAD_SPEAKERHSTOPTABLE_H_ +#define _SYSVAD_SPEAKERHSTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE SpeakerHsTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpeakerHsTopoPinDataRangePointersBridge[] = +{ + &SpeakerHsTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerHsTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerHsTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerHsTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN - topology filter out-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerHsTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerHsTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_SPEAKERS, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +static +KSJACK_DESCRIPTION SpeakerHsJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + JACKDESC_RGB(179, 201, 140), + eConnTypeCombination, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + FALSE // NOTE: For convenience, wired headset jacks will be "unplugged" at boot. + // However, we need to introduce a test hook to toggle jack state of this and other endpoints. +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpeakerHsJackDescriptions[] = +{ + NULL, + &SpeakerHsJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR SpeakerHsTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerHsTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerHsTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerHsTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHsTopoFilter, PropertiesSpeakerHsTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerHsTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerHsTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerHsTopoMiniportPins), // PinCount + SpeakerHsTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpeakerHsTopoMiniportConnections), // ConnectionCount + SpeakerHsTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_SPEAKERHSTOPTABLE_H_ + + diff --git a/audio/sysvad/PhoneAudioSample/speakerhswavtable.h b/audio/sysvad/PhoneAudioSample/speakerhswavtable.h new file mode 100644 index 00000000..d1103e63 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/speakerhswavtable.h @@ -0,0 +1,854 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhswavtable.h + +Abstract: + + Declaration of wave miniport tables for the speaker (external: headset). + +--*/ + +#ifndef _SYSVAD_SPEAKERHSWAVTABLE_H_ +#define _SYSVAD_SPEAKERHSWAVTABLE_H_ + + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + +#define SPEAKERHS_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPEAKERHS_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHS_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHS_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHS_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKERHS_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPEAKERHS_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHS_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHS_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHS_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKERHS_OFFLOAD_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKERHS_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHS_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHS_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHS_LOOPBACK_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKERHS_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKERHS_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHS_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHS_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHS_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKERHS_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPEAKERHS_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define SPEAKERHS_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define SPEAKERHS_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHsAudioEngineSupportedDeviceFormats[] = +{ + { // 0 : First entry in htis table is the default format for the audio engine + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHsHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHsOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHsLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpeakerHsHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &SpeakerHsHostPinSupportedDeviceFormats[0].DataFormat // 24KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + } +}; + +static +MODE_AND_DEFAULT_FORMAT SpeakerHsOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHsOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHsOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpeakerHsPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpeakerHsHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHsHostPinSupportedDeviceFormats), + SpeakerHsHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHsHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + SpeakerHsOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHsOffloadPinSupportedDeviceFormats), + SpeakerHsOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHsOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + SpeakerHsLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHsLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + SpeakerHsAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHsAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpeakerHsPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHS_HOST_MAX_CHANNELS, + SPEAKERHS_HOST_MIN_BITS_PER_SAMPLE, + SPEAKERHS_HOST_MAX_BITS_PER_SAMPLE, + SPEAKERHS_HOST_MIN_SAMPLE_RATE, + SPEAKERHS_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHS_OFFLOAD_MAX_CHANNELS, + SPEAKERHS_OFFLOAD_MIN_BITS_PER_SAMPLE, + SPEAKERHS_OFFLOAD_MAX_BITS_PER_SAMPLE, + SPEAKERHS_OFFLOAD_MIN_SAMPLE_RATE, + SPEAKERHS_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHS_LOOPBACK_MAX_CHANNELS, + SPEAKERHS_LOOPBACK_MIN_BITS_PER_SAMPLE, + SPEAKERHS_LOOPBACK_MAX_BITS_PER_SAMPLE, + SPEAKERHS_LOOPBACK_MIN_SAMPLE_RATE, + SPEAKERHS_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpeakerHsPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpeakerHsPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpeakerHsPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&SpeakerHsPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpeakerHsPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&SpeakerHsPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE SpeakerHsPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpeakerHsPinDataRangePointersBridge[] = +{ + &SpeakerHsPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesSpeakerHsOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHsOffloadPin, PropertiesSpeakerHsOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerHsWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + SPEAKERHS_MAX_INPUT_SYSTEM_STREAMS, + SPEAKERHS_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHsPinDataRangePointersStream), + SpeakerHsPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + SPEAKERHS_MAX_INPUT_OFFLOAD_STREAMS, + SPEAKERHS_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationSpeakerHsOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHsPinDataRangePointersOffloadStream), + SpeakerHsPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + SPEAKERHS_MAX_OUTPUT_LOOPBACK_STREAMS, + SPEAKERHS_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHsPinDataRangePointersLoopbackStream), + SpeakerHsPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHsPinDataRangePointersBridge), + SpeakerHsPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR SpeakerHsWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpeakerHsWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerHsWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHsWaveFilter, PropertiesSpeakerHsWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerHsWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerHsWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerHsWaveMiniportPins), // PinCount + SpeakerHsWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpeakerHsWaveMiniportNodes), // NodeCount + SpeakerHsWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(SpeakerHsWaveMiniportConnections), // ConnectionCount + SpeakerHsWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_SPEAKERHSWAVTABLE_H_ + diff --git a/audio/sysvad/ReadMe.md b/audio/sysvad/ReadMe.md new file mode 100644 index 00000000..ceaa2259 --- /dev/null +++ b/audio/sysvad/ReadMe.md @@ -0,0 +1,189 @@ +Slate Virtual Audio Device Driver Sample +======================================== + +The Microsoft Slate Virtual Audio Device Driver (SYSVAD) shows how to develop a WDM audio driver that exposes support for multiple audio devices. + +Some of these audio devices are embedded in the system (for example, speakers, microphone arrays) while others are pluggable (like headphones, speakers, microphones, Bluetooth headsets etc.). The driver uses WaveRT and audio offloading for rendering devices. The driver uses a "virtual audio device" instead of an actual hardware-based adapter, and highlights the different aspects of the audio offloading WDM audio driver architecture. + +Driver developers can use the framework in this sample to provide support for various audio devices without concern for hardware dependencies. The framework includes implementations of the following interfaces: + +- The CAdapterCommon interface gives the miniports access to virtual mixer hardware. It also implements the **IAdapterPowerManagement** interface. + +- The CMiniportTopologyMSVAD interface is the base class for all sample topologies. It has very basic common functions. In addition, this class contains common topology property handlers. + +The following table shows the features that are implemented in the various subdirectories of this sample. + + +For more information about the Windows audio engine, see [Exposing Hardware-Offloaded Audio Processing in Windows](http://msdn.microsoft.com/en-us/windows/hardware/br259116), and note that audio hardware that is offload-capable replicates the architecture that is presented in the diagram shown in the topic. + + +Build the sample +---------------- + +If you simply want to Build this sample driver and don't intend to run or test it, then you do not need a target computer (also called a test computer). If, however, you would like to deploy, run and test this sample driver, then you need a second computer that will server as your target computer. Instructions are provided in the **Run the sample** section to show you how to set up the target computer - also referred to as *provisioning* a target computer. + +Perform the following steps to build this sample driver. + +**1. Donwload and extract the sample** + +Click the download button on this page. Click **Save**, and then click **Open Folder**. Right click *Microsoft slate system virtual audio device driver sample.zip*, and choose **Extract All**. Specify a new folder, or browse to an existing one that will store the extracted files. For example, you could specify *c:\\SlateAudioSample* as the new folder into which the files will be extracted. + +**2. Open the driver solution in Visual Studio** + +In Microsoft Visual Studio, Click **File** \> **Open** \> **Project/Solution...** and navigate to the folder that contains the extracted files (for example, *c:\\SlateAudioSample*). Double-click the *sysvad* solution file. + +In Visual Studio locate the Solution Explorer. (If this is not already open, choose **Solution Explorer** from the **View** menu.) In Solution Explorer, you can see one solution that has four (4) projects. Note that the project titled SwapAPO is actually a folder that contains two projects - APO and PropPageExtensions. + +**3. Set the sample's configuration and platform** + +In Solution Explorer, right-click **Solution 'sysvad' (4 projects)**, and choose **Configuration Manager**. Make sure that the configuration and platform settings are the same for the four projects. By default, the configuration is set to "Win8.1 Debug", and the platform is set to "Win32" for all the projects. If you make any configuration and/or platform changes for one project, you must make the same changes for the remaining three projects. + +Here are the explanations for some of the configuration and platform options. + +<table> +<colgroup> +<col width="33%" /> +<col width="33%" /> +<col width="33%" /> +</colgroup> +<thead> +<tr class="header"> +<th align="left">Configuration +Platform +Description</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td align="left">Win8.1 Debug +x64 +The driver will run on an x64 hardware platform that is running Windows 8.1. The driver will not run on any earlier versions of Windows.</td> +<td align="left">Win7 Debug +x64 +The driver will run on an x64 hardware platform that is running Windows 7 or a later version of Windows. The driver will not run on any earlier versions of Windows.</td> +</tr> +</tbody> +</table> + +**4. Build the sample using Visual Studio** + +In Visual Studio, click **Build** \> **Build Solution**. + +**5. Locate the built driver package** + +In File Explorer, navigate to the folder that contains the extracted files for the sample. For example, you would navigate to *c:\\SlateAudioSample*, if that's the folder you specified in the preceding Step 1. + +In the folder, the location of the driver package varies depending on the configuration and platform settings that you selected in the **Configuration Manager**. For example, if you left the default settings unchanged, then the built driver package will be saved to a folder named *Win8.1Debug* inside the same folder as the extracted files. Double-click the folder for the built driver package, and then double-click the folder named *package*. + +The package should contain these files: + +<table> +<colgroup> +<col width="50%" /> +<col width="50%" /> +</colgroup> +<thead> +<tr class="header"> +<th align="left">File +Description</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td align="left">PropPageExt.dll +A sample driver extension for a property page.</td> +<td align="left">SlateAudioSample.sys +The driver file.</td> +</tr> +</tbody> +</table> + +Run the sample +-------------- + +The computer where you install the driver is called the *target computer* or the *test computer*. Typically this is a separate computer from the computer on which you develop and build the driver package. The computer where you develop and build the driver is called the *host computer*. + +The process of moving the driver package to the target computer and installing the driver is called *deploying* the driver. You can deploy the sample driver, SlateAudioSample, automatically or manually. + +**Automatic deployment** + +Before you automatically deploy a driver, you must provision the target computer. Verify that the target computer has an ethernet cable connecting it to your local network, and that your host and target computers can ping each other. Then perform the following steps to prepare your host and target computers. + +**1. Provision the target computer** + +On the target computer install the latest [Windows Driver Kit](http://msdn.microsoft.com/en-us/windows/hardware/gg454513.aspx) (WDK), and then when the installation is completed, navigate to the following folder: + +\\Program Files (x86)\\Windows Kits\\8.1\\Remote\\\<architecture\>\\ + +For example, if your target computer is an x64 machine, you would navigate to: + +\\Program Files (x86)\\Windows Kits\\8.1\\Remote\\x64\\ + +Double-click the "*WDK Test Target Setup x64-x64\_en-us.msi*" file to run it. This program prepares the target computer for provisioning. + +On the host computer, in Visual Studio click **Driver** \> **Test** \> **Configure Computers...**, and then click **Add a new computer**. + +Type the name of the target computer, select **Provision computer and choose debugger settings**, and click **Next**. In the next window, verify that the **Connection Type** is set to Network. Leave the other (default) settings as they are, and click **Next**. For more information about the settings in this window, see [Getting Set Up for Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/hh450944(v=vs.85).aspx). + +**2. Prepare the host computer** + +If you haven't already done so, then preform the steps in the **Build the sample** section, to build the sample driver. + +In Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties** \> **Driver Install** \> **Deployment**. + +Check , **Enable deployment** and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Hardware ID Driver Update**, and enter *\*SYSVAD\_SLATEAUDIO* for the hardware ID. Click **OK**. + +On the **Build** menu, choose **Deploy Package** or **Build Solution**. This will deploy the sample driver to your target computer. + +On the target computer, perform the steps in the **Test the sample** section to test the sample driver. + +**Manual deployment** + +Before you manually deploy a driver, you must prepare the target computer by turning on test signing and by installing a certificate. You also need to locate the DevCon tool in your WDK installation. After that you’re ready to run the built driver sample. + +**1. Prepare the target computer** + +Open a Command Prompt window as Administrator. Then enter the following command: + +**bcdedit /set TESTSIGNING ON** + +Reboot the target computer. Then navigate to the Tools folder in your WDK installation and locate the DevCon tool. For example, look in the following folder: + +*C:\\Program Files (x86)\\Windows Kits\\8.1\\Tools\\x64\\devcon.exe* + +Copy *devcon.exe* to a folder on the target computer where it is easier to find. For example, create a *C:\\Tools* folder and copy *devcon.exe* to that folder. + +Create a folder on the target for the built driver package (for example, *C:\\SysvadDriver*). Copy all the files from the built driver package on the host computer and save them to the folder that you created on the target computer. + +Create a folder on the target computer for the certificate created by the build process. For example, you could create a folder named *C:\\Certificates* on the target computer, and then copy *package.cer* to it from the host computer. You can find this certificate in the same folder on the host computer, as the *package* folder that contains the built driver files. On the target computer, right-click the certificate file, and click **Install**, then follow the prompts to install the test certificate. + +If you need more detailed instructions for setting up the target computer, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571(v=vs.85).aspx). + +**2. Install the driver** + +The SlateAudioSample driver package contains a sample driver and 2 driver extension samples. The following instructions show you how to install and test the sample driver. Here's the general syntax for the devcon tool that you will use to install the driver: + +*devcon install \<INF file\> \<hardware ID\>* + +The INF file required for installing this driver is *sysvad.inf*. Here's how to find the hardware ID for installing the *SlateAudioSample.sys* sample: On the target computer, navigate to the folder that contains the files for your driver (for example, *C:\\SysvadDriver*). Then right-click the INF file (*sysvad.inf*) and open it with Notepad. Use Ctrl+F to find the [MicrosoftDS] section. Note that there is a comma-separated element at the end of the row. The element after the comma shows the hardware ID. So for this sample, the hardware ID is \*SYSVAD\_SLATEAUDIO. + +On the target computer, open a Command Prompt window as Administrator. Navigate to your driver package folder, and enter the following command: + +**devcon install sysvad.inf \*SYSVAD\_SLATEAUDIO** + +If you get an error message about *devcon* not being recognized, try adding the path to the *devcon* tool. For example, if you copied it to a folder called *C:\\Tools*, then try using the following command: + +**c:\\tools\\devcon install sysvad.inf \*SYSVAD\_SLATEAUDIO** + +For more detailed instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/hh698272(v=vs.85).aspx). + +After successfully installing the sample driver, you're now ready to test it. + +**Test the driver** + +On the target computer, in a Command Prompt window, enter **devmgmt** to open Device Manager. In Device Manager, on the **View** menu, choose **Devices by type**. In the device tree, locate *Microsoft Virtual Audio Device (WDM) - Slate Sample*. This is typically under the **Sound, video and game controllers** node. + +On the target computer, open Control Panel and navigate to **Hardware and Sound** \> **Manage audio devices**. In the Sound dialog box, select the speaker icon labeled as *Microsoft Virtual Audio Device (WDM) - Slate Sample*, then click **Set Default**, but do not click **OK**. This will keep the Sound dialog box open. + +Locate an MP3 or other audio file on the target computer and double-click to play it. Then in the Sound dialog box, verify that there is activity in the volume level indicator associated with the *Microsoft Virtual Audio Device (WDM) - Slate Sample* driver. + diff --git a/audio/sysvad/SwapAPO/APO/Resource.h b/audio/sysvad/SwapAPO/APO/Resource.h new file mode 100644 index 00000000..0e7dff6d --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/Resource.h @@ -0,0 +1,20 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SwapAPODll.rc +// +#define IDS_PROJNAME 100 +#define IDR_SWAPAPODLL 101 +#define IDR_SWAPAPOMFX 110 +#define IDR_SWAPAPOSFX 111 +#define IDI_EFFECT_ICON 200 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 132 +#endif +#endif diff --git a/audio/sysvad/SwapAPO/APO/SwapAPO.h b/audio/sysvad/SwapAPO/APO/SwapAPO.h new file mode 100644 index 00000000..2f687c04 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPO.h @@ -0,0 +1,319 @@ +// +// SwapAPO.h -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Declaration of the CSwapAPO class. +// + +#pragma once + +#include <audioenginebaseapo.h> +#include <BaseAudioProcessingObject.h> +#include <SwapAPOInterface.h> +#include <SwapAPODll.h> + +#include <commonmacros.h> +#include <devicetopology.h> + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +#define PK_EQUAL(x, y) ((x.fmtid == y.fmtid) && (x.pid == y.pid)) + +// +// Define a GUID identifying the type of this APO's custom effect. +// +// APOs generally should not define new GUIDs for types of effects and instead +// should use predefined effect types. Only define a new GUID if the effect is +// truly very different from all predefined types of effects. +// +// {B8EC75BA-00ED-434C-A732-064A0F00788E} +DEFINE_GUID(SwapEffectId, 0xb8ec75ba, 0x00ed, 0x434c, 0xa7, 0x32, 0x06, 0x4a, 0x0f, 0x00, 0x78, 0x8e); +// {29FBFBB5-9002-4ABC-DCBC-DD45462478C8} +DEFINE_GUID(DelayEffectId, 0x29FBFBB5, 0x9002, 0x4ABC, 0xDC, 0xBC, 0xDD, 0x45, 0x46, 0x24, 0x78, 0xC8); + +// 1000 ms of delay +#define HNS_DELAY HNS_PER_SECOND + +#define FRAMES_FROM_HNS(hns) (ULONG)(1.0 * hns / HNS_PER_SECOND * GetFramesPerSecond() + 0.5) + +LONG GetCurrentEffectsSetting(IPropertyStore* properties, PROPERTYKEY pkeyEnable, GUID processingMode); + +#pragma AVRT_VTABLES_BEGIN +// Swap APO class - MFX +class CSwapAPOMFX : + public CComObjectRootEx<CComMultiThreadModel>, + public CComCoClass<CSwapAPOMFX, &CLSID_SwapAPOMFX>, + public CBaseAudioProcessingObject, + public IMMNotificationClient, + public IAudioSystemEffects2, + // IAudioSystemEffectsCustomFormats may be optionally supported + // by APOs that attach directly to the connector in the DEFAULT mode streaming graph + public IAudioSystemEffectsCustomFormats, + public ISwapAPOMFX +{ +public: + // constructor + CSwapAPOMFX() + : CBaseAudioProcessingObject(sm_RegProperties) + , m_hEffectsChangedEvent(NULL) + , m_AudioProcessingMode(AUDIO_SIGNALPROCESSINGMODE_DEFAULT) + , m_fEnableSwapMFX(FALSE) + , m_fEnableDelayMFX(FALSE) + , m_nDelayFrames(0) + , m_iDelayIndex(0) + { + m_pf32Coefficients = NULL; + } + + virtual ~CSwapAPOMFX(); // destructor + +DECLARE_REGISTRY_RESOURCEID(IDR_SWAPAPOMFX) + +BEGIN_COM_MAP(CSwapAPOMFX) + COM_INTERFACE_ENTRY(ISwapAPOMFX) + COM_INTERFACE_ENTRY(IAudioSystemEffects) + COM_INTERFACE_ENTRY(IAudioSystemEffects2) + // IAudioSystemEffectsCustomFormats may be optionally supported + // by APOs that attach directly to the connector in the DEFAULT mode streaming graph + COM_INTERFACE_ENTRY(IAudioSystemEffectsCustomFormats) + COM_INTERFACE_ENTRY(IMMNotificationClient) + COM_INTERFACE_ENTRY(IAudioProcessingObjectRT) + COM_INTERFACE_ENTRY(IAudioProcessingObject) + COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration) +END_COM_MAP() + +DECLARE_PROTECT_FINAL_CONSTRUCT() + +public: + STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections); + + STDMETHOD(GetLatency)(HNSTIME* pTime); + + STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections); + + STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData); + + // IAudioSystemEffects2 + STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event); + + virtual HRESULT ValidateAndCacheConnectionInfo( + UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_DESCRIPTOR** ppOutputConnections); + + // IMMNotificationClient + STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + UNREFERENCED_PARAMETER(dwNewState); + return S_OK; + } + STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + return S_OK; + } + STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + return S_OK; + } + STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) + { + UNREFERENCED_PARAMETER(flow); + UNREFERENCED_PARAMETER(role); + UNREFERENCED_PARAMETER(pwstrDefaultDeviceId); + return S_OK; + } + STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); + + // IAudioSystemEffectsCustomFormats + // This interface may be optionally supported by APOs that attach directly to the connector in the DEFAULT mode streaming graph + STDMETHODIMP GetFormatCount(UINT* pcFormats); + STDMETHODIMP GetFormat(UINT nFormat, IAudioMediaType** ppFormat); + STDMETHODIMP GetFormatRepresentation(UINT nFormat, _Outptr_ LPWSTR* ppwstrFormatRep); + +public: + LONG m_fEnableSwapMFX; + LONG m_fEnableDelayMFX; + GUID m_AudioProcessingMode; + CComPtr<IPropertyStore> m_spAPOSystemEffectsProperties; + CComPtr<IMMDeviceEnumerator> m_spEnumerator; + static const CRegAPOProperties<1> sm_RegProperties; // registration properties + + // Locked memory + FLOAT32 *m_pf32Coefficients; + + CComHeapPtr<FLOAT32> m_pf32DelayBuffer; + UINT32 m_nDelayFrames; + UINT32 m_iDelayIndex; + +private: + CCriticalSection m_EffectsLock; + HANDLE m_hEffectsChangedEvent; + + HRESULT ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_pAPOSysFxInit2); + +}; +#pragma AVRT_VTABLES_END + + +#pragma AVRT_VTABLES_BEGIN +// Swap APO class - SFX +class CSwapAPOSFX : + public CComObjectRootEx<CComMultiThreadModel>, + public CComCoClass<CSwapAPOSFX, &CLSID_SwapAPOSFX>, + public CBaseAudioProcessingObject, + public IMMNotificationClient, + public IAudioSystemEffects2, + public ISwapAPOSFX +{ +public: + // constructor + CSwapAPOSFX() + : CBaseAudioProcessingObject(sm_RegProperties) + , m_hEffectsChangedEvent(NULL) + , m_AudioProcessingMode(AUDIO_SIGNALPROCESSINGMODE_DEFAULT) + , m_fEnableSwapSFX(FALSE) + , m_fEnableDelaySFX(FALSE) + , m_nDelayFrames(0) + , m_iDelayIndex(0) + { + } + + virtual ~CSwapAPOSFX(); // destructor + +DECLARE_REGISTRY_RESOURCEID(IDR_SWAPAPOSFX) + +BEGIN_COM_MAP(CSwapAPOSFX) + COM_INTERFACE_ENTRY(ISwapAPOSFX) + COM_INTERFACE_ENTRY(IAudioSystemEffects) + COM_INTERFACE_ENTRY(IAudioSystemEffects2) + COM_INTERFACE_ENTRY(IMMNotificationClient) + COM_INTERFACE_ENTRY(IAudioProcessingObjectRT) + COM_INTERFACE_ENTRY(IAudioProcessingObject) + COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration) +END_COM_MAP() + +DECLARE_PROTECT_FINAL_CONSTRUCT() + +public: + STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections); + + STDMETHOD(GetLatency)(HNSTIME* pTime); + + STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections); + + STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData); + + // IAudioSystemEffects2 + STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event); + + // IMMNotificationClient + STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + UNREFERENCED_PARAMETER(dwNewState); + return S_OK; + } + STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + return S_OK; + } + STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + return S_OK; + } + STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) + { + UNREFERENCED_PARAMETER(flow); + UNREFERENCED_PARAMETER(role); + UNREFERENCED_PARAMETER(pwstrDefaultDeviceId); + return S_OK; + } + STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); + +public: + LONG m_fEnableSwapSFX; + LONG m_fEnableDelaySFX; + GUID m_AudioProcessingMode; + CComPtr<IPropertyStore> m_spAPOSystemEffectsProperties; + CComPtr<IMMDeviceEnumerator> m_spEnumerator; + static const CRegAPOProperties<1> sm_RegProperties; // registration properties + + CCriticalSection m_EffectsLock; + HANDLE m_hEffectsChangedEvent; + + CComHeapPtr<FLOAT32> m_pf32DelayBuffer; + UINT32 m_nDelayFrames; + UINT32 m_iDelayIndex; +}; +#pragma AVRT_VTABLES_END + +OBJECT_ENTRY_AUTO(__uuidof(SwapAPOMFX), CSwapAPOMFX) +OBJECT_ENTRY_AUTO(__uuidof(SwapAPOSFX), CSwapAPOSFX) + +// +// Declaration of the ProcessSwap routine. +// +void ProcessSwap( + FLOAT32 *pf32OutputFrames, + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame); + +// +// Declaration of the ProcessSwapScale routine. +// +void ProcessSwapScale( + FLOAT32 *pf32OutputFrames, + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame, + FLOAT32 *pf32Coefficients ); + +// +// Declaration of the ProcessDelay routine. +// +void ProcessDelay( + _Out_writes_(u32ValidFrameCount * u32SamplesPerFrame) + FLOAT32 *pf32OutputFrames, + _In_reads_(u32ValidFrameCount * u32SamplesPerFrame) + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame, + _Inout_updates_(u32DelayFrames * u32SamplesPerFrame) + FLOAT32 *pf32DelayBuffer, + UINT32 u32DelayFrames, + _Inout_ + UINT32 *pu32DelayIndex ); + +// +// Convenience methods +// + +void WriteSilence( + _Out_writes_(u32FrameCount * u32SamplesPerFrame) + FLOAT32 *pf32Frames, + UINT32 u32FrameCount, + UINT32 u32SamplesPerFrame ); + +void CopyFrames( + _Out_writes_(u32FrameCount * u32SamplesPerFrame) + FLOAT32 *pf32OutFrames, + _In_reads_(u32FrameCount * u32SamplesPerFrame) + const FLOAT32 *pf32InFrames, + UINT32 u32FrameCount, + UINT32 u32SamplesPerFrame ); diff --git a/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj b/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj new file mode 100644 index 00000000..9f79895e --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj @@ -0,0 +1,242 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{2A1B7375-D9CB-4067-AEDE-180D75B5934D}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <SupportsPackaging>false</SupportsPackaging> + <RequiresPackageProject>true</RequiresPackageProject> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{0ADE9CDB-E126-4F11-8496-335EA4A1B368}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>SwapAPO</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>SwapAPO</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>SwapAPO</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>SwapAPO</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="Swap.cpp" /> + <ClCompile Include="SwapAPODll.cpp" /> + <ClCompile Include="SwapAPOMFX.cpp" /> + <ClCompile Include="SwapAPOSFX.cpp" /> + <Midl Include="SwapAPODll.idl" /> + <Midl Include="SwapAPOInterface.idl" /> + <ResourceCompile Include="SwapAPODll.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj.Filters b/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj.Filters new file mode 100644 index 00000000..10ad06cd --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj.Filters @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{3D2AE2AB-4A59-4C45-8B37-DF9973DE29F4}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{D2776F29-9EAD-4F9A-B1DB-827FEBAE9166}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{AC7F517D-37C5-4622-9D43-DFFD0B196012}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="Swap.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SwapAPODll.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SwapAPOMFX.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SwapAPOSFX.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <Midl Include="SwapAPODll.idl"> + <Filter>Source Files</Filter> + </Midl> + <Midl Include="SwapAPOInterface.idl"> + <Filter>Source Files</Filter> + </Midl> + <None Include="SwapAPODll.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="SwapAPODll.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.cpp b/audio/sysvad/SwapAPO/APO/SwapAPODll.cpp new file mode 100644 index 00000000..4792b344 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.cpp @@ -0,0 +1,148 @@ +// +// SwapAPODll.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Author: +// +// Description: +// +// SwapAPODll.cpp : Implementation of DLL Exports. + +#include <atlbase.h> +#include <atlcom.h> +#include <atlcoll.h> +#include <atlsync.h> +#include <mmreg.h> + +#include "resource.h" +#include "SwapAPODll.h" +#include <SwapAPO.h> + +#include <SwapAPODll_i.c> + +//------------------------------------------------------------------------- +// Array of APO_REG_PROPERTIES structures implemented in this module. +// Each new APO implementation will be added to this array. +// +APO_REG_PROPERTIES const *gCoreAPOs[] = +{ + &CSwapAPOMFX::sm_RegProperties.m_Properties, + &CSwapAPOSFX::sm_RegProperties.m_Properties +}; + +// {secret} +class CSwapAPODllModule : public CAtlDllModuleT< CSwapAPODllModule > +{ +public : + DECLARE_LIBID(LIBID_SwapAPODlllib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SWAPAPODLL, "{0A21D954-674A-4C09-806E-DB4FBE8F199C}") +public: + HRESULT DllRegisterServer(BOOL bRegTypeLib = TRUE) throw(); + HRESULT DllUnregisterServer(BOOL bUnRegTypeLib = TRUE) throw(); +}; + +// {secret} +CSwapAPODllModule _AtlModule; + + +HRESULT CSwapAPODllModule::DllRegisterServer(BOOL bRegTypeLib) throw() +{ + HRESULT hResult; + UINT32 u32APORegIndex = 0; + UINT32 u32APOUnregIndex = 0; + + // Register all APOs implemented in this module. + for (u32APORegIndex = 0; u32APORegIndex < SIZEOF_ARRAY(gCoreAPOs); u32APORegIndex++) + { + hResult = RegisterAPO(gCoreAPOs[u32APORegIndex]); + if (FAILED(hResult)) + { + goto ExitFailed; + } + } + + // Register the module. + hResult = CAtlDllModuleT<CSwapAPODllModule>::DllRegisterServer(bRegTypeLib); + if (FAILED(hResult)) + { + goto ExitFailed; + } + + return hResult; + +ExitFailed: + // Unregister all registered APOs if something failed. + for (u32APOUnregIndex = 0; u32APOUnregIndex < u32APORegIndex; u32APOUnregIndex++) + { + ATLVERIFY(SUCCEEDED(UnregisterAPO(gCoreAPOs[u32APOUnregIndex]->clsid))); + } + + return hResult; +} // DllRegisterServer + +HRESULT CSwapAPODllModule::DllUnregisterServer(BOOL bUnRegTypeLib) throw() +{ + HRESULT hResult; + UINT32 u32APOUnregIndex = 0; + + // Unregister the module. + hResult = CAtlDllModuleT<CSwapAPODllModule>::UnregisterServer(bUnRegTypeLib); + if (FAILED(hResult)) + { + goto Exit; + } + + // Unregister all the APOs that are implemented in this module. + for (u32APOUnregIndex = 0; u32APOUnregIndex < SIZEOF_ARRAY(gCoreAPOs); u32APOUnregIndex++) + { + hResult = UnregisterAPO(gCoreAPOs[u32APOUnregIndex]->clsid); + ATLASSERT(SUCCEEDED(hResult)); + } + +Exit: + return hResult; +} // DllUnregisterServer + +// {secret} +extern "C" BOOL WINAPI DllMain(HINSTANCE /* hInstance */, DWORD dwReason, LPVOID lpReserved) +{ + if (DLL_PROCESS_ATTACH == dwReason) + { + } + // do necessary cleanup only if the DLL is being unloaded dynamically + else if ((DLL_PROCESS_DETACH == dwReason) && (NULL == lpReserved)) + { + } + + return _AtlModule.DllMain(dwReason, lpReserved); +} + + +// {secret} +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); +} + + +// {secret} +STDAPI DllGetClassObject(_In_ REFCLSID rclsid,_In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + + +// {secret} +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + HRESULT hr = _AtlModule.DllRegisterServer(); + return hr; +} + + +// {secret} +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.def b/audio/sysvad/SwapAPO/APO/SwapAPODll.def new file mode 100644 index 00000000..722ab4c4 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.def @@ -0,0 +1,6 @@ + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.idl b/audio/sysvad/SwapAPO/APO/SwapAPODll.idl new file mode 100644 index 00000000..e9000c7c --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.idl @@ -0,0 +1,54 @@ +// +// SwapAPODll.idl -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Author: +// +// Description: +// +// SwapAPODll.idl : Definition of COM interfaces and coclasses for the DLL. + +import "oaidl.idl"; +import "ocidl.idl"; +import "SwapAPOInterface.idl"; + +//------------------------------------------------------------------------- +// SwapAPODlllib +// +[ + uuid(7092F0B2-D28D-4095-95A7-6C37A97432A2), + version(1.0) +] +library SwapAPODlllib +{ + importlib("stdole2.tlb"); + + // for Swap APO - MFX + [ + uuid(06687E71-F043-403A-BF49-CB591BA6E103) + ] + coclass SwapAPOMFX + { + interface IAudioProcessingObject; + interface IAudioProcessingObjectRT; + interface IAudioProcessingObjectConfiguration; + interface IMMNotificationClient; + interface IAudioSystemEffects; + [default] interface ISwapAPOMFX; + }; + + // for Swap APO - SFX + [ + uuid(B48DEA3F-D962-425a-8D9A-9A5BB37A9904) + ] + coclass SwapAPOSFX + { + interface IAudioProcessingObject; + interface IAudioProcessingObjectRT; + interface IAudioProcessingObjectConfiguration; + interface IMMNotificationClient; + interface IAudioSystemEffects; + [default] interface ISwapAPOSFX; + }; + + +} diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.rc b/audio/sysvad/SwapAPO/APO/SwapAPODll.rc new file mode 100644 index 00000000..119a3ebc --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.rc @@ -0,0 +1,24 @@ +#if 0 +Copyright (c) Microsoft Corporation. All Rights Reserved +#endif + +#include "resource.h" +#include "winres.h" +#include <ntverp.h> +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Swap APO" +#define VER_INTERNALNAME_STR "SWAPAPODll" +#define VER_ORIGINALFILENAME_STR "SwapAPODll.Dll" +#include <Common.ver> + +IDR_SWAPAPODLL REGISTRY "SwapAPODll.rgs" +IDR_SWAPAPOMFX REGISTRY "SwapAPOMFX.rgs" +IDR_SWAPAPOSFX REGISTRY "SwapAPOSFX.rgs" + +// ICON +IDI_EFFECT_ICON RCDATA "SwapApo.png" + +#if 0 +1 TYPELIB "SwapAPODll.tlb" +#endif diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.rgs b/audio/sysvad/SwapAPO/APO/SwapAPODll.rgs new file mode 100644 index 00000000..ab3277b3 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.rgs @@ -0,0 +1,11 @@ +HKCR +{ + NoRemove AppID + { + '%APPID%' = s 'SwapAPODll' + 'SwapAPODll.DLL' + { + val AppID = s '%APPID%' + } + } +} diff --git a/audio/sysvad/SwapAPO/APO/SwapAPOInterface.idl b/audio/sysvad/SwapAPO/APO/SwapAPOInterface.idl new file mode 100644 index 00000000..e83f16fb --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPOInterface.idl @@ -0,0 +1,31 @@ +// +// SwapAPO.idl -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// The interface and type definitions for Swap APO functionality. +// +import "oaidl.idl"; +import "ocidl.idl"; +import "audioenginebaseapo.idl"; + +// This is the SwapAPO interface. +[ + object, + uuid(3865B91A-096E-4ACA-BF56-B17D49C77406), + pointer_default(unique) +] +interface ISwapAPOMFX : IUnknown +{ +}; + +// This is the SwapAPO interface. +[ + object, + uuid(658A4077-B277-4d14-97E1-0356044D8110), + pointer_default(unique) +] +interface ISwapAPOSFX : IUnknown +{ +}; + diff --git a/audio/sysvad/SwapAPO/APO/SwapAPOMFX.rgs b/audio/sysvad/SwapAPO/APO/SwapAPOMFX.rgs new file mode 100644 index 00000000..3bebda2e --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPOMFX.rgs @@ -0,0 +1,13 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {06687E71-F043-403A-BF49-CB591BA6E103} = s 'SwapAPOMFX Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +} diff --git a/audio/sysvad/SwapAPO/APO/SwapAPOSFX.rgs b/audio/sysvad/SwapAPO/APO/SwapAPOSFX.rgs new file mode 100644 index 00000000..c553edd5 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPOSFX.rgs @@ -0,0 +1,13 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {B48DEA3F-D962-425a-8D9A-9A5BB37A9904} = s 'SwapAPOSFX Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +} diff --git a/audio/sysvad/SwapAPO/APO/swap.cpp b/audio/sysvad/SwapAPO/APO/swap.cpp new file mode 100644 index 00000000..0efe23f7 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/swap.cpp @@ -0,0 +1,183 @@ +// +// Swap.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Implementation of SwapSamples +// +#include <atlbase.h> +#include <atlcom.h> +#include <atlcoll.h> +#include <atlsync.h> +#include <mmreg.h> + +#include <audioenginebaseapo.h> +#include <baseaudioprocessingobject.h> +#include <resource.h> + +#include <float.h> + +#include "SwapAPO.h" + +#pragma AVRT_CODE_BEGIN +void WriteSilence( + _Out_writes_(u32FrameCount * u32SamplesPerFrame) + FLOAT32 *pf32Frames, + UINT32 u32FrameCount, + UINT32 u32SamplesPerFrame ) +{ + ZeroMemory(pf32Frames, sizeof(FLOAT32) * u32FrameCount * u32SamplesPerFrame); +} +#pragma AVRT_CODE_END + +#pragma AVRT_CODE_BEGIN +void CopyFrames( + _Out_writes_(u32FrameCount * u32SamplesPerFrame) + FLOAT32 *pf32OutFrames, + _In_reads_(u32FrameCount * u32SamplesPerFrame) + const FLOAT32 *pf32InFrames, + UINT32 u32FrameCount, + UINT32 u32SamplesPerFrame ) +{ + CopyMemory(pf32OutFrames, pf32InFrames, sizeof(FLOAT32) * u32FrameCount * u32SamplesPerFrame); +} +#pragma AVRT_CODE_END + +#pragma AVRT_CODE_BEGIN +void ProcessSwap( + FLOAT32 *pf32OutputFrames, + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame ) +{ + UINT32 u32SampleIndex; + FLOAT32 fSwap32; + + ASSERT_REALTIME(); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + ATLASSERT( IS_VALID_TYPED_WRITE_POINTER(pf32OutputFrames) ); + + // loop through samples + while (u32ValidFrameCount--) + { + for (u32SampleIndex=0; u32SampleIndex+1<u32SamplesPerFrame; u32SampleIndex += 2) + { + // apply swap + fSwap32 = *pf32InputFrames; + *pf32OutputFrames = *(pf32InputFrames + 1); + pf32OutputFrames++; + *pf32OutputFrames = fSwap32; + pf32OutputFrames++; + pf32InputFrames += 2; + } + } +} +#pragma AVRT_CODE_END + + +#pragma AVRT_CODE_BEGIN +void ProcessSwapScale( + FLOAT32 *pf32OutputFrames, + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame, + FLOAT32 *pf32Coefficients ) +{ + UINT32 u32SampleIndex; + FLOAT32 fSwap32; + + ASSERT_REALTIME(); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32OutputFrames) ); + + // loop through samples + while (u32ValidFrameCount--) + { + for (u32SampleIndex=0; u32SampleIndex+1<u32SamplesPerFrame; u32SampleIndex += 2) + { + // apply swap for each stereo pair and scale + + // save left channel + fSwap32 = *pf32InputFrames; + // left output equals right input times 1st coefficient + *pf32OutputFrames = *(pf32InputFrames + 1) * pf32Coefficients[u32SampleIndex]; + pf32OutputFrames++; + + // right output equals left input times 2nd coefficient + *pf32OutputFrames = fSwap32 * pf32Coefficients[u32SampleIndex+1]; + pf32OutputFrames++; + + pf32InputFrames += 2; + } + } +} +#pragma AVRT_CODE_END + + +#pragma AVRT_CODE_BEGIN +void ProcessDelay( + _Out_writes_(u32ValidFrameCount * u32SamplesPerFrame) + FLOAT32 *pf32OutputFrames, + _In_reads_(u32ValidFrameCount * u32SamplesPerFrame) + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame, + _Inout_updates_(u32DelayFrames * u32SamplesPerFrame) + FLOAT32 *pf32DelayBuffer, + UINT32 u32DelayFrames, + _Inout_ + UINT32 *pu32DelayIndex ) +{ + ASSERT_REALTIME(); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32OutputFrames) ); + + if (u32DelayFrames == 0) + { + CopyFrames( pf32OutputFrames, + pf32InputFrames, + u32ValidFrameCount, + u32SamplesPerFrame ); + } + else + { + + // Invariants: + // 0 <= (*pu32DelayIndex) < u32DelayFrames + // pf32OutputFrames[0 ... u32ValidFrameCount * u32SamplesPerFrame - 1] is writable + // pf32InputFrames[0 ... u32ValidFrameCount * u32SamplesPerFrame - 1] is readable + while (u32ValidFrameCount > 0) + { + // copy either the rest of the input/output buffer, + // or the rest of the delay buffer, + // whichever is smaller + UINT32 framesToCopy = min(u32ValidFrameCount, u32DelayFrames - (*pu32DelayIndex)); + + // delay => output +#pragma prefast(suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "this copy to pf32OutputFrames is in-range") + CopyFrames( pf32OutputFrames, + &pf32DelayBuffer[(*pu32DelayIndex) * u32SamplesPerFrame], + framesToCopy, + u32SamplesPerFrame ); + + // input => delay + CopyFrames( &pf32DelayBuffer[(*pu32DelayIndex) * u32SamplesPerFrame], + pf32InputFrames, + framesToCopy, + u32SamplesPerFrame ); + + pf32OutputFrames += framesToCopy * u32SamplesPerFrame; + pf32InputFrames += framesToCopy * u32SamplesPerFrame; + u32ValidFrameCount -= framesToCopy; + + *pu32DelayIndex += framesToCopy; + if (*pu32DelayIndex == u32DelayFrames) + { + *pu32DelayIndex = 0; + } + } + + } +} +#pragma AVRT_CODE_END + diff --git a/audio/sysvad/SwapAPO/APO/swapapo.png b/audio/sysvad/SwapAPO/APO/swapapo.png Binary files differnew file mode 100644 index 00000000..861d5bcb --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/swapapo.png diff --git a/audio/sysvad/SwapAPO/APO/swapapomfx.cpp b/audio/sysvad/SwapAPO/APO/swapapomfx.cpp new file mode 100644 index 00000000..113e0aa4 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/swapapomfx.cpp @@ -0,0 +1,1047 @@ +// +// SwapAPOMFX.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Implementation of CSwapAPOMFX +// + +#include <atlbase.h> +#include <atlcom.h> +#include <atlcoll.h> +#include <atlsync.h> +#include <mmreg.h> + +#include <initguid.h> +#include <audioenginebaseapo.h> +#include <baseaudioprocessingobject.h> +#include <resource.h> + +#include <float.h> +#include "SwapAPO.h" +#include "SysVadShared.h" +#include <CustomPropKeys.h> + + + +// Static declaration of the APO_REG_PROPERTIES structure +// associated with this APO. The number in <> brackets is the +// number of IIDs supported by this APO. If more than one, then additional +// IIDs are added at the end +#pragma warning (disable : 4815) +const AVRT_DATA CRegAPOProperties<1> CSwapAPOMFX::sm_RegProperties( + __uuidof(SwapAPOMFX), // clsid of this APO + L"CSwapAPOMFX", // friendly name of this APO + L"Copyright (c) Microsoft Corporation", // copyright info + 1, // major version # + 0, // minor version # + __uuidof(ISwapAPOMFX) // iid of primary interface +// +// If you need to change any of these attributes, uncomment everything up to +// the point that you need to change something. If you need to add IIDs, uncomment +// everything and add additional IIDs at the end. +// +// Enable inplace processing for this APO. +// , DEFAULT_APOREG_FLAGS +// , DEFAULT_APOREG_MININPUTCONNECTIONS +// , DEFAULT_APOREG_MAXINPUTCONNECTIONS +// , DEFAULT_APOREG_MINOUTPUTCONNECTIONS +// , DEFAULT_APOREG_MAXOUTPUTCONNECTIONS +// , DEFAULT_APOREG_MAXINSTANCES +// + ); + +//------------------------------------------------------------------------- +// Description: +// +// GetCurrentEffectsSetting +// Gets the current aggregate effects-enable setting +// +// Parameters: +// +// properties - Property store holding configurable effects settings +// +// pkeyEnable - VT_UI4 property holding an enable/disable setting +// +// processingMode - Audio processing mode +// +// Return values: +// LONG - true if the effect is enabled +// +// Remarks: +// The routine considers the value of the specified property, the well known +// master PKEY_AudioEndpoint_Disable_SysFx property, and the specified +// processing mode.If the processing mode is RAW then the effect is off. If +// PKEY_AudioEndpoint_Disable_SysFx is non-zero then the effect is off. +// +LONG GetCurrentEffectsSetting(IPropertyStore* properties, PROPERTYKEY pkeyEnable, GUID processingMode) +{ + HRESULT hr; + BOOL enabled; + PROPVARIANT var; + + PropVariantInit(&var); + + // Get the state of whether channel swap MFX is enabled or not. + + // Check the master disable property defined by Windows + hr = properties->GetValue(PKEY_AudioEndpoint_Disable_SysFx, &var); + enabled = (SUCCEEDED(hr)) && !((var.vt == VT_UI4) && (var.ulVal != 0)); + + PropVariantClear(&var); + + // Check the APO's enable property, defined by this APO. + hr = properties->GetValue(pkeyEnable, &var); + enabled = enabled && ((SUCCEEDED(hr)) && ((var.vt == VT_UI4) && (var.ulVal != 0))); + + PropVariantClear(&var); + + enabled = enabled && !IsEqualGUID(processingMode, AUDIO_SIGNALPROCESSINGMODE_RAW); + + return (LONG)enabled; +} + +#pragma AVRT_CODE_BEGIN +//------------------------------------------------------------------------- +// Description: +// +// Do the actual processing of data. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections +// ppInputConnections - [in] pointer to list of input APO_CONNECTION_PROPERTY pointers +// u32NumOutputConnections - [in] number of output connections +// ppOutputConnections - [in] pointer to list of output APO_CONNECTION_PROPERTY pointers +// +// Return values: +// +// void +// +// Remarks: +// +// This function processes data in a manner dependent on the implementing +// object. This routine can not fail and can not block, or call any other +// routine that blocks, or touch pagable memory. +// +STDMETHODIMP_(void) CSwapAPOMFX::APOProcess( + UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections) +{ + UNREFERENCED_PARAMETER(u32NumInputConnections); + UNREFERENCED_PARAMETER(u32NumOutputConnections); + + FLOAT32 *pf32InputFrames, *pf32OutputFrames; + + ATLASSERT(m_bIsLocked); + + // assert that the number of input and output connectins fits our registration properties + ATLASSERT(m_pRegProperties->u32MinInputConnections <= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MaxInputConnections >= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MinOutputConnections <= u32NumOutputConnections); + ATLASSERT(m_pRegProperties->u32MaxOutputConnections >= u32NumOutputConnections); + + // check APO_BUFFER_FLAGS. + switch( ppInputConnections[0]->u32BufferFlags ) + { + case BUFFER_INVALID: + { + ATLASSERT(false); // invalid flag - should never occur. don't do anything. + break; + } + case BUFFER_VALID: + case BUFFER_SILENT: + { + // get input pointer to connection buffer + pf32InputFrames = reinterpret_cast<FLOAT32*>(ppInputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + + // get output pointer to connection buffer + pf32OutputFrames = reinterpret_cast<FLOAT32*>(ppOutputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32OutputFrames) ); + + if (BUFFER_SILENT == ppInputConnections[0]->u32BufferFlags) + { + WriteSilence( pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame() ); + } + + // swap and apply coefficients to the input buffer in-place + if ( + !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && + m_fEnableSwapMFX && + (1 < m_u32SamplesPerFrame) + ) + { + ProcessSwapScale(pf32InputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + m_u32SamplesPerFrame, m_pf32Coefficients ); + } + + // copy to the delay buffer + if ( + !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && + m_fEnableDelayMFX + ) + { + ProcessDelay(pf32OutputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame(), + m_pf32DelayBuffer, + m_nDelayFrames, + &m_iDelayIndex); + + // we don't try to remember silence + ppOutputConnections[0]->u32BufferFlags = BUFFER_VALID; + } + else + { + // copy the memory only if there is an output connection, and input/output pointers are unequal + if ( (0 != u32NumOutputConnections) && + (ppOutputConnections[0]->pBuffer != ppInputConnections[0]->pBuffer) ) + { + CopyFrames( pf32OutputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame() ); + } + + // pass along buffer flags + ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags; + } + + // Set the valid frame count. + ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount; + + break; + } + default: + { + ATLASSERT(false); // invalid flag - should never occur + break; + } + } // switch + +} // APOProcess +#pragma AVRT_CODE_END + +//------------------------------------------------------------------------- +// Description: +// +// Report delay added by the APO between samples given on input +// and samples given on output. +// +// Parameters: +// +// pTime - [out] hundreds-of-nanoseconds of delay added +// +// Return values: +// +// S_OK on success, a failure code on failure +STDMETHODIMP CSwapAPOMFX::GetLatency(HNSTIME* pTime) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + IF_TRUE_ACTION_JUMP(NULL == pTime, hr = E_POINTER, Exit); + + if (IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + *pTime = 0; + } + else + { + *pTime = (m_fEnableDelayMFX ? HNS_DELAY : 0); + } + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Verifies that the APO is ready to process and locks its state if so. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections attached to this APO +// ppInputConnections - [in] connection descriptor of each input connection attached to this APO +// u32NumOutputConnections - [in] number of output connections attached to this APO +// ppOutputConnections - [in] connection descriptor of each output connection attached to this APO +// +// Return values: +// +// S_OK Object is locked and ready to process. +// E_POINTER Invalid pointer passed to function. +// APOERR_INVALID_CONNECTION_FORMAT Invalid connection format. +// APOERR_NUM_CONNECTIONS_INVALID Number of input or output connections is not valid on +// this APO. +STDMETHODIMP CSwapAPOMFX::LockForProcess(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections, + ppInputConnections, u32NumOutputConnections, ppOutputConnections); + IF_FAILED_JUMP(hr, Exit); + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && m_fEnableDelayMFX) + { + m_nDelayFrames = FRAMES_FROM_HNS(HNS_DELAY); + m_iDelayIndex = 0; + + m_pf32DelayBuffer.Free(); + + // Allocate one second's worth of audio + // + // This allocation is being done using CoTaskMemAlloc because the delay is very large + // This introduces a risk of glitches if the delay buffer gets paged out + // + // A more typical approach would be to allocate the memory using AERT_Allocate, which locks the memory + // But for the purposes of this APO, CoTaskMemAlloc suffices, and the risk of glitches is not important + m_pf32DelayBuffer.Allocate(GetSamplesPerFrame() * m_nDelayFrames); + WriteSilence(m_pf32DelayBuffer, m_nDelayFrames, GetSamplesPerFrame()); + if (nullptr == m_pf32DelayBuffer) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + } + +Exit: + return hr; +} + +// The method that this long comment refers to is "Initialize()" +//------------------------------------------------------------------------- +// Description: +// +// Generic initialization routine for APOs. +// +// Parameters: +// +// cbDataSize - [in] the size in bytes of the initialization data. +// pbyData - [in] initialization data specific to this APO +// +// Return values: +// +// S_OK Successful completion. +// E_POINTER Invalid pointer passed to this function. +// E_INVALIDARG Invalid argument +// AEERR_ALREADY_INITIALIZED APO is already initialized +// +// Remarks: +// +// This method initializes the APO. The data is variable length and +// should have the form of: +// +// struct MyAPOInitializationData +// { +// APOInitBaseStruct APOInit; +// ... // add additional fields here +// }; +// +// If the APO needs no initialization or needs no data to initialize +// itself, it is valid to pass NULL as the pbyData parameter and 0 as +// the cbDataSize parameter. +// +// As part of designing an APO, decide which parameters should be +// immutable (set once during initialization) and which mutable (changeable +// during the lifetime of the APO instance). Immutable parameters must +// only be specifiable in the Initialize call; mutable parameters must be +// settable via methods on whichever parameter control interface(s) your +// APO provides. Mutable values should either be set in the initialize +// method (if they are required for proper operation of the APO prior to +// LockForProcess) or default to reasonable values upon initialize and not +// be required to be set before LockForProcess. +// +// Within the mutable parameters, you must also decide which can be changed +// while the APO is locked for processing and which cannot. +// +// All parameters should be considered immutable as a first choice, unless +// there is a specific scenario which requires them to be mutable; similarly, +// no mutable parameters should be changeable while the APO is locked, unless +// a specific scenario requires them to be. Following this guideline will +// simplify the APO's state diagram and implementation and prevent certain +// types of bug. +// +// If a parameter changes the APOs latency or MaxXXXFrames values, it must be +// immutable. +// +// The default version of this function uses no initialization data, but does verify +// the passed parameters and set the m_bIsInitialized member to true. +// +// Note: This method may not be called from a real-time processing thread. +// + +HRESULT CSwapAPOMFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) +{ + HRESULT hr = S_OK; + GUID processingMode; + + IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit); + + if (cbDataSize == sizeof(APOInitSystemEffects2)) + { + // + // Initialize for mode-specific signal processing + // + APOInitSystemEffects2* papoSysFxInit2 = (APOInitSystemEffects2*)pbyData; + + // Save reference to the effects property store. This saves effects settings + // and is the communication medium between this APO and any associated UI. + m_spAPOSystemEffectsProperties = papoSysFxInit2->pAPOSystemEffectsProperties; + + // Windows should pass a valid collection. + ATLASSERT(papoSysFxInit2->pDeviceCollection != nullptr); + IF_TRUE_ACTION_JUMP(papoSysFxInit2->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit); + + // Save the processing mode being initialized. + processingMode = papoSysFxInit2->AudioProcessingMode; + + // There is information in the APOInitSystemEffects2 structure that could help facilitate + // proprietary communication between an APO instance and the KS pin that the APO is initialized on + // Eg, in the case that an APO is implemented as an effect proxy for the effect processing hosted inside + // an driver (either host CPU based or offload DSP based), the example below uses a combination of + // IDeviceTopology, IConnector, and IKsControl interfaces to communicate with the underlying audio driver. + // the following following routine demonstrates how to implement how to communicate to an audio driver from a APO. + ProprietaryCommunicationWithDriver(papoSysFxInit2); + } + else if (cbDataSize == sizeof(APOInitSystemEffects)) + { + // + // Initialize for default signal processing + // + APOInitSystemEffects* papoSysFxInit = (APOInitSystemEffects*)pbyData; + + // Save reference to the effects property store. This saves effects settings + // and is the communication medium between this APO and any associated UI. + m_spAPOSystemEffectsProperties = papoSysFxInit->pAPOSystemEffectsProperties; + + // Assume default processing mode + processingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT; + } + else + { + // Invalid initialization size + hr = E_INVALIDARG; + goto Exit; + } + + // Validate then save the processing mode. Note an endpoint effects APO + // does not depend on the mode. Windows sets the APOInitSystemEffects2 + // AudioProcessingMode member to GUID_NULL for an endpoint effects APO. + IF_TRUE_ACTION_JUMP((processingMode != AUDIO_SIGNALPROCESSINGMODE_DEFAULT && + processingMode != AUDIO_SIGNALPROCESSINGMODE_RAW && + processingMode != AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS && + processingMode != AUDIO_SIGNALPROCESSINGMODE_SPEECH && + processingMode != AUDIO_SIGNALPROCESSINGMODE_MEDIA && + processingMode != AUDIO_SIGNALPROCESSINGMODE_MOVIE), hr = E_INVALIDARG, Exit); + m_AudioProcessingMode = processingMode; + + // + // An APO that implements signal processing more complex than this sample + // would configure its processing for the processingMode determined above. + // If necessary, the APO would also use the IDeviceTopology and IConnector + // interfaces retrieved above to communicate with its counterpart audio + // driver to configure any additional signal processing in the driver and + // associated hardware. + // + + // + // Get current effects settings + // + if (m_spAPOSystemEffectsProperties != NULL) + { + m_fEnableSwapMFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Channel_Swap_MFX, m_AudioProcessingMode); + m_fEnableDelayMFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Delay_MFX, m_AudioProcessingMode); + } + + // + // Register for notification of registry updates + // + hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); + IF_FAILED_JUMP(hr, Exit); + + m_bIsInitialized = true; +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// +// GetEffectsList +// +// Retrieves the list of signal processing effects currently active and +// stores an event to be signaled if the list changes. +// +// Parameters +// +// ppEffectsIds - returns a pointer to a list of GUIDs each identifying a +// class of effect. The caller is responsible for freeing this memory by +// calling CoTaskMemFree. +// +// pcEffects - returns a count of GUIDs in the list. +// +// Event - passes an event handle. The APO signals this event when the list +// of effects changes from the list returned from this function. The APO +// uses this event until either this function is called again or the APO +// is destroyed. The passed handle may be NULL. In this case, the APO +// stops using any previous handle and does not signal an event. +// +// Remarks +// +// An APO imlements this method to allow Windows to discover the current +// effects applied by the APO. The list of effects may depend on what signal +// processing mode the APO initialized (see AudioProcessingMode in the +// APOInitSystemEffects2 structure) as well as any end user configuration. +// +// If there are no effects then the function still succeeds, ppEffectsIds +// returns a NULL pointer, and pcEffects returns a count of 0. +// +STDMETHODIMP CSwapAPOMFX::GetEffectsList(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event) +{ + HRESULT hr; + BOOL effectsLocked = FALSE; + UINT cEffects = 0; + + IF_TRUE_ACTION_JUMP(ppEffectsIds == NULL, hr = E_POINTER, Exit); + IF_TRUE_ACTION_JUMP(pcEffects == NULL, hr = E_POINTER, Exit); + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + effectsLocked = TRUE; + + // Always close existing effects change event handle + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + m_hEffectsChangedEvent = NULL; + } + + // If an event handle was specified, save it here (duplicated to control lifetime) + if (Event != NULL) + { + if (!DuplicateHandle(GetCurrentProcess(), Event, GetCurrentProcess(), &m_hEffectsChangedEvent, EVENT_MODIFY_STATE, FALSE, 0)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + } + + // naked scope to force the initialization of list[] to be after we enter the critical section + { + struct EffectControl + { + GUID effect; + BOOL control; + }; + + EffectControl list[] = + { + { SwapEffectId, m_fEnableSwapMFX }, + { DelayEffectId, m_fEnableDelayMFX }, + }; + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + // count the active effects + for (UINT i = 0; i < ARRAYSIZE(list); i++) + { + if (list[i].control) + { + cEffects++; + } + } + } + + if (0 == cEffects) + { + *ppEffectsIds = NULL; + *pcEffects = 0; + } + else + { + GUID *pEffectsIds = (LPGUID)CoTaskMemAlloc(sizeof(GUID) * cEffects); + if (pEffectsIds == nullptr) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + + // pick up the active effects + UINT j = 0; + for (UINT i = 0; i < ARRAYSIZE(list); i++) + { + if (list[i].control) + { + pEffectsIds[j++] = list[i].effect; + } + } + + *ppEffectsIds = pEffectsIds; + *pcEffects = cEffects; + } + + hr = S_OK; + } + +Exit: + if (effectsLocked) + { + m_EffectsLock.Leave(); + } + return hr; +} + +HRESULT CSwapAPOMFX::ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_pAPOSysFxInit2) +{ + HRESULT hr = S_OK; + CComPtr<IMMDevice> spMyDevice; + CComPtr<IDeviceTopology> spMyDeviceTopology; + CComPtr<IConnector> spMyConnector; + CComPtr<IPart> spMyConnectorPart; + CComPtr<IKsControl> spKsControl; + UINT uKsPinId = 0; + UINT myPartId = 0; + + ULONG ulBytesReturned = 0; + CComHeapPtr<KSMULTIPLE_ITEM> spKsMultipleItem; + KSP_PIN ksPin = {0}; + + UINT nSoftwareIoDeviceInCollection = 0 ; + UINT nSoftwareIoConnectorIndex = 0 ; + + nSoftwareIoDeviceInCollection = _pAPOSysFxInit2->nSoftwareIoDeviceInCollection; + nSoftwareIoConnectorIndex = _pAPOSysFxInit2->nSoftwareIoConnectorIndex; + + // Get the target IMMDevice + hr = _pAPOSysFxInit2->pDeviceCollection->Item(nSoftwareIoDeviceInCollection, &spMyDevice); + IF_FAILED_JUMP(hr, Exit); + + // Instantiate a device topology instance + hr = spMyDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); + IF_FAILED_JUMP(hr, Exit); + + // retrieve connect instance + hr = spMyDeviceTopology->GetConnector(nSoftwareIoConnectorIndex, &spMyConnector); + IF_FAILED_JUMP(hr, Exit); + + // activate IKsControl on the IMMDevice + hr = spMyDevice->Activate(__uuidof(IKsControl), CLSCTX_INPROC_SERVER, NULL, (void**)&spKsControl); + IF_FAILED_JUMP(hr, Exit); + + // get KS pin id + hr = spMyConnector->QueryInterface(__uuidof(IPart), (void**)&spMyConnectorPart); + IF_FAILED_JUMP(hr, Exit); + hr = spMyConnectorPart->GetLocalId(&myPartId); + IF_FAILED_JUMP(hr, Exit); + + uKsPinId = myPartId & 0x0000ffff; + + ksPin.Property.Set = KSPROPSETID_SysVAD; + ksPin.Property.Id = KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS; + ksPin.Property.Flags = KSPROPERTY_TYPE_GET; + ksPin.PinId = uKsPinId; + + // First, get size of array returned by driver + hr = spKsControl->KsProperty( &ksPin.Property, + sizeof(KSP_PIN), + NULL, + 0, + &ulBytesReturned ); + IF_FAILED_JUMP(hr, Exit); + + if( !spKsMultipleItem.AllocateBytes(ulBytesReturned) ) + { + hr = E_OUTOFMEMORY; + IF_FAILED_JUMP(hr, Exit); + } + + // Second, now get the active effects from the driver + hr = spKsControl->KsProperty( &ksPin.Property, + sizeof(KSP_PIN), + spKsMultipleItem, + ulBytesReturned, + &ulBytesReturned ); + IF_FAILED_JUMP(hr, Exit); + + // Upon successful return, effect guids could be found in the memory following (spKsMultipleItem.m_pData + 1) + // and effectcount could be found in spKsMultipleItem->Count; + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Implementation of IMMNotificationClient::OnPropertyValueChanged +// +// Parameters: +// +// pwstrDeviceId - [in] the id of the device whose property has changed +// key - [in] the property that changed +// +// Return values: +// +// Ignored by caller +// +// Remarks: +// +// This method is called asynchronously. No UI work should be done here. +// +HRESULT CSwapAPOMFX::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pwstrDeviceId); + + if (!m_spAPOSystemEffectsProperties) + { + return hr; + } + + // If either the master disable or our APO's enable properties changed... + if (PK_EQUAL(key, PKEY_Endpoint_Enable_Channel_Swap_MFX) || + PK_EQUAL(key, PKEY_Endpoint_Enable_Delay_MFX) || + PK_EQUAL(key, PKEY_AudioEndpoint_Disable_SysFx)) + { + LONG nChanges = 0; + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + + struct KeyControl + { + PROPERTYKEY key; + LONG *value; + }; + + KeyControl controls[] = + { + { PKEY_Endpoint_Enable_Channel_Swap_MFX, &m_fEnableSwapMFX }, + { PKEY_Endpoint_Enable_Delay_MFX, &m_fEnableDelayMFX }, + }; + + for (int i = 0; i < ARRAYSIZE(controls); i++) + { + LONG fOldValue; + LONG fNewValue = true; + + // Get the state of whether channel swap MFX is enabled or not + fNewValue = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, controls[i].key, m_AudioProcessingMode); + + // Swap in the new setting + fOldValue = InterlockedExchange(controls[i].value, fNewValue); + + if (fNewValue != fOldValue) + { + nChanges++; + } + } + + // If anything changed and a change event handle exists + if ((nChanges > 0) && (m_hEffectsChangedEvent != NULL)) + { + SetEvent(m_hEffectsChangedEvent); + } + + m_EffectsLock.Leave(); + } + + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Destructor. +// +// Parameters: +// +// void +// +// Return values: +// +// void +// +// Remarks: +// +// This method deletes whatever was allocated. +// +// This method may not be called from a real-time processing thread. +// +CSwapAPOMFX::~CSwapAPOMFX(void) +{ + if (m_bIsInitialized) + { + // + // unregister for callbacks + // + if (m_spEnumerator != NULL) + { + m_spEnumerator->UnregisterEndpointNotificationCallback(this); + } + } + + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + } + + // Free locked memory allocations + if (NULL != m_pf32Coefficients) + { + AERT_Free(m_pf32Coefficients); + m_pf32Coefficients = NULL; + } +} // ~CSwapAPOMFX + + +//------------------------------------------------------------------------- +// Description: +// +// Validates input/output format pair during LockForProcess. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections attached to this APO +// ppInputConnections - [in] format of each input connection attached to this APO +// u32NumOutputConnections - [in] number of output connections attached to this APO +// ppOutputConnections - [in] format of each output connection attached to this APO +// +// Return values: +// +// S_OK Connections are valid. +// +// See Also: +// +// CBaseAudioProcessingObject::LockForProcess +// +// Remarks: +// +// This method is an internal call that is called by the default implementation of +// CBaseAudioProcessingObject::LockForProcess(). This is called after the connections +// are validated for simple conformance to the APO's registration properties. It may be +// used to verify that the APO is initialized properly and that the connections that are passed +// agree with the data used for initialization. Any failure code passed back from this +// function will get returned by LockForProcess, and cause it to fail. +// +// By default, this routine just ASSERTS and returns S_OK. +// +HRESULT CSwapAPOMFX::ValidateAndCacheConnectionInfo(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_DESCRIPTOR** ppOutputConnections) +{ + ASSERT_NONREALTIME(); + HRESULT hResult; + CComPtr<IAudioMediaType> pFormat; + UNCOMPRESSEDAUDIOFORMAT UncompInputFormat, UncompOutputFormat; + FLOAT32 f32InverseChannelCount; + + UNREFERENCED_PARAMETER(u32NumInputConnections); + UNREFERENCED_PARAMETER(u32NumOutputConnections); + + _ASSERTE(!m_bIsLocked); + _ASSERTE(((0 == u32NumInputConnections) || (NULL != ppInputConnections)) && + ((0 == u32NumOutputConnections) || (NULL != ppOutputConnections))); + + EnterCriticalSection(&m_CritSec); + + // get the uncompressed formats and channel masks + hResult = ppInputConnections[0]->pFormat->GetUncompressedAudioFormat(&UncompInputFormat); + IF_FAILED_JUMP(hResult, Exit); + + hResult = ppOutputConnections[0]->pFormat->GetUncompressedAudioFormat(&UncompOutputFormat); + IF_FAILED_JUMP(hResult, Exit); + + // Since we haven't overridden the IsIn{Out}putFormatSupported APIs in this example, this APO should + // always have input channel count == output channel count. The sampling rates should also be eqaul, + // and formats 32-bit float. + _ASSERTE(UncompOutputFormat.fFramesPerSecond == UncompInputFormat.fFramesPerSecond); + _ASSERTE(UncompOutputFormat. dwSamplesPerFrame == UncompInputFormat.dwSamplesPerFrame); + + // Allocate some locked memory. We will use these as scaling coefficients during APOProcess->ProcessSwapScale + hResult = AERT_Allocate(sizeof(FLOAT32)*m_u32SamplesPerFrame, (void**)&m_pf32Coefficients); + IF_FAILED_JUMP(hResult, Exit); + + // Set scalars to decrease volume from 1.0 to 1.0/N where N is the number of channels + // starting with the first channel. + f32InverseChannelCount = 1.0f/m_u32SamplesPerFrame; + for (UINT16 u16Index=0; u16Index<m_u32SamplesPerFrame; u16Index++) + { + m_pf32Coefficients[u16Index] = 1.0f - (FLOAT32)(f32InverseChannelCount)*u16Index; + } + + +Exit: + LeaveCriticalSection(&m_CritSec); + return hResult;} + +// ---------------------------------------------------------------------- +// ---------------------------------------------------------------------- +// IAudioSystemEffectsCustomFormats implementation + +// +// For demonstration purposes we will add 44.1KHz, 16-bit stereo and 48KHz, 16-bit +// stereo formats. These formats should already be available in mmsys.cpl. We +// embellish the labels to make it obvious that these formats are coming from +// the APO. +// + +// +// Note that the IAudioSystemEffectsCustomFormats interface, if present, is invoked only on APOs +// that attach directly to the connector in the 'DEFAULT' mode streaming graph. For example: +// - APOs implementing global effects +// - APOs implementing endpoint effects +// - APOs implementing DEFAULT mode effects which attach directly to a connector supporting DEFAULT processing mode + +struct CUSTOM_FORMAT_ITEM +{ + WAVEFORMATEXTENSIBLE wfxFmt; + LPCWSTR pwszRep; +}; + +#define STATIC_KSDATAFORMAT_SUBTYPE_AC3\ + DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_DOLBY_AC3_SPDIF) +DEFINE_GUIDSTRUCT("00000092-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_AC3); +#define KSDATAFORMAT_SUBTYPE_AC3 DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_AC3) + +CUSTOM_FORMAT_ITEM _rgCustomFormats[] = +{ + {{ WAVE_FORMAT_EXTENSIBLE, 2, 44100, 176400, 4, 16, sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX), 16, KSAUDIO_SPEAKER_STEREO, KSDATAFORMAT_SUBTYPE_PCM}, L"Custom #1 (really 44.1 KHz, 16-bit, stereo)"}, + {{ WAVE_FORMAT_EXTENSIBLE, 2, 48000, 192000, 4, 16, sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX), 16, KSAUDIO_SPEAKER_STEREO, KSDATAFORMAT_SUBTYPE_PCM}, L"Custom #2 (really 48 KHz, 16-bit, stereo)"}, + {{ WAVE_FORMAT_EXTENSIBLE, 2, 48000, 192000, 4, 16, sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX), 16, KSAUDIO_SPEAKER_STEREO, KSDATAFORMAT_SUBTYPE_AC3}, L"Custom #3 (really 48 KHz AC-3)"} +}; + +#define _cCustomFormats (ARRAYSIZE(_rgCustomFormats)) + +//------------------------------------------------------------------------- +// Description: +// +// Implementation of IAudioSystemEffectsCustomFormats::GetFormatCount +// +// Parameters: +// +// pcFormats - [out] receives the number of formats to be added +// +// Return values: +// +// S_OK Success +// E_POINTER Null pointer passed +// +// Remarks: +// +STDMETHODIMP CSwapAPOMFX::GetFormatCount +( + UINT* pcFormats +) +{ + if (pcFormats == NULL) + return E_POINTER; + + *pcFormats = _cCustomFormats; + return S_OK; +} + +//------------------------------------------------------------------------- +// Description: +// +// Implementation of IAudioSystemEffectsCustomFormats::GetFormat +// +// Parameters: +// +// nFormat - [in] which format is being requested +// IAudioMediaType - [in] address of a variable that will receive a ptr +// to a new IAudioMediaType object +// +// Return values: +// +// S_OK Success +// E_INVALIDARG nFormat is out of range +// E_POINTER Null pointer passed +// +// Remarks: +// +STDMETHODIMP CSwapAPOMFX::GetFormat +( + UINT nFormat, + IAudioMediaType** ppFormat +) +{ + HRESULT hr; + + IF_TRUE_ACTION_JUMP((nFormat >= _cCustomFormats), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP((ppFormat == NULL), hr = E_POINTER, Exit); + + *ppFormat = NULL; + + hr = CreateAudioMediaType( (const WAVEFORMATEX*)&_rgCustomFormats[nFormat].wfxFmt, + sizeof(_rgCustomFormats[nFormat].wfxFmt), + ppFormat); + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Implementation of IAudioSystemEffectsCustomFormats::GetFormatRepresentation +// +// Parameters: +// +// nFormat - [in] which format is being requested +// ppwstrFormatRep - [in] address of a variable that will receive a ptr +// to a new string description of the requested format +// +// Return values: +// +// S_OK Success +// E_INVALIDARG nFormat is out of range +// E_POINTER Null pointer passed +// +// Remarks: +// +STDMETHODIMP CSwapAPOMFX::GetFormatRepresentation +( + UINT nFormat, + _Outptr_ LPWSTR* ppwstrFormatRep +) +{ + HRESULT hr; + size_t cbRep; + LPWSTR pwstrLocal; + + IF_TRUE_ACTION_JUMP((nFormat >= _cCustomFormats), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP((ppwstrFormatRep == NULL), hr = E_POINTER, Exit); + + cbRep = (wcslen(_rgCustomFormats[nFormat].pwszRep) + 1) * sizeof(WCHAR); + + pwstrLocal = (LPWSTR)CoTaskMemAlloc(cbRep); + IF_TRUE_ACTION_JUMP((pwstrLocal == NULL), hr = E_OUTOFMEMORY, Exit); + + hr = StringCbCopyW(pwstrLocal, cbRep, _rgCustomFormats[nFormat].pwszRep); + if (FAILED(hr)) + { + CoTaskMemFree(pwstrLocal); + } + else + { + *ppwstrFormatRep = pwstrLocal; + } + +Exit: + return hr; +} diff --git a/audio/sysvad/SwapAPO/APO/swapaposfx.cpp b/audio/sysvad/SwapAPO/APO/swapaposfx.cpp new file mode 100644 index 00000000..32ddb7fc --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/swapaposfx.cpp @@ -0,0 +1,677 @@ +// +// SwapAPOSFX.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Implementation of CSwapAPOSFX +// + +#include <atlbase.h> +#include <atlcom.h> +#include <atlcoll.h> +#include <atlsync.h> +#include <mmreg.h> + +#include <audioenginebaseapo.h> +#include <baseaudioprocessingobject.h> +#include <resource.h> + +#include <float.h> + +#include "SwapAPO.h" +#include <devicetopology.h> +#include <CustomPropKeys.h> + + +// Static declaration of the APO_REG_PROPERTIES structure +// associated with this APO. The number in <> brackets is the +// number of IIDs supported by this APO. If more than one, then additional +// IIDs are added at the end +#pragma warning (disable : 4815) +const AVRT_DATA CRegAPOProperties<1> CSwapAPOSFX::sm_RegProperties( + __uuidof(SwapAPOSFX), // clsid of this APO + L"CSwapAPOSFX", // friendly name of this APO + L"Copyright (c) Microsoft Corporation", // copyright info + 1, // major version # + 0, // minor version # + __uuidof(ISwapAPOSFX) // iid of primary interface + +// +// If you need to change any of these attributes, uncomment everything up to +// the point that you need to change something. If you need to add IIDs, uncomment +// everything and add additional IIDs at the end. +// +// , DEFAULT_APOREG_FLAGS +// , DEFAULT_APOREG_MININPUTCONNECTIONS +// , DEFAULT_APOREG_MAXINPUTCONNECTIONS +// , DEFAULT_APOREG_MINOUTPUTCONNECTIONS +// , DEFAULT_APOREG_MAXOUTPUTCONNECTIONS +// , DEFAULT_APOREG_MAXINSTANCES +// + ); + +#pragma AVRT_CODE_BEGIN +//------------------------------------------------------------------------- +// Description: +// +// Do the actual processing of data. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections +// ppInputConnections - [in] pointer to list of input APO_CONNECTION_PROPERTY pointers +// u32NumOutputConnections - [in] number of output connections +// ppOutputConnections - [in] pointer to list of output APO_CONNECTION_PROPERTY pointers +// +// Return values: +// +// void +// +// Remarks: +// +// This function processes data in a manner dependent on the implementing +// object. This routine can not fail and can not block, or call any other +// routine that blocks, or touch pagable memory. +// +STDMETHODIMP_(void) CSwapAPOSFX::APOProcess( + UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections) +{ + UNREFERENCED_PARAMETER(u32NumInputConnections); + UNREFERENCED_PARAMETER(u32NumOutputConnections); + + FLOAT32 *pf32InputFrames, *pf32OutputFrames; + + ATLASSERT(m_bIsLocked); + + // assert that the number of input and output connectins fits our registration properties + ATLASSERT(m_pRegProperties->u32MinInputConnections <= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MaxInputConnections >= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MinOutputConnections <= u32NumOutputConnections); + ATLASSERT(m_pRegProperties->u32MaxOutputConnections >= u32NumOutputConnections); + + // check APO_BUFFER_FLAGS. + switch( ppInputConnections[0]->u32BufferFlags ) + { + case BUFFER_INVALID: + { + ATLASSERT(false); // invalid flag - should never occur. don't do anything. + break; + } + case BUFFER_VALID: + case BUFFER_SILENT: + { + // get input pointer to connection buffer + pf32InputFrames = reinterpret_cast<FLOAT32*>(ppInputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + + // get output pointer to connection buffer + pf32OutputFrames = reinterpret_cast<FLOAT32*>(ppOutputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_WRITE_POINTER(pf32OutputFrames) ); + + if (BUFFER_SILENT == ppInputConnections[0]->u32BufferFlags) + { + WriteSilence( pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame() ); + } + + // swap the input buffer in-place + if ( + !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && + m_fEnableSwapSFX + ) + { + ProcessSwap(pf32InputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + m_u32SamplesPerFrame); + } + + // copy to the delay buffer + if ( + !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && + m_fEnableDelaySFX + ) + { + ProcessDelay(pf32OutputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame(), + m_pf32DelayBuffer, + m_nDelayFrames, + &m_iDelayIndex); + + // we don't try to remember silence + ppOutputConnections[0]->u32BufferFlags = BUFFER_VALID; + } + else + { + // copy the memory only if there is an output connection, and input/output pointers are unequal + if ( (0 != u32NumOutputConnections) && + (ppOutputConnections[0]->pBuffer != ppInputConnections[0]->pBuffer) ) + { + CopyFrames( pf32OutputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame() ); + } + + // pass along buffer flags + ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags; + } + + // Set the valid frame count. + ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount; + + break; + } + default: + { + ATLASSERT(false); // invalid flag - should never occur + break; + } + } // switch + +} // APOProcess +#pragma AVRT_CODE_END + +//------------------------------------------------------------------------- +// Description: +// +// Report delay added by the APO between samples given on input +// and samples given on output. +// +// Parameters: +// +// pTime - [out] hundreds-of-nanoseconds of delay added +// +// Return values: +// +// S_OK on success, a failure code on failure +STDMETHODIMP CSwapAPOSFX::GetLatency(HNSTIME* pTime) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + IF_TRUE_ACTION_JUMP(NULL == pTime, hr = E_POINTER, Exit); + + if (IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + *pTime = 0; + } + else + { + *pTime = (m_fEnableDelaySFX ? HNS_DELAY : 0); + } + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Verifies that the APO is ready to process and locks its state if so. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections attached to this APO +// ppInputConnections - [in] connection descriptor of each input connection attached to this APO +// u32NumOutputConnections - [in] number of output connections attached to this APO +// ppOutputConnections - [in] connection descriptor of each output connection attached to this APO +// +// Return values: +// +// S_OK Object is locked and ready to process. +// E_POINTER Invalid pointer passed to function. +// APOERR_INVALID_CONNECTION_FORMAT Invalid connection format. +// APOERR_NUM_CONNECTIONS_INVALID Number of input or output connections is not valid on +// this APO. +STDMETHODIMP CSwapAPOSFX::LockForProcess(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections, + ppInputConnections, u32NumOutputConnections, ppOutputConnections); + IF_FAILED_JUMP(hr, Exit); + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && m_fEnableDelaySFX) + { + m_nDelayFrames = FRAMES_FROM_HNS(HNS_DELAY); + m_iDelayIndex = 0; + + m_pf32DelayBuffer.Free(); + + // Allocate one second's worth of audio + // + // This allocation is being done using CoTaskMemAlloc because the delay is very large + // This introduces a risk of glitches if the delay buffer gets paged out + // + // A more typical approach would be to allocate the memory using AERT_Allocate, which locks the memory + // But for the purposes of this APO, CoTaskMemAlloc suffices, and the risk of glitches is not important + m_pf32DelayBuffer.Allocate(GetSamplesPerFrame() * m_nDelayFrames); + + WriteSilence(m_pf32DelayBuffer, m_nDelayFrames, GetSamplesPerFrame()); + if (nullptr == m_pf32DelayBuffer) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + } + +Exit: + return hr; +} + +// The method that this long comment refers to is "Initialize()" +//------------------------------------------------------------------------- +// Description: +// +// Generic initialization routine for APOs. +// +// Parameters: +// +// cbDataSize - [in] the size in bytes of the initialization data. +// pbyData - [in] initialization data specific to this APO +// +// Return values: +// +// S_OK Successful completion. +// E_POINTER Invalid pointer passed to this function. +// E_INVALIDARG Invalid argument +// AEERR_ALREADY_INITIALIZED APO is already initialized +// +// Remarks: +// +// This method initializes the APO. The data is variable length and +// should have the form of: +// +// struct MyAPOInitializationData +// { +// APOInitBaseStruct APOInit; +// ... // add additional fields here +// }; +// +// If the APO needs no initialization or needs no data to initialize +// itself, it is valid to pass NULL as the pbyData parameter and 0 as +// the cbDataSize parameter. +// +// As part of designing an APO, decide which parameters should be +// immutable (set once during initialization) and which mutable (changeable +// during the lifetime of the APO instance). Immutable parameters must +// only be specifiable in the Initialize call; mutable parameters must be +// settable via methods on whichever parameter control interface(s) your +// APO provides. Mutable values should either be set in the initialize +// method (if they are required for proper operation of the APO prior to +// LockForProcess) or default to reasonable values upon initialize and not +// be required to be set before LockForProcess. +// +// Within the mutable parameters, you must also decide which can be changed +// while the APO is locked for processing and which cannot. +// +// All parameters should be considered immutable as a first choice, unless +// there is a specific scenario which requires them to be mutable; similarly, +// no mutable parameters should be changeable while the APO is locked, unless +// a specific scenario requires them to be. Following this guideline will +// simplify the APO's state diagram and implementation and prevent certain +// types of bug. +// +// If a parameter changes the APOs latency or MaxXXXFrames values, it must be +// immutable. +// +// The default version of this function uses no initialization data, but does verify +// the passed parameters and set the m_bIsInitialized member to true. +// +// Note: This method may not be called from a real-time processing thread. +// + +HRESULT CSwapAPOSFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) +{ + HRESULT hr = S_OK; + CComPtr<IMMDevice> spMyDevice; + CComPtr<IDeviceTopology> spMyDeviceTopology; + CComPtr<IConnector> spMyConnector; + GUID processingMode; + + IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit); + + if (cbDataSize == sizeof(APOInitSystemEffects2)) + { + // + // Initialize for mode-specific signal processing + // + APOInitSystemEffects2* papoSysFxInit2 = (APOInitSystemEffects2*)pbyData; + + // Save reference to the effects property store. This saves effects settings + // and is the communication medium between this APO and any associated UI. + m_spAPOSystemEffectsProperties = papoSysFxInit2->pAPOSystemEffectsProperties; + + // Windows should pass a valid collection. + ATLASSERT(papoSysFxInit2->pDeviceCollection != nullptr); + IF_TRUE_ACTION_JUMP(papoSysFxInit2->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit); + + // Get the IDeviceTopology and IConnector interfaces to communicate with this + // APO's counterpart audio driver. This can be used for any proprietary + // communication. + hr = papoSysFxInit2->pDeviceCollection->Item(papoSysFxInit2->nSoftwareIoDeviceInCollection, &spMyDevice); + IF_FAILED_JUMP(hr, Exit); + + hr = spMyDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); + IF_FAILED_JUMP(hr, Exit); + + hr = spMyDeviceTopology->GetConnector(papoSysFxInit2->nSoftwareIoConnectorIndex, &spMyConnector); + IF_FAILED_JUMP(hr, Exit); + + // Save the processing mode being initialized. + processingMode = papoSysFxInit2->AudioProcessingMode; + + } + else if (cbDataSize == sizeof(APOInitSystemEffects)) + { + // + // Initialize for default signal processing + // + APOInitSystemEffects* papoSysFxInit = (APOInitSystemEffects*)pbyData; + + // Save reference to the effects property store. This saves effects settings + // and is the communication medium between this APO and any associated UI. + m_spAPOSystemEffectsProperties = papoSysFxInit->pAPOSystemEffectsProperties; + + // Assume default processing mode + processingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT; + } + else + { + // Invalid initialization size + hr = E_INVALIDARG; + goto Exit; + } + + // Validate then save the processing mode. Note an endpoint effects APO + // does not depend on the mode. Windows sets the APOInitSystemEffects2 + // AudioProcessingMode member to GUID_NULL for an endpoint effects APO. + IF_TRUE_ACTION_JUMP((processingMode != AUDIO_SIGNALPROCESSINGMODE_DEFAULT && + processingMode != AUDIO_SIGNALPROCESSINGMODE_RAW && + processingMode != AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS && + processingMode != AUDIO_SIGNALPROCESSINGMODE_SPEECH && + processingMode != AUDIO_SIGNALPROCESSINGMODE_MEDIA && + processingMode != AUDIO_SIGNALPROCESSINGMODE_MOVIE), hr = E_INVALIDARG, Exit); + m_AudioProcessingMode = processingMode; + + // + // An APO that implements signal processing more complex than this sample + // would configure its processing for the processingMode determined above. + // If necessary, the APO would also use the IDeviceTopology and IConnector + // interfaces retrieved above to communicate with its counterpart audio + // driver to configure any additional signal processing in the driver and + // associated hardware. + // + + // + // Get the current values + // + if (m_spAPOSystemEffectsProperties != NULL) + { + m_fEnableSwapSFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Channel_Swap_SFX, m_AudioProcessingMode); + m_fEnableDelaySFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Delay_SFX, m_AudioProcessingMode); + } + + // + // Register for notification of registry updates + // + hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); + IF_FAILED_JUMP(hr, Exit); + + m_bIsInitialized = true; + + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// +// GetEffectsList +// +// Retrieves the list of signal processing effects currently active and +// stores an event to be signaled if the list changes. +// +// Parameters +// +// ppEffectsIds - returns a pointer to a list of GUIDs each identifying a +// class of effect. The caller is responsible for freeing this memory by +// calling CoTaskMemFree. +// +// pcEffects - returns a count of GUIDs in the list. +// +// Event - passes an event handle. The APO signals this event when the list +// of effects changes from the list returned from this function. The APO +// uses this event until either this function is called again or the APO +// is destroyed. The passed handle may be NULL. In this case, the APO +// stops using any previous handle and does not signal an event. +// +// Remarks +// +// An APO imlements this method to allow Windows to discover the current +// effects applied by the APO. The list of effects may depend on what signal +// processing mode the APO initialized (see AudioProcessingMode in the +// APOInitSystemEffects2 structure) as well as any end user configuration. +// +// If there are no effects then the function still succeeds, ppEffectsIds +// returns a NULL pointer, and pcEffects returns a count of 0. +// +STDMETHODIMP CSwapAPOSFX::GetEffectsList(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event) +{ + HRESULT hr; + BOOL effectsLocked = FALSE; + UINT cEffects = 0; + + IF_TRUE_ACTION_JUMP(ppEffectsIds == NULL, hr = E_POINTER, Exit); + IF_TRUE_ACTION_JUMP(pcEffects == NULL, hr = E_POINTER, Exit); + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + effectsLocked = TRUE; + + // Always close existing effects change event handle + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + m_hEffectsChangedEvent = NULL; + } + + // If an event handle was specified, save it here (duplicated to control lifetime) + if (Event != NULL) + { + if (!DuplicateHandle(GetCurrentProcess(), Event, GetCurrentProcess(), &m_hEffectsChangedEvent, EVENT_MODIFY_STATE, FALSE, 0)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + } + + // naked scope to force the initialization of list[] to be after we enter the critical section + { + struct EffectControl + { + GUID effect; + BOOL control; + }; + + EffectControl list[] = + { + { SwapEffectId, m_fEnableSwapSFX }, + { DelayEffectId, m_fEnableDelaySFX }, + }; + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + // count the active effects + for (UINT i = 0; i < ARRAYSIZE(list); i++) + { + if (list[i].control) + { + cEffects++; + } + } + } + + if (0 == cEffects) + { + *ppEffectsIds = NULL; + *pcEffects = 0; + } + else + { + GUID *pEffectsIds = (LPGUID)CoTaskMemAlloc(sizeof(GUID) * cEffects); + if (pEffectsIds == nullptr) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + + // pick up the active effects + UINT j = 0; + for (UINT i = 0; i < ARRAYSIZE(list); i++) + { + if (list[i].control) + { + pEffectsIds[j++] = list[i].effect; + } + } + + *ppEffectsIds = pEffectsIds; + *pcEffects = cEffects; + } + + hr = S_OK; + } + +Exit: + if (effectsLocked) + { + m_EffectsLock.Leave(); + } + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// +// +// Parameters: +// +// +// +// Return values: +// +// +// +// Remarks: +// +// +HRESULT CSwapAPOSFX::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pwstrDeviceId); + + if (!m_spAPOSystemEffectsProperties) + { + return hr; + } + + // If either the master disable or our APO's enable properties changed... + if (PK_EQUAL(key, PKEY_Endpoint_Enable_Channel_Swap_SFX) || + PK_EQUAL(key, PKEY_Endpoint_Enable_Delay_SFX) || + PK_EQUAL(key, PKEY_AudioEndpoint_Disable_SysFx)) + { + LONG nChanges = 0; + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + + struct KeyControl + { + PROPERTYKEY key; + LONG *value; + }; + + KeyControl controls[] = + { + { PKEY_Endpoint_Enable_Channel_Swap_SFX, &m_fEnableSwapSFX }, + { PKEY_Endpoint_Enable_Delay_SFX, &m_fEnableDelaySFX }, + }; + + for (int i = 0; i < ARRAYSIZE(controls); i++) + { + LONG fOldValue; + LONG fNewValue = true; + + // Get the state of whether channel swap MFX is enabled or not + fNewValue = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, controls[i].key, m_AudioProcessingMode); + + // Swap in the new setting + fOldValue = InterlockedExchange(controls[i].value, fNewValue); + + if (fNewValue != fOldValue) + { + nChanges++; + } + } + + // If anything changed and a change event handle exists + if ((nChanges > 0) && (m_hEffectsChangedEvent != NULL)) + { + SetEvent(m_hEffectsChangedEvent); + } + + m_EffectsLock.Leave(); + } + + return hr; +} + + +//------------------------------------------------------------------------- +// Description: +// +// Destructor. +// +// Parameters: +// +// void +// +// Return values: +// +// void +// +// Remarks: +// +// This method deletes whatever was allocated. +// +// This method may not be called from a real-time processing thread. +// +CSwapAPOSFX::~CSwapAPOSFX(void) +{ + // + // unregister for callbacks + // + if (m_bIsInitialized) + { + m_spEnumerator->UnregisterEndpointNotificationCallback(this); + } + + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + } +} // ~CSwapAPOSFX diff --git a/audio/sysvad/SwapAPO/Inc/CommonMacros.h b/audio/sysvad/SwapAPO/Inc/CommonMacros.h new file mode 100644 index 00000000..097c6d47 --- /dev/null +++ b/audio/sysvad/SwapAPO/Inc/CommonMacros.h @@ -0,0 +1,337 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: CommonMacros.h +// +// Abstract: Useful macros +// +// ---------------------------------------------------------------------------- + + +#pragma once +#include <windef.h> +#include <windows.h> + + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, jump to the given label. +// +// Parameters: +// +// condition - [in] code that fits in if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_JUMP(condition, label) \ + if (condition) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to FALSE, jump to the given label. +// +// Parameters: +// +// condition - [in] code that fits in if statement +// label - [in] label to jump if condition is met +// +#define IF_FALSE_JUMP(condition, label) \ + if (!condition) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the hresult passed FAILED, jump to the given label. +// +// Parameters: +// +// _hresult - [in] Value to check +// label - [in] label to jump if condition is met +// +#define IF_FAILED_JUMP(_hresult, label) \ + if (FAILED(_hresult)) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the hresult passed SUCCEEDED, jump to the given label. +// +// Parameters: +// +// _hresult - [in] Value to check +// label - [in] label to jump if condition is met +// +#define IF_SUCCEEDED_JUMP(_hresult, label) \ + if (SUCCEEDED(_hresult)) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, perform the given statement +// then jump to the given label. +// +// Parameters: +// +// condition - [in] Code that fits in if statement +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_ACTION_JUMP(condition, action, label) \ + if (condition) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the hresult FAILED, perform the given statement then jump to +// the given label. +// +// Parameters: +// +// _hresult - [in] Value to check +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_FAILED_ACTION_JUMP(_hresult, action, label) \ + if (FAILED(_hresult)) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Closes a handle and assigns NULL. +// +// Parameters: +// +// h - [in] handle to close +// +#define SAFE_CLOSE_HANDLE(h) \ + if (NULL != h) \ + { \ + CloseHandle(h); \ + h = NULL; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Addref an interface pointer +// +// Parameters: +// +// p - [in] object to addref +// +#define SAFE_ADDREF(p) \ + if (NULL != p) \ + { \ + (p)->AddRef();; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Releases an interface pointer and assigns NULL. +// +// Parameters: +// +// p - [in] object to release +// +#define SAFE_RELEASE(p) \ + if (NULL != p) \ + { \ + (p)->Release(); \ + (p) = NULL; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Deletes a pointer and assigns NULL. Do not check for NULL because +// the default delete operator checks for it. +// +// Parameters: +// +// p - [in] object to delete +// +#define SAFE_DELETE(p) \ + delete p; \ + p = NULL; + +//------------------------------------------------------------------------- +// Description: +// +// Deletes an array pointer and assigns NULL. Do not check for NULL because +// the default delete operator checks for it. +// +// Parameters: +// +// p - [in] Array to delete +// +#define SAFE_DELETE_ARRAY(p) \ + delete [] p; \ + p = NULL; + +//------------------------------------------------------------------------- +// Description: +// +// Frees a block of memory allocated by CoTaskMemAlloc and assigns NULL to +// the pointer +// +// Parameters: +// +// p - [in] Pointer to memory to free +// +#define SAFE_COTASKMEMFREE(p) \ + if (NULL != p) \ + { \ + CoTaskMemFree(p); \ + (p) = NULL; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Frees a DLL loaded with LoadLibrary and assigns NULL to the handle +// +// Parameters: +// +// h - [in] Handle to DLL to free +// +#define SAFE_FREELIBRARY(h) \ + if (NULL != h) \ + { \ + FreeLibrary(h); \ + (h) = NULL; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Used to validate a read pointer +// +// Parameters: +// +// p - [in] read pointer. +// s - [in] size of memory in bytes pointed to by p. +// +#define IS_VALID_READ_POINTER(p, s) ((NULL != p) || (0 == s)) + +//------------------------------------------------------------------------- +// Description: +// +// Used to validate a write pointer +// +// Parameters: +// +// p - [in] write pointer. +// s - [in] size of memory in bytes pointed to by p. +// +#define IS_VALID_WRITE_POINTER(p, s) ((NULL != p) || (0 == s)) + +//------------------------------------------------------------------------- +// Description: +// +// Used to validate a read pointer of a particular type +// +// Parameters: +// +// p - [in] typed read pointer +// +#define IS_VALID_TYPED_READ_POINTER(p) IS_VALID_READ_POINTER((p), sizeof *(p)) + +//------------------------------------------------------------------------- +// Description: +// +// Used to validate a write pointer of a particular type +// +// Parameters: +// +// p - [in] typed write pointer +// +#define IS_VALID_TYPED_WRITE_POINTER(p) IS_VALID_WRITE_POINTER((p), sizeof *(p)) + +// --------------------------------------------------------------------------- +// Macros that wrap windows messages. Similar to those in windowsX.h and +// commctrl.h +// +#if !defined Static_SetIcon +#define Static_SetIcon(hwnd, hi) \ + (BOOL)SNDMSG((hwnd), STM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)(hi)) +#endif + +#define TrackBar_SetTickFrequency(hwnd, f) \ + (BOOL)SNDMSG((hwnd), TBM_SETTICFREQ, (WPARAM)(f), 0) + +#define TrackBar_SetBuddy(hwnd, f, hbud) \ + (HWND)SNDMSG((hwnd), TBM_SETBUDDY, (WPARAM)(f), (LPARAM)hbud) + +#define TrackBar_GetPos(hwnd) \ + (int)SNDMSG((hwnd), TBM_GETPOS, 0, 0) + +#define TrackBar_SetPos(hwnd, pos) \ + SNDMSG((hwnd), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pos) + +#define TrackBar_SetRange(hwnd, min, max) \ + SNDMSG((hwnd), TBM_SETRANGE , (WPARAM)TRUE, (LPARAM) MAKELONG(min, max)) + +#define TrackBar_SetThumbLength(hwnd, l) \ + SNDMSG((hwnd), TBM_SETTHUMBLENGTH, (WPARAM)l, 0); + +#define TrackBar_SetPageSize(hwnd, n) \ + SNDMSG((hwnd), TBM_SETPAGESIZE, 0, (LPARAM)n) + +#define Window_GetFont(hwnd) \ + (HFONT)SNDMSG((hwnd), WM_GETFONT, 0, 0) + +#define Window_SetFont(hwnd, font) \ + SNDMSG((hwnd), WM_SETFONT, (WPARAM)font, FALSE) + + +// ---------------------------------------------------------------------- +// A struct for holding a rect in easier terms than a RECT struct +// +struct SRECT +{ + int x, y, w, h; + SRECT() + { + x = y = w = h = 0; + } + SRECT(int X, int Y, int W, int H) + { + x = X; y = Y; w = W; h = H; + } + SRECT(RECT* prc) + { + x = prc->left; + y = prc->top; + w = prc->right - prc->left; + h = prc->bottom - prc->top; + } +}; + +#define HNS_PER_SECOND (10ull * 1000ull * 1000ull) diff --git a/audio/sysvad/SwapAPO/Inc/CustomPropKeys.h b/audio/sysvad/SwapAPO/Inc/CustomPropKeys.h new file mode 100644 index 00000000..dab286a5 --- /dev/null +++ b/audio/sysvad/SwapAPO/Inc/CustomPropKeys.h @@ -0,0 +1,39 @@ +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +#pragma once + +// header files for imported files +#include "propidl.h" + +#ifdef DEFINE_PROPERTYKEY +#undef DEFINE_PROPERTYKEY +#endif + +#ifdef INITGUID +#define DEFINE_PROPERTYKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) EXTERN_C const PROPERTYKEY name = { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid } +#else +#define DEFINE_PROPERTYKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) EXTERN_C const PROPERTYKEY name +#endif // INITGUID + +// ---------------------------------------------------------------------- +// +// PKEY_Endpoint_Enable_Channel_Swap_SFX: When value is 0x00000001, Channel Swap local effect is enabled +// {A44531EF-5377-4944-AE15-53789A9629C7},2 +// vartype = VT_UI4 +DEFINE_PROPERTYKEY(PKEY_Endpoint_Enable_Channel_Swap_SFX, 0xa44531ef, 0x5377, 0x4944, 0xae, 0x15, 0x53, 0x78, 0x9a, 0x96, 0x29, 0xc7, 2); + +// PKEY_Endpoint_Enable_Channel_Swap_MFX: When value is 0x00000001, Channel Swap global effect is enabled +// {A44531EF-5377-4944-AE15-53789A9629C7},3 +// vartype = VT_UI4 +DEFINE_PROPERTYKEY(PKEY_Endpoint_Enable_Channel_Swap_MFX, 0xa44531ef, 0x5377, 0x4944, 0xae, 0x15, 0x53, 0x78, 0x9a, 0x96, 0x29, 0xc7, 3); + +// PKEY_Endpoint_Enable_Delay_SFX: When value is 0x00000001, Delay local effect is enabled +// {A44531EF-5377-4944-AE15-53789A9629C7},4 +// vartype = VT_UI4 +DEFINE_PROPERTYKEY(PKEY_Endpoint_Enable_Delay_SFX, 0xa44531ef, 0x5377, 0x4944, 0xae, 0x15, 0x53, 0x78, 0x9a, 0x96, 0x29, 0xc7, 4); + +// PKEY_Endpoint_Enable_Delay_MFX: When value is 0x00000001, Delay global effect is enabled +// {A44531EF-5377-4944-AE15-53789A9629C7},5 +// vartype = VT_UI4 +DEFINE_PROPERTYKEY(PKEY_Endpoint_Enable_Delay_MFX, 0xa44531ef, 0x5377, 0x4944, 0xae, 0x15, 0x53, 0x78, 0x9a, 0x96, 0x29, 0xc7, 5); diff --git a/audio/sysvad/SwapAPO/Inc/tlist.h b/audio/sysvad/SwapAPO/Inc/tlist.h new file mode 100644 index 00000000..6c0479aa --- /dev/null +++ b/audio/sysvad/SwapAPO/Inc/tlist.h @@ -0,0 +1,476 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: tlist.h +// +// Abstract: This is the header and implementation file for a template +// list class +// +// ---------------------------------------------------------------------------- + +#pragma once + + +typedef void* LISTPOS; + +template <class T> +class TItem +{ +public: + TItem() throw(); + ~TItem() throw(); + TItem<T> * pNext; + TItem<T> * pPrev; + T* pData; +}; + +template <class T> +TItem<T>::TItem() : + pNext(NULL), + pPrev(NULL), + pData(NULL) +{ + return; +} + +template <class T> +TItem<T>::~TItem() +{ + return; +} + + +template <class T> +class TList +{ + public: + TList() throw(); + ~TList() throw(); + + BOOL GetHead( T** ppHead ) const throw(); + LISTPOS GetHeadPosition() const throw(); + LISTPOS GetTailPosition() const throw(); + LISTPOS AddHead(__drv_aliasesMem T* pNewHead) throw(); + LISTPOS AddTail(__drv_aliasesMem T* pNewTail) throw(); + BOOL RemoveTail(T** ppOldTail) throw(); + BOOL RemoveHead(T** ppOldHead) throw(); + void RemoveAll() throw(); + LISTPOS Find( T* pSearchValue ) const throw(); + void RemoveAt(LISTPOS pos) throw(); + BOOL GetNext(LISTPOS& rPos, T**ppThisData) const throw(); + BOOL IsEmpty() const throw(); + DWORD GetCount() const throw(); + HRESULT Initialize(DWORD dwValue) throw(); + BOOL GetAt( LONG index, T** ppElement) const throw(); + BOOL GetAt( LISTPOS pos, T** ppElement) const throw(); + LISTPOS InsertBefore(LISTPOS pos, T* pNewElement) throw(); + LISTPOS InsertAfter(LISTPOS pos, T* pNewElement) throw(); + void MoveHeadList(TList<T> *pFromList) throw(); +private: + TItem<T>* m_pHead; + TItem<T>* m_pTail; + LONG m_lCount; +}; + +template <class T> +TList<T>::TList() : + m_pHead(NULL), + m_pTail(NULL), + m_lCount(0) +{ + return; +} + +template <class T> +TList<T>::~TList() +{ + ATLASSERT(NULL == m_pHead); + ATLASSERT(NULL == m_pTail); + ATLASSERT(0 == m_lCount); + return; +} + +template <class T> +BOOL TList<T>::GetHead(T** ppHead) const +{ + if (!m_pHead) + { + *ppHead = NULL; + return FALSE; + } + + *ppHead = m_pHead->pData; + return TRUE; +} + +template <class T> +LISTPOS TList<T>::GetHeadPosition() const +{ + return reinterpret_cast<LISTPOS>(m_pHead); +} + +template <class T> +LISTPOS TList<T>::GetTailPosition() const +{ + return reinterpret_cast<LISTPOS>(m_pTail); +} + +template <class T> +LISTPOS TList<T>::AddTail(__drv_aliasesMem T* pNewTail) +{ + TItem<T>* pNewItem; + + pNewItem = new TItem<T>; + if (NULL == pNewItem) + { + return( reinterpret_cast<LISTPOS>( NULL )); + } + + pNewItem->pData = pNewTail; + pNewItem->pPrev = m_pTail; + pNewItem->pNext = NULL; + + if (NULL == m_pTail) + { + m_pHead = pNewItem; + } + else + { + m_pTail->pNext = pNewItem; + } + + m_pTail = pNewItem; + m_lCount++; + + return( reinterpret_cast<LISTPOS>( pNewItem ) ); +} + +template <class T> +LISTPOS TList<T>::AddHead(__drv_aliasesMem T* pNewHead) +{ + TItem<T> *pNewItem; + pNewItem = new TItem<T>; + if ( !pNewItem ) + { + return( reinterpret_cast<LISTPOS>( NULL ) ); + } + + pNewItem->pData = pNewHead; + pNewItem->pNext = m_pHead; + pNewItem->pPrev = NULL; + + if ( NULL != m_pHead ) + { + m_pHead->pPrev = pNewItem; + } + else + { + m_pTail = pNewItem; + } + + m_pHead = pNewItem; + m_lCount++; + + return( reinterpret_cast<LISTPOS>( pNewItem ) ); +} + +template <class T> +void TList<T>::MoveHeadList(TList<T> *pFromList) +{ + // Link head item of this list to tail item of other list + if (NULL != m_pHead) + m_pHead->pPrev = pFromList->m_pTail; + if (NULL != pFromList->m_pTail) + pFromList->m_pTail->pNext = m_pHead; + + // Adjust this list's head and tail pointers + if (pFromList->m_pHead) + m_pHead = pFromList->m_pHead; + if (NULL == m_pTail) + m_pTail = pFromList->m_pTail; + + // Adjust this list's count + m_lCount += pFromList->m_lCount; + + // Reset other list's head and tail pointers and count + pFromList->m_pHead = NULL; + pFromList->m_pTail = NULL; + pFromList->m_lCount = 0; +} + +template <class T> +BOOL TList<T>::RemoveTail( T** ppOldTail) +{ + + if (!ppOldTail || !m_pTail) + { + return FALSE; + } + + TItem<T> *pOldItem = m_pTail; + *ppOldTail = pOldItem->pData; + + m_pTail = pOldItem->pPrev; + + if ( NULL != m_pTail ) + { + m_pTail->pNext = NULL; + } + else + { + m_pHead = NULL; + } + + delete pOldItem; + m_lCount--; + ATLASSERT(m_lCount >= 0); + + return( TRUE ); +} + +template <class T> +BOOL TList<T>::RemoveHead(T** ppOldHead) +{ + if ( !ppOldHead || !m_pHead ) + { + return( FALSE ); + } + + TItem<T> *pOldItem = m_pHead; + *ppOldHead = pOldItem->pData; + + m_pHead = pOldItem->pNext; + + if ( NULL != m_pHead ) + { + m_pHead->pPrev = NULL; + } + else + { + m_pTail = NULL; + } + + delete pOldItem; + m_lCount--; + ATLASSERT(m_lCount >= 0); + + return( TRUE ); +} + +template <class T> +void TList<T>::RemoveAll() +{ + TItem<T> *pNext; + + while( NULL != m_pHead ) + { + pNext = m_pHead->pNext; + delete m_pHead; + m_pHead = pNext; + } + + m_lCount = 0; + m_pTail = NULL; + + return; +} + +template <class T> +LISTPOS TList<T>::Find( T* pSearchValue ) const +{ + TItem<T> *pItem = m_pHead; + + for ( ; NULL != pItem; pItem = pItem->pNext ) + { + if ( pItem->pData == pSearchValue ) + { + return( reinterpret_cast<LISTPOS>(pItem) ); + } + } + + return( reinterpret_cast<LISTPOS>(NULL) ); +} + +template <class T> +void TList<T>::RemoveAt(LISTPOS pos) +{ + // ATLASSERT( NULL != pos ) + TItem<T> *pOldItem = reinterpret_cast< TItem<T>* >(pos); + + // remove pOldItem from list + if ( pOldItem == m_pHead ) + { + m_pHead = pOldItem->pNext; + } + else + { + ATLASSERT( pOldItem->pPrev ); + pOldItem->pPrev->pNext = pOldItem->pNext; + } + + if (pOldItem == m_pTail) + { + m_pTail = pOldItem->pPrev; + } + else + { + ATLASSERT( pOldItem->pNext ); + pOldItem->pNext->pPrev = pOldItem->pPrev; + } + + delete pOldItem; + m_lCount--; + ATLASSERT(m_lCount >= 0); + return; +} + +template <class T> +BOOL TList<T>::GetNext(LISTPOS& rPos, T**ppThisData) const +{ + TItem<T> *pItem = reinterpret_cast< TItem<T>* >(rPos); + + if ( !pItem ) + { + return( FALSE ); + } + + rPos = reinterpret_cast<LISTPOS>(pItem->pNext); + if (ppThisData) + { + *ppThisData = pItem->pData; + } + + return( TRUE ); +} + +template <class T> +BOOL TList<T>::IsEmpty() const +{ + return (0 == m_lCount); +} + +template <class T> +HRESULT TList<T>::Initialize(DWORD dwValue) +{ + HRESULT hr = S_OK; + return hr; +} + +template <class T> +DWORD TList<T>::GetCount() const +{ + return static_cast<DWORD>(m_lCount); +} + + +template <class T> +LISTPOS TList<T>::InsertBefore(LISTPOS pos, T* pNewElement) +{ + TItem<T>* pOldItem = reinterpret_cast<TItem<T>*>(pos); + TItem<T>* pNewItem; + + if ((pOldItem == NULL) || (pOldItem->pPrev == NULL)) + { + return (AddHead(pNewElement)); // insert before nothing -> head of the list + } + + // Insert it before pos + pNewItem = new TItem<T>; + if (!pNewItem) + { + return (reinterpret_cast<LISTPOS>(NULL)); + } + + pNewItem->pData = pNewElement; + pNewItem->pPrev = pOldItem->pPrev; + pNewItem->pNext = pOldItem; + + pOldItem->pPrev->pNext = pNewItem; + pOldItem->pPrev = pNewItem; + + m_lCount++; + + return (reinterpret_cast<LISTPOS>(pNewItem)); +} + +template <class T> +LISTPOS TList<T>::InsertAfter(LISTPOS pos, T* pNewElement) +{ + TItem<T>* pOldItem = reinterpret_cast<TItem<T>*>(pos); + TItem<T>* pNewItem; + + if ((pOldItem == NULL) || (pOldItem->pNext == NULL)) + { + return (AddTail(pNewElement)); // insert after nothing -> tail of the list + } + + // Insert it after pos + pNewItem = new TItem<T>; + if (!pNewItem) + { + return (reinterpret_cast<LISTPOS>(NULL)); + } + + pNewItem->pData = pNewElement; + pNewItem->pPrev = pOldItem; + pNewItem->pNext = pOldItem->pNext; + + pOldItem->pNext->pPrev = pNewItem; + pOldItem->pNext = pNewItem; + + m_lCount++; + + return (reinterpret_cast<LISTPOS>(pNewItem)); +} + +template <class T> +BOOL TList<T>::GetAt( LONG index, T** ppElement) const +{ + if (ppElement == NULL) + { + return false; + } + + *ppElement = NULL; + + if ((index < 0) || (index > (m_lCount - 1))) + { + return false; + } + + T* pElement = NULL; + BOOL bRes = TRUE; + LISTPOS pos = GetHeadPosition(); + + for (int i = 0; bRes && (i <= index); i++) + { + bRes = GetNext(pos, &pElement); + } + + if (bRes) + { + *ppElement = pElement; + } + + return bRes; +} + +template <class T> +BOOL TList<T>::GetAt(LISTPOS pos, T**ppThisData) const +{ + TItem<T> *pItem = reinterpret_cast< TItem<T>* >(pos); + + if ( !pItem ) + { + return( FALSE ); + } + + *ppThisData = pItem->pData; + + return( TRUE ); +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.cpp new file mode 100644 index 00000000..73c243b4 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.cpp @@ -0,0 +1,1087 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: AdvEndpointPropPage.cpp +// +// Abstract: Implementation of CAdvEndpointPropPage +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" +#include <mmdeviceapi.h> +#include <DeviceTopology.h> +#include "UIWidgets.h" +#include "Parts.h" +#include "TopologyExaminers.h" +#include "AdvEndpointPropPage.h" +#include <functiondiscoverykeys.h> + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +#define MAX_SEARCH_DEPTH 5 // when searching for a host pin or interface + +static IID s_rgAdvCtrlInterfaces[] = +{ + __uuidof(IAudioLoudness), + __uuidof(IAudioAutoGainControl), + __uuidof(IDeviceSpecificProperty) +}; + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::CAdvEndpointPropPage +// +// Description: +// CAdvEndpointPropPage constructor +// ---------------------------------------------------------------------------- +CAdvEndpointPropPage::CAdvEndpointPropPage() +: m_pAudioExtParams(NULL) +{ +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::~CAdvEndpointPropPage +// +// Description: +// CAdvEndpointPropPage destructor +// ---------------------------------------------------------------------------- +CAdvEndpointPropPage::~CAdvEndpointPropPage() +{ + CUIWidget* pWidget; + + while (m_lstWidgets.RemoveHead(&pWidget)) + { + SAFE_DELETE(pWidget); + } + + SAFE_RELEASE(m_pAudioExtParams->pEndpoint); + SAFE_RELEASE(m_pAudioExtParams->pPnpInterface); + SAFE_RELEASE(m_pAudioExtParams->pPnpDevnode); + + SAFE_DELETE(m_pAudioExtParams); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::MakeNewWidget +// +// Description: +// Adds a widget to the list +// +// Parameters: +// pPart - [in] Part interface +// iid - [in] Advanced control IID which controls the kind of widget +// that gets created +// nCtrlId - [in] ID of the control +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::MakeNewWidget +( + IPart* pPart, + REFIID iid, + UINT nCtrlId +) +{ + UNREFERENCED_PARAMETER(nCtrlId); + + HRESULT hr = S_OK; + LISTPOS posChk; + CUIWidget* pNewWidget = NULL; + + if ((iid == __uuidof(IAudioLoudness)) || + (iid == __uuidof(IAudioAutoGainControl))) + { + pNewWidget = new CBooleanWidget(pPart, iid); + IF_TRUE_ACTION_JUMP((pNewWidget == NULL), hr = E_OUTOFMEMORY, Exit); + } + else if (iid == __uuidof(IDeviceSpecificProperty)) + { + CComPtr<IDeviceSpecificProperty> spDevSpec; + VARTYPE vt; + + hr = pPart->Activate(CLSCTX_INPROC_SERVER, __uuidof(IDeviceSpecificProperty), (void**)&spDevSpec); + IF_FAILED_JUMP(hr, Exit); + + hr = spDevSpec->GetType(&vt); + IF_FAILED_JUMP(hr, Exit); + + if (vt == VT_BOOL) + { + pNewWidget = new CBooleanWidget(pPart, iid); + IF_TRUE_ACTION_JUMP((pNewWidget == NULL), hr = E_OUTOFMEMORY, Exit); + } + else if (vt == VT_I4) + { + pNewWidget = new CLongWidget<LONG>(pPart); + IF_TRUE_ACTION_JUMP((pNewWidget == NULL), hr = E_OUTOFMEMORY, Exit); + } + else if (vt == VT_UI4) + { + pNewWidget = new CLongWidget<ULONG>(pPart); + IF_TRUE_ACTION_JUMP((pNewWidget == NULL), hr = E_OUTOFMEMORY, Exit); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + goto Exit; + } + } + + ATLASSERT(pNewWidget); + + posChk = m_lstWidgets.AddTail(pNewWidget); + if (posChk == NULL) + { + hr = E_OUTOFMEMORY; + SAFE_DELETE(pNewWidget); + } + +Exit: + return hr; +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::GetDeviceFriendlyName +// +// Description: +// Retrieves the endpoint's friendly name +// +// Parameters: +// ppNameOut - [out] The friendly name of the endpoint +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::GetDeviceFriendlyName +( + _Outptr_result_maybenull_ LPWSTR* ppNameOut +) +{ + HRESULT hr = S_OK; + CComPtr<IPropertyStore> spProperties; + PROPVARIANT var; + + IF_TRUE_ACTION_JUMP((m_pAudioExtParams == NULL), hr = E_POINTER, Exit); + IF_TRUE_ACTION_JUMP((ppNameOut == NULL), hr = E_POINTER, Exit); + + *ppNameOut = NULL; + + PropVariantInit(&var); + + if (m_pAudioExtParams->pEndpoint != NULL) + { + // Open the PropertyStore for read access + hr = m_pAudioExtParams->pEndpoint->OpenPropertyStore(STGM_READ, &spProperties); + IF_FAILED_JUMP(hr, Exit); + + // Retrieve the friendly name of the endpoint + hr = spProperties->GetValue(PKEY_Device_FriendlyName, &var); + if (SUCCEEDED(hr) && (var.vt == VT_LPWSTR)) + { + *ppNameOut = var.pwszVal; + } + else + { + PropVariantClear(&var); + } + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::OnInitDialog +// +// Description: +// Dialog initialization routine +// +// Parameters: +// hwndDlg - [in] Handle to dialog box +// wParam - [in] Handle to control to receive the default keyboard focus +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE to direct the system to set the keyboard focus to the control +// specified by wParam. Otherwise, it should return FALSE to prevent the +// system from setting the default keyboard focus. +// ---------------------------------------------------------------------------- +BOOL CAdvEndpointPropPage::OnInitDialog +( + HWND hwndDlg, + WPARAM wParam, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + LPWSTR pwstrEndpointName = NULL; + + LISTPOS pos; + CUIWidget* pWidget; + SRECT rcWidget(34, 144, 0, 0); + RECT rcDlg; + HRESULT hr = S_OK; + UINT id = 1800; + + // Retrieve the endpoint's friendly name + hr = GetDeviceFriendlyName(&pwstrEndpointName); + IF_FAILED_JUMP(hr, Exit); + + // Update the property page with retrieved information + SetWindowText(GetDlgItem(hwndDlg, IDC_EPP_ENDPOINT_NAME), pwstrEndpointName); + + // Initialize and create widgets + hr = InitializeWidgets(); + if (hr == E_NOTFOUND) + { + // If no widgets were found, show a message to depict that + SetWindowText(GetDlgItem(hwndDlg, IDC_NO_ADV_CONTROLS_FOUND), + L"No advanced controls found."); + } + else + { + // Create widget windows + ::GetClientRect(hwndDlg, &rcDlg); + rcWidget.w = rcDlg.right - rcDlg.left - 28; + rcWidget.h = 14; + + pos = m_lstWidgets.GetHeadPosition(); + while (pos) + { + pWidget = NULL; + m_lstWidgets.GetNext(pos, &pWidget); + + hr = pWidget->Create(hwndDlg, rcWidget, id); + if (SUCCEEDED(hr)) + { + id++; + + // offset to next widget + rcWidget.y = rcWidget.y + rcWidget.h + 12; + } + } + } + +Exit: + SAFE_COTASKMEMFREE(pwstrEndpointName); + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::OnApply +// +// Description: +// Handle the pressing of the apply button +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// wParam - [in] Handle to the control to receive the default keyboard focus +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE to set keyboard focus on control +// ---------------------------------------------------------------------------- +BOOL CAdvEndpointPropPage::OnApply +( + HWND hwndDlg, + WPARAM wParam, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + CUIWidget* pWidget; + LISTPOS pos; + + pos = m_lstWidgets.GetHeadPosition(); + + while (pos) + { + pWidget = NULL; + m_lstWidgets.GetNext(pos, &pWidget); + pWidget->CommitValue(); + } + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + + return(TRUE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::OnCheckBoxClicked +// +// Description: +// Handle the clicking of the check boxes +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// wParam - [in] Handle to the control to receive the default keyboard focus +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CAdvEndpointPropPage::OnCheckBoxClicked +( + HWND hwndDlg, + WPARAM wParam, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(lParam); + + CUIWidget* pWidget; + LISTPOS pos; + + pos = m_lstWidgets.GetHeadPosition(); + + while (pos) + { + pWidget = NULL; + m_lstWidgets.GetNext(pos, &pWidget); + + if (pWidget->OwnsCtrlId(LOWORD(wParam))) + { + pWidget->OnClick(); + } + } + + // Enable the Apply button upon user changing the control + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::DialogProcPage1 +// +// Description: +// Callback for property page +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// uMsg - [in] Specifies the message +// wParam - [in] Specifies additional message-specific information +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE if it processed the message, FALSE if not +// ---------------------------------------------------------------------------- +INT_PTR CALLBACK CAdvEndpointPropPage::DialogProcPage1 +( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + CAdvEndpointPropPage* pthis = (CAdvEndpointPropPage*)(LONG_PTR)GetWindowLongPtr( + hwndDlg, GWLP_USERDATA); + BOOL fRet = FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + // Extract the context data from PROPSHEETPAGE::lParam + PROPSHEETPAGE* pSheetDesc = (PROPSHEETPAGE*)lParam; + + // Create the property page factory class +#pragma warning(push) +#pragma warning(disable: 28197) + pthis = new CComObject<CAdvEndpointPropPage>(); +#pragma warning(pop) + if (pthis == NULL) + { + return(FALSE); + } + + // Save this object in lParam + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)pthis); + + // Keep audio extension parameters passed by the control panel + pthis->m_pAudioExtParams = (AudioExtensionParams*)pSheetDesc->lParam; + + fRet = pthis->OnInitDialog(hwndDlg, wParam, lParam); + break; + } + + case WM_NOTIFY: + { + switch (((NMHDR FAR*)lParam)->code) + { + case PSN_APPLY: + if (pthis) + { + // Apply button pressed + fRet = pthis->OnApply(hwndDlg, wParam, lParam); + } + break; + } + break; + } + + case WM_COMMAND: + { + if (pthis) + { + // Check box clicked + fRet = pthis->OnCheckBoxClicked(hwndDlg, wParam, lParam); + } + break; + } + + case WM_DESTROY: + { + SAFE_DELETE(pthis); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, NULL); + fRet = TRUE; + break; + } + } + + return(fRet); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::PropSheetPageProc +// +// Description: +// Callback that gets invoked right after page creation or right before +// before page destruction +// +// Parameters: +// hwnd - Reserved; must be NULL +// uMsg - [in] Action flag. PSPCB_ADDREF, PSPCB_CREATE, or PSPCB_RELEASE +// ppsp - [in, out] Pointer to a PROPSHEETPAGE structure that defines +// the page being created or destroyed. +// +// Return values: +// Depends on the value of the uMsg parameter +// ---------------------------------------------------------------------------- +UINT CALLBACK CAdvEndpointPropPage::PropSheetPageProc +( + HWND hwnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp +) +{ + UNREFERENCED_PARAMETER(hwnd); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(ppsp); + + // if (uMsg == PSPCB_CREATE) ... + return(1); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::Initialize +// +// Description: +// Implementation of IShellExtInit::Initialize. Initializes a property +// sheet extension, shortcut menu extension, or drag-and-drop handler. +// +// Parameters: +// pidlFolder - [in] Address of an ITEMIDLIST structure that uniquely +// identifies a folder. For property sheet extensions, +// this parameter is NULL. +// pdtobj - [out] Address of an IDataObject interface object that can be +// used to retrieve the objects being acted upon. +// hkeyProgID - [in] Registry key for the file object or folder type. +// +// Return values: +// Returns NOERROR if successful, or an OLE-defined error value otherwise +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT CAdvEndpointPropPage::Initialize +( + LPCITEMIDLIST pidlFolder, + IDataObject* pdtobj, + HKEY hkeyProgID +) +{ + UNREFERENCED_PARAMETER(pidlFolder); + UNREFERENCED_PARAMETER(pdtobj); + UNREFERENCED_PARAMETER(hkeyProgID); + + return(S_OK); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::FindHostConnector +// +// Description: +// This method initiates a search for a software connector that supports +// the supplied format. If one is found, it returns a path to that +// connector. +// +// Parameters: +// pKsFormat - [in] Requested format +// cbFormat - [in] Size of format buffer in BYTEs +// bRejectMixedPaths - [in] If TRUE, stop looking if you find a mix unit +// ppPath - [out] The path to the Host pin +// +// Remarks: +// To get the host connector, just take the zeroth element in the +// resulting path +// +// Return: +// S_OK on success, E_NOTFOUND if not. Other error code indicates +// something unexpected. +// ---------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::FindHostConnector +( + PKSDATAFORMAT pKsFormat, + ULONG cbFormat, + BOOL bRejectMixedPaths, + IPartsList** ppPath +) +{ + HRESULT hr; + CPartsList* pPathOut = NULL; + CComPtr<IDeviceTopology> spTopology; + UINT cConnectors; + CComPtr<IConnector> spEndpointConnector; + CComPtr<IConnector> spConStart; + CComQIPtr<IPart> spPartStart; + DataFlow flow; + UINT cDepth = 0; + CFormatExaminer* pExaminer = NULL; + + IF_TRUE_ACTION_JUMP((ppPath == NULL), hr = E_POINTER, Exit); + *ppPath = NULL; + + ATLASSERT(m_pAudioExtParams->pEndpoint != NULL); + + // Get IDeviceTopology interface for the Endpoint + hr = m_pAudioExtParams->pEndpoint->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spTopology); + IF_FAILED_JUMP(hr, Exit); + + // Get the connector to start searching from. By definition, an endpoint device can only have 1 connector. + hr = spTopology->GetConnectorCount(&cConnectors); + IF_FAILED_JUMP(hr, Exit); + + ATLASSERT(cConnectors == 1); + IF_TRUE_ACTION_JUMP((cConnectors != 1), hr = E_FAIL, Exit); + + // Since an endpoint device can only have 1 connector, get the connector at index 0 + hr = spTopology->GetConnector(0, &spEndpointConnector); + IF_FAILED_JUMP(hr, Exit); + + hr = spEndpointConnector->GetConnectedTo(&spConStart); + IF_FAILED_JUMP(hr, Exit); + + // get the dataflow (required by Search method) + hr = spConStart->GetDataFlow(&flow); + IF_FAILED_JUMP(hr, Exit); + + // QI for IPart (required by Search method) + spPartStart = spConStart; + + // Create an examiner that looks for a pin with the specified format + pExaminer = new CFormatExaminer(pKsFormat, cbFormat); + IF_TRUE_ACTION_JUMP((pExaminer == NULL), hr = E_OUTOFMEMORY, Exit); + + // Create a new parts list +#pragma warning(push) +#pragma warning(disable: 28197) + pPathOut = new CPartsList(); +#pragma warning(pop) + IF_TRUE_ACTION_JUMP((pPathOut == NULL), hr = E_OUTOFMEMORY, Exit); + + // Start looking + hr = Search(spPartStart, flow, pExaminer, pPathOut, cDepth, bRejectMixedPaths); + if (bRejectMixedPaths && (hr == E_NOTFOUND)) + { + // Don't actually reject mixed paths, just avoid them + hr = Search(spPartStart, flow, pExaminer, pPathOut, cDepth, FALSE); + } + IF_FAILED_JUMP(hr, Exit); + + hr = pPathOut->QueryInterface(__uuidof(IPartsList), (void**)ppPath); + +Exit: + SAFE_RELEASE(pPathOut); + + SAFE_DELETE(pExaminer); + + return hr; +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::Search +// +// Description: +// Searches the device topology for parts that satisfy the supplied +// examiner. +// +// Parameters: +// pPartStart - [in] The IPart to start looking at +// flowStart - [in] The flow of the starting part. This never changes +// so it is passed as an argument. +// pExaminer - [in] The examiner that tests each IPart against some criteria +// pPathAggregate - [out] The cumulative path that has been searched +// cDepth - [in] Stop looking if we reach this recursion depth +// bRejectMixedPaths - [in] If TRUE, then stop looking if we encounter a +// MIX unit +// +// Remarks: +// The search may span several FunctionInstances connected by DeviceModel +// IConnections. The algorithm works as follows: +// +// 1) Each connector in the model is tested against the following +// (in order): +// a) Is the flow opposite that of the starting part? +// If no then goto next connector. +// b) Does there exist a path from the starting part to this +// connector? If no then goto next connector. +// c) Is the supplied examiner satisfied by the path? +// If yes, then return. +// d) Is this connector connected to another connector? +// If yes, then add the connector to a list of "deferred +// connectors" to be checked if no other connectors in this model +// satisfy (c) +// +// 2) If the function still hasn't returned, then for each connector in +// the list generated in step 1d: +// a) Get the connector that it is connected to +// b) Recurse into this function with "that" connector as the starting +// part +// +// The point of the list of "deferred connectors" is to minimize the +// length of the paths that we find. e.g. if two viable paths exist, one +// (A) which spans 3 models, and another (B) which spans only 2, then B +// will be found first, regardless of the ordering of connections in the +// models involved. +// +// Note that when a path is found, all of the parts involved in that path +// are aggregated into pPathAggregate IN REVERSE ORDER. In general, all +// the parts in pPathAggregate will not belong to a single model, so when +// using pPathAggregate, you must use IPart::GetTopologyObject to get the +// model and/or FunctionInstance of the part (i.e. don't cache the value +// of either). +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::Search +( + IPart* pPartStart, + DataFlow flowStart, + IExaminer* pExaminer, + CPartsList* pPathAggregate, + UINT cDepth, + BOOL bRejectMixedPaths +) +{ + HRESULT hr = S_OK; + TList<IConnector> lstDeferredConnectors; + BOOL bHaveViableConnector = FALSE; + + { + CComPtr<IDeviceTopology> spTopology; + UINT cConnectors; + + // Bail if we are recursing too deep + IF_TRUE_ACTION_JUMP((cDepth == MAX_SEARCH_DEPTH), hr = E_NOTFOUND, Exit); + + // Get DeviceModel and DeviceModelUtil interfaces associated with pPartStart. + // We can't just pass these as params, because each time this function is called + // recursively, we are talking about a part (pPartStart) on a different device. + hr = pPartStart->GetTopologyObject(&spTopology); + IF_FAILED_JUMP(hr, Exit); + + // + // [Step 1] Examine each connector. + // + hr = spTopology->GetConnectorCount(&cConnectors); + IF_FAILED_JUMP(hr, Exit); + + for (UINT c = 0; (c < cConnectors) && !bHaveViableConnector; c++) + { + CComPtr<IConnector> spConTest; + DataFlow flowTest; + CComPtr<IPartsList> spPath; + CComPtr<IPart> spPartTest; + BOOL bConnectorIsConnected; + + hr = spTopology->GetConnector(c, &spConTest); + IF_FAILED_JUMP(hr, Exit); + + hr = spConTest->GetDataFlow(&flowTest); + IF_FAILED_JUMP(hr, Exit); + + // [1a] We only care about connectors whose dataflow is opposite that of pConStart + // (i.e. on the other side of the topology) + if (flowTest == flowStart) + continue; + + // QI for IPart + spPartTest = spConTest; + + ATLASSERT(spPartTest); + IF_TRUE_ACTION_JUMP((spPartTest == NULL), hr = E_NOINTERFACE, Exit); + + // [1b] See if there exists a path from one to the other + hr = spTopology->GetSignalPath(pPartStart, spPartTest, bRejectMixedPaths, &spPath); + + // If no path exists from here to there, then try the next connector + if (hr != S_OK) + continue; + + // [1c] Let pExaminer take a look at this path, but don't worry if it fails + pExaminer->Examine(spPath); + + // See if pExaminer has found what it was looking for yet + if (pExaminer->IsSatisfied()) + { + bHaveViableConnector = TRUE; + + // add all of the parts in spPath to pPathAggregate + hr = pPathAggregate->AddParts(spPath); + break; + } + + // [1d] See if the connector is connected. If so, add to the list of deferred + // connectors + hr = spConTest->IsConnected(&bConnectorIsConnected); + + if (SUCCEEDED(hr) && bConnectorIsConnected) + { + LISTPOS posChk; + IConnector* pConDeferred; + + // Get another interface pointer that is NOT a CComPtr, so it doesn't + // get released until we want (note that .Detach means that when spConTest + // goes out of scope it won't release the interface) + pConDeferred = spConTest.Detach(); + + // Add to list of connectors to look more closely at + posChk = lstDeferredConnectors.AddTail(pConDeferred); + + if (posChk == NULL) + { + hr = E_OUTOFMEMORY; + pConDeferred->Release(); + goto Exit; + } + } + } + + if (!bHaveViableConnector) + { + // + // [Step 2] Jump to connected devices and test them. + // + + LISTPOS pos = lstDeferredConnectors.GetHeadPosition(); + + while (pos != NULL) + { + CComPtr<IConnector> spConConnectedTo; + IConnector* pConDeferred = NULL; // this will be cleaned up at the end of the function + CComPtr<IPart> spPartConnectedTo; + CComPtr<IPartsList> spPath; + CComQIPtr<IPart> spPartDeferred; + + // Get the deferred connector. RefCount should be 1 + lstDeferredConnectors.GetNext(pos, &pConDeferred); + + // Get the part (a connector) that this connector is connected to + hr = pConDeferred->GetConnectedTo(&spConConnectedTo); + IF_FAILED_JUMP(hr, Exit); + + // Search that device model as well. Note that we can just reuse the dataflow here + spPartConnectedTo = spConConnectedTo; + ATLASSERT(spPartConnectedTo != NULL); + IF_TRUE_ACTION_JUMP((spPartConnectedTo == NULL), hr = E_NOINTERFACE, Exit); + + // Recursion + hr = Search(spPartConnectedTo, flowStart, pExaminer, pPathAggregate, cDepth + 1, bRejectMixedPaths); + if (FAILED(hr)) + continue; + + // We found what we were looking for! + bHaveViableConnector = TRUE; + + // We need to figure out how we got here again and add that path to pPath + spPartDeferred = pConDeferred; + ATLASSERT(spPartDeferred != NULL); + + // Note: If we fail past here, pPathAggregate will contain some parts. These will + // be cleaned up by CPartsList destructor + IF_TRUE_ACTION_JUMP((spPartDeferred == NULL), hr = E_NOINTERFACE, Exit); + + hr = spTopology->GetSignalPath(pPartStart, spPartDeferred, bRejectMixedPaths, &spPath); + IF_FAILED_JUMP(hr, Exit); + + // Add all of the parts in spPath to pPathAggregate + hr = pPathAggregate->AddParts(spPath); + IF_FAILED_JUMP(hr, Exit); + + break; + } + } + } + +Exit: + // Cleanup + IConnector* pConDelete; + while (lstDeferredConnectors.RemoveHead(&pConDelete)) + { + SAFE_RELEASE(pConDelete); + } + + // Adjust result. If there were no errors, but we didn't find what + // we were searching for, then return "not found" + if (SUCCEEDED(hr) && (!bHaveViableConnector)) + { + hr = E_NOTFOUND; + } + + return hr; +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::InitializeWidgets +// +// Description: +// Looks for parts in the path that support any of these control +// interfaces: +// -- IAudioLoudness +// -- IAudioAutoGainControl +// -- IDeviceSpecificProperty +// +// Return: +// S_OK if at least one control found +// E_NOTFOUND if no controls found +// other if unexpected error +// +// --------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::InitializeWidgets() +{ + HRESULT hr = S_OK; + HRESULT hrWarn = S_OK; + CComPtr<IPartsList> spPath; + UINT cParts; + BOOL bFoundSomething = FALSE; + UINT nCtrlId = 900; + + // Note: Can use the endpoint format instead of this ... + KSDATAFORMAT format; + ZeroMemory(&format, sizeof(format)); + + format.FormatSize = sizeof(KSDATAFORMAT); + format.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; + format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + format.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; + + // See if we can find a path to any old PCM host pin by only passing sizeof(KSDATAFORMAT) + hr = FindHostConnector(&format, sizeof(KSDATAFORMAT), FALSE, &spPath); + IF_FAILED_JUMP(hr, Exit); + + // + hr = spPath->GetCount(&cParts); + IF_FAILED_JUMP(hr, Exit); + + // Look for parts that support any of the interfaces in s_rgAdvCtrlInterfaces + for (UINT i = 0; i < cParts; i++) + { + UINT cInterfaces; + CComPtr<IPart> spPart; + + hr = spPath->GetPart(i, &spPart); + IF_FAILED_JUMP(hr, Exit); + + hr = spPart->GetControlInterfaceCount(&cInterfaces); + IF_FAILED_JUMP(hr, Exit); + + for (UINT j = 0; j < cInterfaces; j++) + { + CComPtr<IControlInterface> spInterfaceDesc; + GUID iid; + + hr = spPart->GetControlInterface(j, &spInterfaceDesc); + IF_FAILED_JUMP(hr, Exit); + + hr = spInterfaceDesc->GetIID(&iid); + IF_FAILED_JUMP(hr, Exit); + + for (int k = 0; k < ARRAYSIZE(s_rgAdvCtrlInterfaces); k++) + { + if (iid == s_rgAdvCtrlInterfaces[k]) + { + bFoundSomething = TRUE; + + hrWarn = MakeNewWidget(spPart, iid, nCtrlId++); + } + } + } + } + + if (!bFoundSomething) + { + hr = E_NOTFOUND; + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::AddPages +// +// Description: +// Implementation of IShellPropSheetExt::AddPages. Adds one or more pages +// to a property sheet that the Shell displays for a file object. +// +// Parameters: +// lpfnAddPage - [in] Address of a function that the property sheet +// handler calls to add a page to the property sheet. The +// function takes a property sheet handle returned by the +// CreatePropertySheetPage function and the lParam parameter +// passed to the AddPages method. +// lParam - [in] Parameter to pass to the function specified by the +// lpfnAddPage method. +// +// Return values: +// Returns S_OK if successful. If the method fails, an OLE-defined error +// code is returned +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT STDMETHODCALLTYPE CAdvEndpointPropPage::AddPages +( + LPFNADDPROPSHEETPAGE lpfnAddPage, // See PrSht.h + LPARAM lParam // Used by caller, don't modify +) +{ + HRESULT hr = S_OK; + PROPSHEETPAGE psp; + HPROPSHEETPAGE hPage1 = NULL; + AudioExtensionParams* pAudioParams = (AudioExtensionParams*)lParam; +#pragma warning(push) +#pragma warning(disable: 28197) + AudioExtensionParams* pAudioParamsCopy = new AudioExtensionParams; +#pragma warning(pop) + + if (pAudioParamsCopy == NULL) + { + return E_OUTOFMEMORY; + } + + // Make a copy of the params + CopyMemory(pAudioParamsCopy, pAudioParams, sizeof(AudioExtensionParams)); + SAFE_ADDREF(pAudioParams->pEndpoint); + SAFE_ADDREF(pAudioParams->pPnpInterface); + SAFE_ADDREF(pAudioParams->pPnpDevnode); + + // Initialize property page params and create page + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_USEREFPARENT | PSP_USECALLBACK; + psp.hInstance = _AtlBaseModule.GetModuleInstance(); + psp.hIcon = 0; + psp.pcRefParent = (UINT*)&m_dwRef; + psp.lParam = (LPARAM)pAudioParamsCopy; + psp.pszTemplate = MAKEINTRESOURCE(IDD_ADV_ENDPOINT_PROP_PAGE); + psp.pfnDlgProc = (DLGPROC)DialogProcPage1; + psp.pfnCallback = PropSheetPageProc; + + // Create the property sheet page and add the page + hPage1 = CreatePropertySheetPage(&psp); + if (hPage1) + { + if (!lpfnAddPage(hPage1, pAudioParams->AddPageParam)) + { + hr = E_FAIL; + delete pAudioParamsCopy; + DestroyPropertySheetPage(hPage1); + } + else + { + // Add ref for page + this->AddRef(); + } + } + else + { + delete pAudioParamsCopy; + hr = E_OUTOFMEMORY; + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::ReplacePage +// +// Description: +// Implementation of IShellPropSheetExt::ReplacePage. Replaces a page in +// a property sheet for a Control Panel object. +// +// Parameters: +// uPageID - [in] Identifier of the page to replace +// lpfnReplacePage - [in] Address of a function that the property sheet +// handler calls to replace a page to the property +// sheet. The function takes a property sheet handle +// returned by the CreatePropertySheetPage function and +// the lParam parameter passed to the ReplacePage +// method. +// lParam - [in] Parameter to pass to the function specified by the +// lpfnReplacePage parameter. +// +// Return values: +// Returns NOERROR if successful, or an OLE-defined error value otherwise +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT STDMETHODCALLTYPE CAdvEndpointPropPage::ReplacePage +( + UINT uPageID, + LPFNSVADDPROPSHEETPAGE lpfnReplaceWith, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(uPageID); + UNREFERENCED_PARAMETER(lpfnReplaceWith); + UNREFERENCED_PARAMETER(lParam); + + return(S_FALSE); +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.h b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.h new file mode 100644 index 00000000..0d02ef97 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.h @@ -0,0 +1,74 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: AdvEndpointPropPage.h +// +// Abstract: Declaration of the CAdvEndpointPropPage class +// +// ---------------------------------------------------------------------------- + + +#pragma once + + +class ATL_NO_VTABLE CAdvEndpointPropPage : + public CComObjectRootEx<CComSingleThreadModel>, + public CComCoClass<CAdvEndpointPropPage, &CLSID_AdvEndpointPropPage>, + public IDispatchImpl<IAdvEndpointPropPage, &__uuidof(IAdvEndpointPropPage), &LIBID_CplExtLib, /*wMajor =*/ 1, /*wMinor =*/ 0>, + public IShellExtInit, + public IShellPropSheetExt +{ +public: + CAdvEndpointPropPage(); + ~CAdvEndpointPropPage(); + + DECLARE_REGISTRY_RESOURCEID(IDR_ADV_ENDPOINT_PROP_PAGE) + + BEGIN_COM_MAP(CAdvEndpointPropPage) + COM_INTERFACE_ENTRY(IAdvEndpointPropPage) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IShellExtInit) + COM_INTERFACE_ENTRY(IShellPropSheetExt) + END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + HRESULT FinalConstruct() + { + return S_OK; + } + + void FinalRelease() + { + } + + static INT_PTR CALLBACK DialogProcPage1(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + static UINT CALLBACK PropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp); + + // IShellExtInit + STDMETHOD(Initialize)(_In_opt_ LPCITEMIDLIST pidlFolder, _In_opt_ IDataObject* pdtobj, _In_opt_ HKEY hkeyProgID); + + // IShellPropSheetExt + STDMETHOD(AddPages)(_In_ LPFNADDPROPSHEETPAGE lpfnAddPage, _In_ LPARAM lParam); + STDMETHOD(ReplacePage)(_In_ UINT uPageID, _In_ LPFNSVADDPROPSHEETPAGE lpfnReplaceWith, _In_ LPARAM lParam); + +private: + AudioExtensionParams* m_pAudioExtParams; + TList<CUIWidget> m_lstWidgets; + + HRESULT FindHostConnector(PKSDATAFORMAT pKsFormat, ULONG cbFormat, BOOL bRejectMixedPaths, IPartsList** ppPath); + HRESULT Search(IPart* pStart, DataFlow flowStart, IExaminer* pExaminer, CPartsList* pPath, UINT cDepth, BOOL bRejectMixedPaths); + HRESULT InitializeWidgets(); + HRESULT GetDeviceFriendlyName(_Outptr_result_maybenull_ LPWSTR* ppNameOut); + HRESULT MakeNewWidget(IPart* pPart, REFIID iid, UINT nCtrlId); + BOOL OnInitDialog (HWND hwndDlg, WPARAM wParam, LPARAM lParam); + BOOL OnApply(HWND hwndDlg, WPARAM wParam, LPARAM lParam); + BOOL OnCheckBoxClicked(HWND hwndDlg, WPARAM wParam, LPARAM lParam); +}; + +OBJECT_ENTRY_AUTO(__uuidof(AdvEndpointPropPage), CAdvEndpointPropPage) diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.rgs b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.rgs new file mode 100644 index 00000000..eecbc126 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.rgs @@ -0,0 +1,27 @@ +HKCR +{ + CplExt.AdvEndpointPropPage.1 = s 'AdvEndpointPropPage Class' + { + CLSID = s '{6C57B2A2-91F5-4b90-93D5-FAB82485ECA6}' + } + CplExt.AdvEndpointPropPage = s 'AdvEndpointPropPage Class' + { + CLSID = s '{6C57B2A2-91F5-4b90-93D5-FAB82485ECA6}' + CurVer = s 'CplExt.AdvEndpointPropPage.1' + } + NoRemove CLSID + { + ForceRemove {6C57B2A2-91F5-4b90-93D5-FAB82485ECA6} = s 'AdvEndpointPropPage Class' + { + ProgID = s 'CplExt.AdvEndpointPropPage.1' + VersionIndependentProgID = s 'CplExt.AdvEndpointPropPage' + ForceRemove 'Programmable' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + val AppID = s '%APPID%' + 'TypeLib' = s '{59390583-ED40-46D9-9B16-7800B9CC5CC2}' + } + } +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.cpp new file mode 100644 index 00000000..dd915cd5 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.cpp @@ -0,0 +1,69 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: CplExt.cpp +// +// Abstract: Implementation of DLL Exports +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +HINSTANCE g_hInstance = NULL; + + +class CCplExtModule : public CAtlDllModuleT< CCplExtModule > +{ +public : + DECLARE_LIBID(LIBID_CplExtLib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CPL_EXT, "{A7D2EC8B-B70F-434C-A0CE-0DF324805F7D}") +}; + +CCplExtModule _AtlModule; + + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + g_hInstance = hInstance; + return _AtlModule.DllMain(dwReason, lpReserved); +} + + +// Used to determine whether the DLL can be unloaded by OLE +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); +} + + +// Returns a class factory to create an object of the requested type +STDAPI DllGetClassObject(_In_ REFCLSID rclsid,_In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + + +// DllRegisterServer - Adds entries to the system registry +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + HRESULT hr = _AtlModule.DllRegisterServer(); + return hr; +} + + +// DllUnregisterServer - Removes entries from the system registry +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.idl b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.idl new file mode 100644 index 00000000..3254cbd8 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.idl @@ -0,0 +1,73 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: CplExt.idl +// +// Abstract: IDL source for CplExt. This file will be processed by the +// MIDL tool to produce the type library (CplExt.tlb) and +// marshalling code. +// +// ---------------------------------------------------------------------------- + + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(E6DB299B-B925-415A-879B-4A76D072F39A), + dual, + nonextensible, + helpstring("ISwapPropPage Interface"), + pointer_default(unique) +] +interface ISwapPropPage : IDispatch{ +}; + + +[ + object, + uuid(D379F7FB-66E0-427f-890D-1F479AD73B4B), + dual, + nonextensible, + helpstring("IAdvEndpointPropPage Interface"), + pointer_default(unique) +] +interface IAdvEndpointPropPage : IDispatch{ +}; + + +[ + uuid(59390583-ED40-46D9-9B16-7800B9CC5CC2), + version(1.0), + helpstring("CplExt 1.0 Type Library") +] +library CplExtLib +{ + importlib("stdole2.tlb"); + + // For Swap Property Page + [ + uuid(19166F23-5F08-47F9-BB57-9F57A977D88E), + helpstring("SwapPropPage Class") + ] + coclass SwapPropPage + { + [default] interface ISwapPropPage; + }; + + // For Advanced Endpoint Property Page + [ + uuid(6C57B2A2-91F5-4b90-93D5-FAB82485ECA6), + helpstring("AdvEndpointPropPage Class") + ] + coclass AdvEndpointPropPage + { + [default] interface IAdvEndpointPropPage; + }; +}; diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rc b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rc new file mode 100644 index 00000000..415ed22e --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rc @@ -0,0 +1,208 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "1 TYPELIB ""cplext.tlb""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "TODO: <Company name>" + VALUE "FileDescription", "TODO: <File description>" + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "CplExt.dll" + VALUE "LegalCopyright", "TODO: (c) <Company name>. All rights reserved." + VALUE "OriginalFilename", "CplExt.dll" + VALUE "ProductName", "TODO: <Product name>" + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +IDR_CPL_EXT REGISTRY "CplExt.rgs" +IDR_SWAP_PROP_PAGE REGISTRY "SwapPropPage.rgs" +IDR_ADV_ENDPOINT_PROP_PAGE REGISTRY "AdvEndpointPropPage.rgs" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_SWAP_PROP_PAGE DIALOGEX 0, 0, 284, 246 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "System Effects - Sample" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_SPP_ENDPOINT_NAME,19,34,211,8 + CONTROL "&Disable System Effects",IDC_DISABLE_SYSFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,92,89,8 + CONTROL "Enable Channel Swap &SFX",IDC_ENABLE_SWAP_SFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,112,98,8 + CONTROL "Enable Channel Swap &MFX",IDC_ENABLE_SWAP_MFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,132,99,8 + CONTROL "Enable D&elay SFX",IDC_ENABLE_DELAY_SFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,152,98,8 + CONTROL "Enable Del&ay MFX",IDC_ENABLE_DELAY_MFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,172,99,8 + GROUPBOX "Endpoint Name",IDC_STATIC,7,18,270,37 + GROUPBOX "System Effects Configuration",IDC_STATIC,7,71,270,132 +END + +IDD_ADV_ENDPOINT_PROP_PAGE DIALOGEX 0, 0, 284, 244 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD | WS_CAPTION | + WS_SYSMENU +CAPTION "Custom - Sample" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_EPP_ENDPOINT_NAME,19,34,211,8 + LTEXT "",IDC_NO_ADV_CONTROLS_FOUND,20,90,232,9 + GROUPBOX "Endpoint Name",IDC_STATIC,7,18,270,37 + GROUPBOX "Advanced Controls",IDC_STATIC,7,71,270,154 +END + +IDD_WIDGET_GEN_LONG DIALOGEX 0, 0, 200, 12 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_STATICEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + RTEXT "L",IDC_GEN_LABEL,0,2,94,8 + CONTROL "",IDC_GEN_SLIDER,"msctls_trackbar32",TBS_BOTH | + TBS_NOTICKS | WS_TABSTOP | 0x400,96,0,76,13 + RTEXT "-6 dB",IDC_GEN_VALUE,174,1,24,10,0,WS_EX_STATICEDGE +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_SWAP_PROP_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 277 + TOPMARGIN, 7 + BOTTOMMARGIN, 239 + END + + IDD_ADV_ENDPOINT_PROP_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 277 + TOPMARGIN, 7 + BOTTOMMARGIN, 237 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_AUDIO_ICON ICON "music.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PROJ_NAME "CplExt" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +1 TYPELIB "cplext.tlb" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rgs b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rgs new file mode 100644 index 00000000..1c97b062 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rgs @@ -0,0 +1,11 @@ +HKCR +{ + NoRemove AppID + { + '%APPID%' = s 'CplExt' + 'CplExt.DLL' + { + val AppID = s '%APPID%' + } + } +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/Parts.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/Parts.cpp new file mode 100644 index 00000000..41130ead --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/Parts.cpp @@ -0,0 +1,298 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows DevTopo.dll +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: Parts.cpp +// +// Abstract: Implementation of CPart and derived classes. +// +// -------------------------------------------------------------------------------- + +#include "stdafx.h" +#include <DeviceTopology.h> +#include "Parts.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::CPartsList +// +// Description: +// CPartsList constructor +// ---------------------------------------------------------------------- +CPartsList::CPartsList() : + m_refCount(1) +{ +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::~CPartsList +// +// Description: +// CPartsList destructor +// ---------------------------------------------------------------------- +CPartsList::~CPartsList() +{ + ATLASSERT(m_refCount == 0); + + IPart* pIPart; + while (m_lstParts.RemoveHead(&pIPart)) + { + SAFE_RELEASE(pIPart); + } +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::QueryInterface +// +// Description: +// Implementation of IUnknown::QueryInterface +// +// Parameters: +// riid - [in] Identifier of the interface being queried +// ppvObj - [in] Pointer to a buffer that receives a pointer to the +// object whose interface is queried +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------- +STDMETHODIMP CPartsList::QueryInterface +( + const IID& iid, + void** ppUnk +) +{ + if (!ppUnk) + return E_POINTER; + + HRESULT hr = E_NOINTERFACE; + *ppUnk = NULL; + + if (iid == __uuidof(IPartsList)) + { + *ppUnk = (IPartsList*)this; + hr = S_OK; + AddRef(); + } + else + if (iid == __uuidof(IUnknown)) + { + *ppUnk = (IUnknown*)this; + hr = S_OK; + AddRef(); + } + + return hr; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::AddRef +// +// Description: +// Implementation of IUnknown::AddRef +// +// Return: +// New refcount +// ---------------------------------------------------------------------- +ULONG STDMETHODCALLTYPE CPartsList::AddRef(void) +{ + return InterlockedIncrement(&m_refCount); +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::Release +// +// Description: +// Implementation of IUnknown::Release +// +// Return: +// New refcount +// ---------------------------------------------------------------------- +ULONG STDMETHODCALLTYPE CPartsList::Release(void) +{ + ATLASSERT(m_refCount > 0); + LONG lRef = InterlockedDecrement(&m_refCount); + ATLASSERT(lRef >= 0); + + if (lRef == 0) + { + delete this; + } + return lRef; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::AddPart +// +// Description: +// Adds an item to the list of IPart interfaces +// +// Parameters: +// pIPart - [in] Part to add to the list +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------- +STDMETHODIMP CPartsList::AddPart +( + IPart* pIPart +) +{ + if (pIPart == NULL) + return E_POINTER; + + // Protect m_lstParts access + m_CritSection.Enter(); + + pIPart->AddRef(); + m_lstParts.AddTail(pIPart); + + m_CritSection.Leave(); + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::AddPart +// +// Description: +// Adds the PartsList Parts to the list of IPart interfaces +// +// Parameters: +// pIParts - [in] List of Parts to be added to the list +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------- +STDMETHODIMP CPartsList::AddParts +( + IPartsList* pIParts +) +{ + ATLASSERT(pIParts != NULL); + + HRESULT hr = S_OK; + UINT i, cParts = 0; + + // Protect m_lstParts + m_CritSection.Enter(); + + // Loop through specified parts list + hr = pIParts->GetCount(&cParts); + IF_FAILED_JUMP(hr, Exit); + + for (i = 0; (i < cParts) && (hr == S_OK); i++) + { + IPart* pIPart; + + // ... and copy reference to each part + hr = pIParts->GetPart(i, &pIPart); + IF_FAILED_JUMP(hr, Exit); + + // ... to this list + m_lstParts.AddTail(pIPart); + } + +Exit: + m_CritSection.Leave(); + + return hr; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::GetCount +// +// Description: +// Returns the count of items in the list of IParts +// +// Parameters: +// pCount - [out] the count of parts in this list +// +// Return: +// S_OK if successful +// +// ---------------------------------------------------------------------- +_Use_decl_annotations_ +STDMETHODIMP CPartsList::GetCount +( + UINT* pCount +) +{ + if (pCount == NULL) + return E_POINTER; + + // Protect m_lstParts access + m_CritSection.Enter(); + + *pCount = m_lstParts.GetCount(); + + m_CritSection.Leave(); + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::GetPart +// +// Description: +// Returns the nIndex-th item in the list of IParts +// +// Parameters: +// nIndex - [in] index of the part to access +// uMsg - [out] the part at the specified index +// +// Return: +// S_OK if successful +// +// ---------------------------------------------------------------------- +_Use_decl_annotations_ +STDMETHODIMP CPartsList::GetPart +( + UINT nIndex, + IPart** ppPart +) +{ + HRESULT hr; + IPart* pIPart = NULL; + + // Protect m_lstParts access + m_CritSection.Enter(); + + IF_TRUE_ACTION_JUMP((ppPart == NULL), hr = E_POINTER, Exit); + + *ppPart = NULL; + + IF_TRUE_ACTION_JUMP((nIndex >= m_lstParts.GetCount()), hr = E_INVALIDARG, Exit); + + IF_TRUE_ACTION_JUMP((!m_lstParts.GetAt(nIndex, &pIPart)), hr = E_NOTFOUND, Exit); + + // Take care of AddRef and ensure correct interface in one shot + hr = pIPart->QueryInterface(__uuidof(IPart), reinterpret_cast<void**>(ppPart)); + IF_FAILED_JUMP(hr, Exit); + +Exit: + m_CritSection.Leave(); + + return hr; +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/Parts.h b/audio/sysvad/SwapAPO/PropPageExtensions/Parts.h new file mode 100644 index 00000000..1cad63f5 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/Parts.h @@ -0,0 +1,49 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows DevTopo.dll +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: Parts.h +// +// Abstract: CPartsList is a list container of parts +// +// -------------------------------------------------------------------------------- + +#pragma once + + +class CPartsList : public IPartsList +{ +private: + + // This is a REAL refcount. When it goes to zero, the object is destroyed. + LONG m_refCount; + TList<IPart> m_lstParts; + + CCriticalSection m_CritSection; + +protected: + ~CPartsList(); // Refcounted objects should have a private destructor. + +public: + CPartsList(); + + STDMETHOD(AddPart)(IPart* pIPart); + STDMETHOD(AddParts)(IPartsList* pIParts); + +public: + + // IPartsList + STDMETHOD(GetCount)(_Out_ UINT* pCount); + STDMETHOD(GetPart)(_In_ UINT nIndex, _Out_ IPart** ppPart); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppUnk); + ULONG STDMETHODCALLTYPE AddRef(void); + ULONG STDMETHODCALLTYPE Release(void); + + friend class CDeviceTopology; +}; diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.def b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.def new file mode 100644 index 00000000..aa2797ce --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.def @@ -0,0 +1,9 @@ +; PropPageExt.def : Declares the module parameters. + +LIBRARY "PropPageExt.DLL" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj new file mode 100644 index 00000000..61fd0d38 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj @@ -0,0 +1,275 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{76DBA957-38F6-47ED-A5C0-AD587D08FF12}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <SupportsPackaging>false</SupportsPackaging> + <RequiresPackageProject>true</RequiresPackageProject> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{3D348571-DCCC-4D1B-B986-C7B51C270E20}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>PropPageExt</TargetName> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>PropPageExt</TargetName> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>PropPageExt</TargetName> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>PropPageExt</TargetName> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <ExceptionHandling>Sync</ExceptionHandling> + </ClCompile> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <ExceptionHandling>Sync</ExceptionHandling> + </ClCompile> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <ExceptionHandling>Sync</ExceptionHandling> + </ClCompile> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <ExceptionHandling>Sync</ExceptionHandling> + </ClCompile> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /wd4127</AdditionalOptions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;comctl32.lib;rpcrt4.lib</AdditionalDependencies> + <ModuleDefinitionFile>PropPageExt.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /wd4127</AdditionalOptions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;comctl32.lib;rpcrt4.lib</AdditionalDependencies> + <ModuleDefinitionFile>PropPageExt.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /wd4127</AdditionalOptions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;comctl32.lib;rpcrt4.lib</AdditionalDependencies> + <ModuleDefinitionFile>PropPageExt.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /wd4127</AdditionalOptions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;comctl32.lib;rpcrt4.lib</AdditionalDependencies> + <ModuleDefinitionFile>PropPageExt.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="AdvEndpointPropPage.cpp"> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\stdafx.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="CplExt.cpp"> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\stdafx.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="Parts.cpp"> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\stdafx.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="stdafxsrc.cpp"> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Create</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\stdafx.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="SwapPropPage.cpp"> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\stdafx.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="TopologyExaminers.cpp"> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\stdafx.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="UIWidgets.cpp"> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\stdafx.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <Midl Include="CplExt.idl" /> + <ResourceCompile Include="CplExt.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj.Filters b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj.Filters new file mode 100644 index 00000000..bf37ff4d --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj.Filters @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{531ADE68-0355-4EB7-BA48-144A9A3C8B98}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{DFFDE202-63D4-4633-A6F6-661E34DAE36E}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{9592BEFC-255E-4BD1-B21A-3D100EE35A6A}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="AdvEndpointPropPage.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CplExt.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Parts.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="stdafxsrc.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SwapPropPage.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TopologyExaminers.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="UIWidgets.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <Midl Include="CplExt.idl"> + <Filter>Source Files</Filter> + </Midl> + <None Include="PropPageExt.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="CplExt.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.cpp new file mode 100644 index 00000000..cdc2b0e2 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.cpp @@ -0,0 +1,1154 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: SwapPropPage.cpp +// +// Abstract: Implementation of the CSwapPropPage class +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" +#include <initguid.h> // DEFINE_GUID +#include <mmdeviceapi.h> +#include <audioenginebaseapo.h> +#include <audioendpoints.h> +#include "SwapPropPage.h" +#include <functiondiscoverykeys.h> +#include <CustomPropKeys.h> +#include "cplext_i.c" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::CSwapPropPage +// +// Description: +// CSwapPropPage constructor +// ---------------------------------------------------------------------------- +CSwapPropPage::CSwapPropPage() +: m_pAudioFXExtParams(NULL) +, m_fDisableSysFX(FALSE) +, m_fEnableSwapSFX(FALSE) +, m_fEnableSwapMFX(FALSE) +, m_fEnableDelaySFX(FALSE) +, m_fEnableDelayMFX(FALSE) +, m_fReset(FALSE) +{ +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::~CSwapPropPage +// +// Description: +// CSwapPropPage destructor +// ---------------------------------------------------------------------------- +CSwapPropPage::~CSwapPropPage() +{ + SAFE_RELEASE(m_pAudioFXExtParams->pFxProperties); + SAFE_DELETE(m_pAudioFXExtParams); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::GetDeviceFriendlyName +// +// Description: +// Retrieves the endpoint's friendly name +// +// Parameters: +// ppNameOut - [out] The friendly name of the endpoint +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::GetDeviceFriendlyName +( + _Outptr_result_maybenull_ LPWSTR* ppNameOut +) +{ + CComPtr<IMMDeviceEnumerator> spEnumerator; + CComPtr<IPropertyStore> spProperties; + CComPtr<IMMDevice> spMMDevice; + HRESULT hr = S_OK; + PROPVARIANT var; + + IF_TRUE_ACTION_JUMP((ppNameOut == NULL), hr = E_POINTER, Exit); + + *ppNameOut = NULL; + + // Create device enumerator and get IMMDevice from the device ID + hr = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = spEnumerator->GetDevice(m_pAudioFXExtParams->pwstrEndpointID, &spMMDevice); + IF_FAILED_JUMP(hr, Exit); + + // Open the PropertyStore for read access + hr = spMMDevice->OpenPropertyStore(STGM_READ, &spProperties); + IF_FAILED_JUMP(hr, Exit); + + PropVariantInit(&var); + + // Retrieve the friendly name of the endpoint + hr = spProperties->GetValue(PKEY_Device_FriendlyName, &var); + if (SUCCEEDED(hr) && (var.vt == VT_LPWSTR)) + { + *ppNameOut = var.pwszVal; + } + else + { + PropVariantClear(&var); + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveSysFXState +// +// Description: +// Get the current state (enabled or disabled) of system effects +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveSysFXState(BOOL *pfDisabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether system effects are enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_AudioEndpoint_Disable_SysFx, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfDisabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetSysFXState +// +// Description: +// Enable or disable system effects +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetSysFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveSysFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fDisableSysFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fDisableSysFX ? 1L : 0L); + + // Enable or disable SysFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_AudioEndpoint_Disable_SysFx, var); + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveSwapSFXState +// +// Description: +// Get the current state (enabled or disabled) of channel swap SFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveSwapSFXState(BOOL *pfEnabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether channel swap SFX is enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_Endpoint_Enable_Channel_Swap_SFX, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfEnabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetSwapSFXState +// +// Description: +// Enable or disable channel swap SFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetSwapSFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveSwapSFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fEnableSwapSFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fEnableSwapSFX ? 1L : 0L); + + // Enable or disable channel swap SFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_Endpoint_Enable_Channel_Swap_SFX, var); + + // Enabling or disabling the swap effect can be done with the engine running + // It does not invalidate anything which should be locked by IAudioProcessingObjectConfiguration::LockForProcess + // In particular, it does not impact IAudioProcessingObject::GetLatency + // So we do not need to call IAudioEndpointFormatControl::ResetToDefault here + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveSwapMFXState +// +// Description: +// Get the current state (enabled or disabled) of channel swap MFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveSwapMFXState(BOOL *pfEnabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether channel swap MFX is enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_Endpoint_Enable_Channel_Swap_MFX, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfEnabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetSwapMFXState +// +// Description: +// Enable or disable channel swap MFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetSwapMFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveSwapMFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fEnableSwapMFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fEnableSwapMFX ? 1L : 0L); + + // Enable or disable channel swap MFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_Endpoint_Enable_Channel_Swap_MFX, var); + + // Enabling or disabling the swap effect can be done with the engine running + // It does not invalidate anything which should be locked by IAudioProcessingObjectConfiguration::LockForProcess + // In particular, it does not impact IAudioProcessingObject::GetLatency + // So we do not need to call IAudioEndpointFormatControl::ResetToDefault here + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveDelaySFXState +// +// Description: +// Get the current state (enabled or disabled) of delay SFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveDelaySFXState(BOOL *pfEnabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether channel swap SFX is enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_Endpoint_Enable_Delay_SFX, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfEnabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetSwapSFXState +// +// Description: +// Enable or disable channel swap SFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetDelaySFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveDelaySFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fEnableDelaySFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fEnableDelaySFX ? 1L : 0L); + + // Enable or disable delay SFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_Endpoint_Enable_Delay_SFX, var); + IF_FAILED_JUMP(hr, Exit); + + // Enabling or disabling delay changes the value that should be returned by IAudioProcessingObject::GetLatency + // This is one of the things that is locked by IAudioProcessingObjectConfiguration::LockForProcess + // So we need to call IAudioEndpointFormatControl::ResetToDefault + // This will prompt AudioDG to tear down the graph and build it up again + m_fReset = TRUE; + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveDelayMFXState +// +// Description: +// Get the current state (enabled or disabled) of delay MFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveDelayMFXState(BOOL *pfEnabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether channel swap MFX is enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_Endpoint_Enable_Delay_MFX, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfEnabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetDelayMFXState +// +// Description: +// Enable or disable delay MFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetDelayMFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveDelayMFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fEnableDelayMFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fEnableDelayMFX ? 1L : 0L); + + // Enable or disable delay MFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_Endpoint_Enable_Delay_MFX, var); + IF_FAILED_JUMP(hr, Exit); + + // Enabling or disabling delay changes the value that should be returned by IAudioProcessingObject::GetLatency + // This is one of the things that is locked by IAudioProcessingObjectConfiguration::LockForProcess + // So we need to call IAudioEndpointFormatControl::ResetToDefault + // This will prompt AudioDG to tear down the graph and build it up again + m_fReset = TRUE; + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnInitDialog +// +// Description: +// Dialog initialization routine +// +// Parameters: +// hwndDlg - [in] Handle to dialog box +// wParam - [in] Handle to control to receive the default keyboard focus +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE to direct the system to set the keyboard focus to the control +// specified by wParam. Otherwise, it should return FALSE to prevent the +// system from setting the default keyboard focus. +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnInitDialog +( + HWND hwndDlg, + WPARAM wParam, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + HRESULT hr = S_OK; + LPWSTR pwstrEndpointName = NULL; + + // Retrieve the endpoint's friendly name, system effects, and swap SFX and MFX states + hr = GetDeviceFriendlyName(&pwstrEndpointName); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveSysFXState(&m_fDisableSysFX); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveSwapSFXState(&m_fEnableSwapSFX); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveSwapMFXState(&m_fEnableSwapMFX); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveDelaySFXState(&m_fEnableDelaySFX); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveDelayMFXState(&m_fEnableDelayMFX); + IF_FAILED_JUMP(hr, Exit); + + // Update the property page with retrieved information + SetWindowText(GetDlgItem(hwndDlg, IDC_SPP_ENDPOINT_NAME), pwstrEndpointName); + + // Based on the retrieved states, toggle the checkboxes to reflect them + if (m_fDisableSysFX) + { + CheckDlgButton(hwndDlg, IDC_DISABLE_SYSFX, BST_CHECKED); + + // Disable APO toggling controls on the page + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_SFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_MFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_SFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_MFX), FALSE); + } + else + { + CheckDlgButton(hwndDlg, IDC_DISABLE_SYSFX, BST_UNCHECKED); + + // Enable APO toggling controls on the page + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_SFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_MFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_SFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_MFX), TRUE); + } + + if (m_fEnableSwapSFX) + { + CheckDlgButton(hwndDlg, IDC_ENABLE_SWAP_SFX, BST_CHECKED); + } + else + { + CheckDlgButton(hwndDlg, IDC_ENABLE_SWAP_SFX, BST_UNCHECKED); + } + + if (m_fEnableSwapMFX) + { + CheckDlgButton(hwndDlg, IDC_ENABLE_SWAP_MFX, BST_CHECKED); + } + else + { + CheckDlgButton(hwndDlg, IDC_ENABLE_SWAP_MFX, BST_UNCHECKED); + } + + if (m_fEnableDelaySFX) + { + CheckDlgButton(hwndDlg, IDC_ENABLE_DELAY_SFX, BST_CHECKED); + } + else + { + CheckDlgButton(hwndDlg, IDC_ENABLE_DELAY_SFX, BST_UNCHECKED); + } + + if (m_fEnableDelayMFX) + { + CheckDlgButton(hwndDlg, IDC_ENABLE_DELAY_MFX, BST_CHECKED); + } + else + { + CheckDlgButton(hwndDlg, IDC_ENABLE_DELAY_MFX, BST_UNCHECKED); + } + +Exit: + SAFE_COTASKMEMFREE(pwstrEndpointName); + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnApply +// +// Description: +// Handle the pressing of the apply button +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// TRUE to set keyboard focus on control +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnApply +( + HWND hwndDlg +) +{ + HRESULT hr = S_OK; + + // Commit the settings + hr = SetSysFXState(); + IF_FAILED_JUMP(hr, Exit); + + hr = SetSwapSFXState(); + IF_FAILED_JUMP(hr, Exit); + + hr = SetSwapMFXState(); + IF_FAILED_JUMP(hr, Exit); + + hr = SetDelaySFXState(); + IF_FAILED_JUMP(hr, Exit); + + hr = SetDelayMFXState(); + IF_FAILED_JUMP(hr, Exit); + + if (NULL != m_pAudioFXExtParams && NULL != m_pAudioFXExtParams->pFxProperties) + { + hr = m_pAudioFXExtParams->pFxProperties->Commit(); + IF_FAILED_JUMP(hr, Exit); + + if (m_fReset) + { + // something changed that forces us to reset the format support + CComPtr<IMMDeviceEnumerator> spEnumerator; + CComPtr<IMMDevice> spMMDevice; + + // Create device enumerator and get IMMDevice from the device ID + hr = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = spEnumerator->GetDevice(m_pAudioFXExtParams->pwstrEndpointID, &spMMDevice); + IF_FAILED_JUMP(hr, Exit); + + CComPtr<IAudioEndpointFormatControl> spEndpointFormat; + hr = spMMDevice->Activate(__uuidof(IAudioEndpointFormatControl), CLSCTX_ALL, NULL, (void **)&spEndpointFormat); + IF_FAILED_JUMP(hr, Exit); + + spEndpointFormat->ResetToDefault(ENDPOINT_FORMAT_RESET_MIX_ONLY); + m_fReset = FALSE; + } + } + +Exit: + if (SUCCEEDED(hr)) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + } + else + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + } + + return(TRUE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedDisableSysFX +// +// Description: +// Handle the clicking of the Disable System Effects check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedDisableSysFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_DISABLE_SYSFX)) + { + m_fDisableSysFX = TRUE; + + // Disable APO toggling controls on the page + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_SFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_MFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_SFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_MFX), FALSE); + } + else + { + m_fDisableSysFX = FALSE; + + // Enable APO toggling controls on the page + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_SFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_MFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_SFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_MFX), TRUE); + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedEnableSwapSFX +// +// Description: +// Handle the clicking of the Enable Channel Swap SFX check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedEnableSwapSFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_ENABLE_SWAP_SFX)) + { + m_fEnableSwapSFX = TRUE; + } + else + { + m_fEnableSwapSFX = FALSE; + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedEnableSwapMFX +// +// Description: +// Handle the clicking of the Enable Channel Swap MFX check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedEnableSwapMFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_ENABLE_SWAP_MFX)) + { + m_fEnableSwapMFX = TRUE; + } + else + { + m_fEnableSwapMFX = FALSE; + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedEnableDelaySFX +// +// Description: +// Handle the clicking of the Enable Delay SFX check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedEnableDelaySFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_ENABLE_DELAY_SFX)) + { + m_fEnableDelaySFX = TRUE; + } + else + { + m_fEnableDelaySFX = FALSE; + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedEnableDelayMFX +// +// Description: +// Handle the clicking of the Enable Delay MFX check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedEnableDelayMFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_ENABLE_DELAY_MFX)) + { + m_fEnableDelayMFX = TRUE; + } + else + { + m_fEnableDelayMFX = FALSE; + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::DialogProcPage1 +// +// Description: +// Callback for property page +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// uMsg - [in] Specifies the message +// wParam - [in] Specifies additional message-specific information +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE if it processed the message, FALSE if not +// ---------------------------------------------------------------------------- +INT_PTR CALLBACK CSwapPropPage::DialogProcPage1 +( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + CSwapPropPage* pthis = (CSwapPropPage*)(LONG_PTR)GetWindowLongPtr( + hwndDlg, GWLP_USERDATA); + BOOL fRet = FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + // Extract the context data from PROPSHEETPAGE::lParam + PROPSHEETPAGE* pSheetDesc = (PROPSHEETPAGE*)lParam; + + // Create the property page factory class +#pragma warning(push) +#pragma warning(disable: 28197) + pthis = new CComObject<CSwapPropPage>(); +#pragma warning(pop) + if (pthis == NULL) + { + return(FALSE); + } + + // Save this object in lParam + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)pthis); + + // Keep audio FX extension parameters passed by the control panel + pthis->m_pAudioFXExtParams = (AudioFXExtensionParams*)pSheetDesc->lParam; + + fRet = pthis->OnInitDialog(hwndDlg, wParam, lParam); + break; + } + + case WM_NOTIFY: + { + switch (((NMHDR FAR*)lParam)->code) + { + case PSN_APPLY: + if (pthis) + { + // Apply button pressed + fRet = pthis->OnApply(hwndDlg); + } + break; + } + break; + } + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + // Handle the clicking of the check boxes + case IDC_DISABLE_SYSFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedDisableSysFX(hwndDlg); + } + break; + + case IDC_ENABLE_SWAP_SFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedEnableSwapSFX(hwndDlg); + } + break; + + case IDC_ENABLE_SWAP_MFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedEnableSwapMFX(hwndDlg); + } + break; + + case IDC_ENABLE_DELAY_SFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedEnableDelaySFX(hwndDlg); + } + break; + + case IDC_ENABLE_DELAY_MFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedEnableDelayMFX(hwndDlg); + } + break; + } + break; + } + + case WM_DESTROY: + { + SAFE_DELETE(pthis); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, NULL); + fRet = TRUE; + break; + } + } + + return(fRet); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::PropSheetPageProc +// +// Description: +// Callback that gets invoked right after page creation or right before +// before page destruction +// +// Parameters: +// hwnd - Reserved; must be NULL +// uMsg - [in] Action flag. PSPCB_ADDREF, PSPCB_CREATE, or PSPCB_RELEASE +// ppsp - [in, out] Pointer to a PROPSHEETPAGE structure that defines +// the page being created or destroyed. +// +// Return values: +// Depends on the value of the uMsg parameter +// ---------------------------------------------------------------------------- +UINT CALLBACK CSwapPropPage::PropSheetPageProc +( + HWND hwnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp +) +{ + UNREFERENCED_PARAMETER(hwnd); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(ppsp); + + // if (uMsg == PSPCB_CREATE) ... + return(1); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::Initialize +// +// Description: +// Implementation of IShellExtInit::Initialize. Initializes a property +// sheet extension, shortcut menu extension, or drag-and-drop handler. +// +// Parameters: +// pidlFolder - [in] Address of an ITEMIDLIST structure that uniquely +// identifies a folder. For property sheet extensions, +// this parameter is NULL. +// pdtobj - [out] Address of an IDataObject interface object that can be +// used to retrieve the objects being acted upon. +// hkeyProgID - [in] Registry key for the file object or folder type. +// +// Return values: +// Returns NOERROR if successful, or an OLE-defined error value otherwise +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT CSwapPropPage::Initialize +( + LPCITEMIDLIST pidlFolder, + IDataObject* pdtobj, + HKEY hkeyProgID +) +{ + UNREFERENCED_PARAMETER(pidlFolder); + UNREFERENCED_PARAMETER(pdtobj); + UNREFERENCED_PARAMETER(hkeyProgID); + + return(S_OK); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::AddPages +// +// Description: +// Implementation of IShellPropSheetExt::AddPages. Adds one or more pages +// to a property sheet that the Shell displays for a file object. +// +// Parameters: +// lpfnAddPage - [in] Address of a function that the property sheet +// handler calls to add a page to the property sheet. The +// function takes a property sheet handle returned by the +// CreatePropertySheetPage function and the lParam parameter +// passed to the AddPages method. +// lParam - [in] Parameter to pass to the function specified by the +// lpfnAddPage method. +// +// Return values: +// Returns S_OK if successful. If the method fails, an OLE-defined error +// code is returned +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT STDMETHODCALLTYPE CSwapPropPage::AddPages +( + LPFNADDPROPSHEETPAGE lpfnAddPage, // See PrSht.h + LPARAM lParam // Used by caller, don't modify +) +{ + HRESULT hr = S_OK; + PROPSHEETPAGE psp; + HPROPSHEETPAGE hPage1 = NULL; + AudioFXExtensionParams* pAudioFXParams = (AudioFXExtensionParams*)lParam; +#pragma warning(push) +#pragma warning(disable: 28197) + AudioFXExtensionParams* pAudioFXParamsCopy = new AudioFXExtensionParams; +#pragma warning(pop) + + if (pAudioFXParamsCopy == NULL) + { + return E_OUTOFMEMORY; + } + + // Make a copy of the params + CopyMemory(pAudioFXParamsCopy, pAudioFXParams, sizeof(AudioFXExtensionParams)); + SAFE_ADDREF(pAudioFXParamsCopy->pFxProperties); + + // Initialize property page params and create page + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_USEREFPARENT | PSP_USECALLBACK; + psp.hInstance = _AtlBaseModule.GetModuleInstance(); + psp.hIcon = 0; + psp.pcRefParent = (UINT*)&m_dwRef; + psp.lParam = (LPARAM)pAudioFXParamsCopy; + psp.pszTemplate = MAKEINTRESOURCE(IDD_SWAP_PROP_PAGE); + psp.pfnDlgProc = (DLGPROC)DialogProcPage1; + psp.pfnCallback = PropSheetPageProc; + + // Create the property sheet page and add the page + hPage1 = CreatePropertySheetPage(&psp); + if (hPage1) + { + if (!lpfnAddPage(hPage1, pAudioFXParams->AddPageParam)) + { + hr = E_FAIL; + delete pAudioFXParamsCopy; + DestroyPropertySheetPage(hPage1); + } + else + { + // Add ref for page + this->AddRef(); + } + } + else + { + delete pAudioFXParamsCopy; + hr = E_OUTOFMEMORY; + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::ReplacePage +// +// Description: +// Implementation of IShellPropSheetExt::ReplacePage. Replaces a page in +// a property sheet for a Control Panel object. +// +// Parameters: +// uPageID - [in] Identifier of the page to replace +// lpfnReplacePage - [in] Address of a function that the property sheet +// handler calls to replace a page to the property +// sheet. The function takes a property sheet handle +// returned by the CreatePropertySheetPage function and +// the lParam parameter passed to the ReplacePage +// method. +// lParam - [in] Parameter to pass to the function specified by the +// lpfnReplacePage parameter. +// +// Return values: +// Returns NOERROR if successful, or an OLE-defined error value otherwise +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT STDMETHODCALLTYPE CSwapPropPage::ReplacePage +( + UINT uPageID, + LPFNSVADDPROPSHEETPAGE lpfnReplaceWith, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(uPageID); + UNREFERENCED_PARAMETER(lpfnReplaceWith); + UNREFERENCED_PARAMETER(lParam); + + return(S_FALSE); +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.h b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.h new file mode 100644 index 00000000..295e29bc --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.h @@ -0,0 +1,92 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: SwapPropPage.h +// +// Abstract: Declaration of the CSwapPropPage class +// +// ---------------------------------------------------------------------------- + + +#pragma once + +// ---------------------------------------------------------------------- +// CSwapPropPage class +// +class ATL_NO_VTABLE CSwapPropPage : + public CComObjectRootEx<CComSingleThreadModel>, + public CComCoClass<CSwapPropPage, &CLSID_SwapPropPage>, + public IDispatchImpl<ISwapPropPage, &__uuidof(ISwapPropPage), &LIBID_CplExtLib, /*wMajor =*/ 1, /*wMinor =*/ 0>, + public IShellExtInit, + public IShellPropSheetExt +{ +public: + CSwapPropPage(); + ~CSwapPropPage(); + + DECLARE_REGISTRY_RESOURCEID(IDR_SWAP_PROP_PAGE) + + BEGIN_COM_MAP(CSwapPropPage) + COM_INTERFACE_ENTRY(ISwapPropPage) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IShellExtInit) + COM_INTERFACE_ENTRY(IShellPropSheetExt) + END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + HRESULT FinalConstruct() + { + return S_OK; + } + + void FinalRelease() + { + } + + static INT_PTR CALLBACK DialogProcPage1(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + static UINT CALLBACK PropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp); + + // IShellExtInit + STDMETHOD(Initialize)(_In_opt_ LPCITEMIDLIST pidlFolder, _In_opt_ IDataObject* pdtobj, _In_opt_ HKEY hkeyProgID); + + // IShellPropSheetExt + STDMETHOD(AddPages)(_In_ LPFNADDPROPSHEETPAGE lpfnAddPage, _In_ LPARAM lParam); + STDMETHOD(ReplacePage)(_In_ UINT uPageID, _In_ LPFNSVADDPROPSHEETPAGE lpfnReplaceWith, _In_ LPARAM lParam); + +private: + AudioFXExtensionParams* m_pAudioFXExtParams; + BOOL m_fDisableSysFX; + BOOL m_fEnableSwapSFX; + BOOL m_fEnableSwapMFX; + BOOL m_fEnableDelaySFX; + BOOL m_fEnableDelayMFX; + BOOL m_fReset; + + HRESULT GetDeviceFriendlyName(_Outptr_result_maybenull_ LPWSTR* ppNameOut); + HRESULT RetrieveSysFXState(BOOL *pfDisabled); + HRESULT SetSysFXState(); + HRESULT RetrieveSwapSFXState(BOOL *pfEnabled); + HRESULT SetSwapSFXState(); + HRESULT RetrieveSwapMFXState(BOOL *pfEnabled); + HRESULT SetSwapMFXState(); + HRESULT RetrieveDelaySFXState(BOOL *pfEnabled); + HRESULT SetDelaySFXState(); + HRESULT RetrieveDelayMFXState(BOOL *pfEnabled); + HRESULT SetDelayMFXState(); + + BOOL OnInitDialog(HWND hwndDlg, WPARAM wParam, LPARAM lParam); + BOOL OnApply(HWND hwndDlg); + BOOL OnCheckBoxClickedDisableSysFX(HWND hwndDlg); + BOOL OnCheckBoxClickedEnableSwapSFX(HWND hwndDlg); + BOOL OnCheckBoxClickedEnableSwapMFX(HWND hwndDlg); + BOOL OnCheckBoxClickedEnableDelaySFX(HWND hwndDlg); + BOOL OnCheckBoxClickedEnableDelayMFX(HWND hwndDlg); +}; + +OBJECT_ENTRY_AUTO(__uuidof(SwapPropPage), CSwapPropPage) diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.rgs b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.rgs new file mode 100644 index 00000000..6ede57a1 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.rgs @@ -0,0 +1,27 @@ +HKCR +{ + CplExt.SwapPropPage.1 = s 'SwapPropPage Class' + { + CLSID = s '{19166F23-5F08-47F9-BB57-9F57A977D88E}' + } + CplExt.SwapPropPage = s 'SwapPropPage Class' + { + CLSID = s '{19166F23-5F08-47F9-BB57-9F57A977D88E}' + CurVer = s 'CplExt.SwapPropPage.1' + } + NoRemove CLSID + { + ForceRemove {19166F23-5F08-47F9-BB57-9F57A977D88E} = s 'SwapPropPage Class' + { + ProgID = s 'CplExt.SwapPropPage.1' + VersionIndependentProgID = s 'CplExt.SwapPropPage' + ForceRemove 'Programmable' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + val AppID = s '%APPID%' + 'TypeLib' = s '{59390583-ED40-46D9-9B16-7800B9CC5CC2}' + } + } +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.cpp new file mode 100644 index 00000000..b224f4c1 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.cpp @@ -0,0 +1,162 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: TopologyExaminers.cpp +// +// Description: +// +// -------------------------------------------------------------------------------- + + +#include "stdafx.h" +#include "DeviceTopology.h" +#include "TopologyExaminers.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// ---------------------------------------------------------------------------- +// Function: +// CFormatExaminer::CFormatExaminer +// +// Description: +// CFormatExaminer constructor +// ---------------------------------------------------------------------------- +CFormatExaminer::CFormatExaminer +( + PKSDATAFORMAT pKsFormat, + ULONG cbFormat +) +: m_bSatisfied(FALSE), + m_pFormatReq(NULL) +{ + ATLASSERT(pKsFormat); + ATLASSERT(cbFormat >= sizeof(KSDATAFORMAT)); + + // If any of this fails, then Examine will fail + if (cbFormat >= sizeof(KSDATAFORMAT)) + { + m_pFormatReq = (PKSDATAFORMAT)(new BYTE[cbFormat]); + CopyMemory(m_pFormatReq, pKsFormat, cbFormat); + if (cbFormat < m_pFormatReq->FormatSize) + m_pFormatReq->FormatSize = cbFormat; + } +} + + +// ---------------------------------------------------------------------------- +// Function: +// CFormatExaminer::~CFormatExaminer +// +// Description: +// CFormatExaminer destructor +// ---------------------------------------------------------------------------- +CFormatExaminer::~CFormatExaminer() +{ + SAFE_DELETE_ARRAY(m_pFormatReq); +} + + +// ---------------------------------------------------------------------- +// Function: +// CFormatExaminer::Examine +// +// Description: +// Implementation of IExaminer::Examine. This method checks the supplied +// path an IConnector that support the KS streaming format (KSDATAFORMAT) +// that this examiner was set up to look for. +// +// Parameters: +// pPath - [in] List of parts to examine +// +// Remarks: +// This examiner only cares about the first part in the list, which is +// always a connector or an endpoint. Therefore it only needs to make +// sure that there is a zeroth element in the list and examine that. +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------- +HRESULT CFormatExaminer::Examine +( + IPartsList* pPath +) +{ + ATLASSERT(pPath != NULL); + + HRESULT hr = S_OK; + UINT cParts; + CComPtr<IPart> spPart; + + // Make sure there is at least 1 part + hr = pPath->GetCount(&cParts); + IF_FAILED_JUMP(hr, Exit); + + IF_TRUE_ACTION_JUMP((cParts == 0), hr = E_NOTFOUND, Exit); + + // This examiner only cares about Connectors so don't bother walking the + // path, just look at the last in the list, which should be a connector. + hr = pPath->GetPart(0, &spPart); + IF_FAILED_JUMP(hr, Exit); + + hr = ExaminePart(spPart); + +Exit: + return hr; +} + + +// ---------------------------------------------------------------------- +// Function: +// CFormatExaminer::ExaminePart +// +// Description: +// Tests a single IPart against the examination criteria, namely be a +// connector that supports whatever KS Streaming format (KSDATAFORMAT) +// this examiner was set up to look for +// +// Parameters: +// pIPart - [in] Part to examine +// +// Remarks: +// 1) Make sure the connector is of type Software_IO +// 2) Activate IKsFormatSupport on the connector +// 3) Call IKsFormatSupport::IsFormatSupported(m_pFormatReq). +// +// ---------------------------------------------------------------------- +HRESULT CFormatExaminer::ExaminePart +( + IPart* pIPart +) +{ + ATLASSERT(pIPart); + + HRESULT hr = S_OK; + + // 1) Check category and make sure it is a software_IO pin + ConnectorType conType; + CComPtr<IConnector> spConnector; + CComPtr<IKsFormatSupport> spFormatSupport; + + hr = pIPart->QueryInterface(__uuidof(IConnector), (void**)&spConnector); + IF_FAILED_JUMP(hr, Exit); + + hr = spConnector->GetType(&conType); + IF_FAILED_JUMP(hr, Exit); + + IF_TRUE_ACTION_JUMP((conType != Software_IO), hr = E_NOTFOUND, Exit); + + // 2) Activate IKsFormatSupport interface + hr = pIPart->Activate(CLSCTX_INPROC_SERVER, __uuidof(IKsFormatSupport), (void**)&spFormatSupport); + IF_FAILED_JUMP(hr, Exit); + + // 3) Ask the interface if this format is supported + hr = spFormatSupport->IsFormatSupported(m_pFormatReq, m_pFormatReq->FormatSize, &m_bSatisfied); + +Exit: + return hr; +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.h b/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.h new file mode 100644 index 00000000..eafd52f0 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.h @@ -0,0 +1,50 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: TopologyExaminers.h +// +// Abstract: An interface and supporting class(es) for examining device +// model IParts +// +// ---------------------------------------------------------------------------- + +#pragma once + + +// ---------------------------------------------------------------------- +// interface IExaminer +// An interface for examining devicemodel IParts +// ---------------------------------------------------------------------- +interface IExaminer +{ +public: + virtual HRESULT Examine(IPartsList* pPath) = 0; + virtual BOOL IsSatisfied() = 0; +}; + + +// -------------------------------------------------------------------------------- +// class CFormatExaminer +// A class that examines IConnectors looking for a soft connector that supports +// a given KS streaming format (KSDATAFORMAT). +// ---------------------------------------------------------------------- +class CFormatExaminer : public IExaminer +{ +private: + PKSDATAFORMAT m_pFormatReq; + BOOL m_bSatisfied; + + HRESULT ExaminePart(IPart* pIPart); + +public: + CFormatExaminer(PKSDATAFORMAT pKsFormat, ULONG cbFormat); + ~CFormatExaminer(); + + HRESULT Examine(IPartsList* pPath); + BOOL IsSatisfied() { return m_bSatisfied; } +}; diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.cpp new file mode 100644 index 00000000..9dc272af --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.cpp @@ -0,0 +1,321 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: UIWidgets.cpp +// +// Abstract: Implementation of CUIWidget and derived classes +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" +#include <DeviceTopology.h> +#include "UIWidgets.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// ---------------------------------------------------------------------- +// CUIWidget base class + + +// ---------------------------------------------------------------------------- +// Function: +// CUIWidget::CUIWidget +// +// Description: +// CUIWidget constructor +// ---------------------------------------------------------------------------- +CUIWidget::CUIWidget +( + IPart* pPart, + REFIID iid +) +: m_hwndParentDlg(NULL), + m_iid(iid), + m_wstrLabel(NULL), + m_bRegisteredForCallbacks(NULL) +{ + // This is done here rather than in the initializer list to avoid a prefix error + if (pPart != NULL) + { + m_spPart = pPart; + } + + m_dwProcessId = GetCurrentProcessId(); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CUIWidget::~CUIWidget +// +// Description: +// CUIWidget destructor +// ---------------------------------------------------------------------------- +CUIWidget::~CUIWidget() +{ + SAFE_COTASKMEMFREE(m_wstrLabel); + + if (m_bRegisteredForCallbacks) + { + m_spPart->UnregisterControlChangeCallback(this); + } +} + + +// ---------------------------------------------------------------------------- +// Function: +// CUIWidget::Create +// +// Description: +// -- Activates the control interface on the specified part +// -- Registers for control change callbacks +// +// Parameters: +// hwndDlg - [in] Parent dialog of the widget +// rc - [in] Not used +// ctrlIdBase - [in] Not used +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CUIWidget::Create +( + HWND hwndDlg, + SRECT& /*rc*/, + UINT& /*ctrlIdBase*/ +) +{ + HRESULT hr; + + m_hwndParentDlg = hwndDlg; + + RPC_STATUS rpcs = UuidCreate(&m_guidEventContext); + if (rpcs != RPC_S_OK) + { + hr = HRESULT_FROM_WIN32(rpcs); + goto Exit; + } + + hr = m_spPart->Activate(CLSCTX_INPROC_SERVER, m_iid, (void**)&m_spControl); + IF_FAILED_JUMP(hr, Exit); + + hr = m_spPart->RegisterControlChangeCallback(m_iid, this); + IF_FAILED_JUMP(hr, Exit); + + m_bRegisteredForCallbacks = TRUE; + + hr = m_spPart->GetName(&m_wstrLabel); + +Exit: + return hr; +}; + + +// ---------------------------------------------------------------------- +// Function: +// CUIWidget::OnNotify +// +// Description: +// Implementation of IControlChangeNotify::OnNotify +// Called when someone calls a "Set" method on a control interface. +// +// Parameters: +// dwProcessId - [in] Process ID +// pguidEventContext - Event context guid +// +// Return: +// S_OK always +// ---------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT CUIWidget::OnNotify +( + DWORD dwProcessId, + LPCGUID pguidEventContext +) +{ + UNREFERENCED_PARAMETER(dwProcessId); + + if ((pguidEventContext == NULL) || + (*pguidEventContext != m_guidEventContext)) + { + // Delegate to derived class + HandleControlChangeNotification(); + } + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// ---------------------------------------------------------------------- +// CBooleanWidget + + +// ---------------------------------------------------------------------------- +// Function: +// CBooleanWidget::CBooleanWidget +// +// Description: +// CBooleanWidget constructor +// ---------------------------------------------------------------------------- +CBooleanWidget::CBooleanWidget +( + IPart* pPart, + REFIID iid +) +: CUIWidget(pPart, iid), + m_hwndCheckbox(NULL), + m_bCurrentValue(FALSE) +{ +} + + +// ---------------------------------------------------------------------------- +// Function: +// CBooleanWidget::Create +// +// Description: +// Creates the widget which is a dialog +// +// Parameters: +// hwndDlg - [in] Parent dialog of the widget +// rc - [in] Position of the widget +// nCtrlId - [in] Child-window identifier +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CBooleanWidget::Create +( + HWND hwndDlg, + SRECT& rc, + UINT& nCtrlId +) +{ + HRESULT hr; + HFONT hFont; + DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX; + + // Activate the control interface and register for callbacks + hr = CUIWidget::Create(hwndDlg, rc, nCtrlId); + IF_FAILED_JUMP(hr, Exit); + + // Create a check box + rc.w = 300; + rc.h = 20; + m_hwndCheckbox = CreateWindowEx( + 0, //WS_EX_STATICEDGE, + WC_BUTTON, + m_wstrLabel, + dwStyle, + rc.x, rc.y, // put at (x,y) + rc.w, rc.h, + m_hwndParentDlg, + (HMENU)(UINT_PTR)nCtrlId, + NULL, + 0); + IF_TRUE_ACTION_JUMP((m_hwndCheckbox == NULL), hr = HRESULT_FROM_WIN32(GetLastError()), Exit); + + // Make the new control use the same font as the dialog + hFont = Window_GetFont(hwndDlg); + Window_SetFont(m_hwndCheckbox, hFont); + + // Get the current value + hr = HandleControlChangeNotification(); + IF_FAILED_JUMP(hr, Exit); + +Exit: + return hr; +} + + +// ---------------------------------------------------------------------- +// Function: +// CBooleanWidget::HandleControlChangeNotification() +// +// Description: +// Handles widget change notifications +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------- +HRESULT CBooleanWidget::HandleControlChangeNotification() +{ + HRESULT hr = S_OK; + + ATLASSERT(m_spControl); + ATLASSERT(m_hwndCheckbox); + + // Set value on DeviceTopology control interface + if (m_iid == __uuidof(IAudioLoudness)) + { + CComQIPtr<IAudioLoudness> spLoudness = m_spControl; + hr = spLoudness->GetEnabled(&m_bCurrentValue); + } + else if (m_iid == __uuidof(IAudioAutoGainControl)) + { + CComQIPtr<IAudioAutoGainControl> spAGC = m_spControl; + hr = spAGC->GetEnabled(&m_bCurrentValue); + } + else if (m_iid == __uuidof(IDeviceSpecificProperty)) + { + CComQIPtr<IDeviceSpecificProperty> spBoolCtrl = m_spControl; + DWORD cbValue = sizeof(m_bCurrentValue); + + hr = spBoolCtrl->GetValue(&m_bCurrentValue, &cbValue); + } + + // Set the check box to reflect the current value + SendMessage(m_hwndCheckbox, BM_SETCHECK, (WPARAM)(int)(m_bCurrentValue), 0L); + + return hr; +} + + +// ---------------------------------------------------------------------------- +// Function: +// CBooleanWidget::CommitValue +// +// Description: +// Commit the value set on the control +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CBooleanWidget::CommitValue() +{ + HRESULT hr = S_OK; + + ATLASSERT(m_spControl); + ATLASSERT(m_hwndCheckbox); + + // Get current value from check box + m_bCurrentValue = ((int)(DWORD)SendMessage(m_hwndCheckbox, BM_GETCHECK, 0L, 0L)); + + // Set value on DeviceTopology control interface + if (m_iid == __uuidof(IAudioLoudness)) + { + CComQIPtr<IAudioLoudness> spLoudness = m_spControl; + hr = spLoudness->SetEnabled(m_bCurrentValue, GetEventContext()); + } + else if (m_iid == __uuidof(IAudioAutoGainControl)) + { + CComQIPtr<IAudioAutoGainControl> spAGC = m_spControl; + hr = spAGC->SetEnabled(m_bCurrentValue, GetEventContext()); + } + else if (m_iid == __uuidof(IDeviceSpecificProperty)) + { + CComQIPtr<IDeviceSpecificProperty> spBoolCtrl = m_spControl; + + hr = spBoolCtrl->SetValue(&m_bCurrentValue, sizeof(m_bCurrentValue), + GetEventContext()); + } + + return hr; +}; diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.h b/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.h new file mode 100644 index 00000000..1f807bd4 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.h @@ -0,0 +1,405 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: UIWidgets.h +// +// Abstract: Declaration of CUIWidget and derived classes +// +// ---------------------------------------------------------------------------- + + +#pragma once + + +// ---------------------------------------------------------------------------- +// Class: +// CUIWidget +// +// Description: +// Base class for different types of widgets +// +// ---------------------------------------------------------------------------- +class CUIWidget : public IControlChangeNotify +{ +protected: + HWND m_hwndParentDlg; + IID m_iid; + CComPtr<IPart> m_spPart; + CComPtr<IUnknown> m_spControl; + DWORD m_dwProcessId; + LPWSTR m_wstrLabel; + BOOL m_bRegisteredForCallbacks; + GUID m_guidEventContext; + +public: + CUIWidget(IPart* pPart, REFIID iid); + virtual ~CUIWidget(); + + virtual HRESULT Create(HWND hwndDlg, SRECT& rc, UINT& nCtrlIdBase); + virtual HRESULT HandleControlChangeNotification() = 0; + virtual BOOL OwnsCtrlId(int ctrlId) + { + UNREFERENCED_PARAMETER(ctrlId); + return FALSE; + } + virtual HRESULT OnClick() { return S_OK; } + virtual HRESULT OnHScroll() { return S_OK; } + virtual LPCGUID GetEventContext() { return &m_guidEventContext; } + virtual HRESULT CommitValue() { return S_OK; } + + // IControlChangeNotify + STDMETHOD(OnNotify)(_In_ DWORD dwProcessId, _In_opt_ LPCGUID pguidEventContext); + + // IUnknown (since we implement IControlChangeNotify) + HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppUnk) + { + UNREFERENCED_PARAMETER(iid); + + *ppUnk = NULL; + return E_NOTIMPL; + } + ULONG STDMETHODCALLTYPE AddRef(void) + { return 0; } + ULONG STDMETHODCALLTYPE Release(void) + { return 0; } +}; + + +// ---------------------------------------------------------------------------- +// Class: +// CBooleanWidget +// +// Description: +// Handles IAudioLoudness, IAudioAutoGainControl and +// IGenericBooleanProperty +// +// ---------------------------------------------------------------------------- +class CBooleanWidget : public CUIWidget +{ +protected: + BOOL m_bCurrentValue; + HWND m_hwndCheckbox; + +public: + CBooleanWidget(IPart* pPart, REFIID iid); + + HRESULT Create(HWND hwndDlg, SRECT& rc, UINT& nCtrlIdBase); // virtual + + virtual HRESULT HandleControlChangeNotification(); + virtual BOOL OwnsCtrlId(int ctrlId) + { return (ctrlId == GetDlgCtrlID(m_hwndCheckbox)); } + HRESULT CommitValue(); +}; + + +// ---------------------------------------------------------------------------- +// TODO: Underlying plumbing for the LONG and ULOG types aren't implemented +// yet in audiocore. Once that's implemented and verified working, need to +// remove #if TEST stuff +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// Class: +// CLongWidget +// +// Description: +// Handles IDeviceSpecificProperty +// ---------------------------------------------------------------------------- +template <class _TYPE_> +class CLongWidget : public CUIWidget +{ +public: + CLongWidget(IPart* pPart); + ~CLongWidget(); + virtual HRESULT Create(HWND hwndDlg, SRECT& rc, UINT& ctrlIdBase); + virtual HRESULT HandleControlChangeNotification(); + virtual HRESULT CommitValue(); + +private: + HWND m_hwnd; + HWND m_hwndLabel; + HWND m_hwndTrackbar; + HWND m_hwndValue; + _TYPE_ m_Value; + _TYPE_ m_Min; + _TYPE_ m_Max; + LONG m_Stepping; + CComQIPtr<IDeviceSpecificProperty> m_spDevSpecControl; + + static INT_PTR CALLBACK DlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); + void UpdateValueText(); +}; + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// Implementation of CLongWidget + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::CLongWidget +// +// Description: +// CLongWidget<_TYPE_> constructor +// ---------------------------------------------------------------------------- +template <class _TYPE_> +CLongWidget<_TYPE_>::CLongWidget(IPart* pPart) +: CUIWidget(pPart, __uuidof(IDeviceSpecificProperty)), + m_hwndTrackbar(NULL), + m_hwndLabel(NULL), + m_Value(0), + m_Min(0), + m_Max(0), + m_Stepping(1) +{ +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::CLongWidget +// +// Description: +// CLongWidget<_TYPE_> destructor +// ---------------------------------------------------------------------------- +template <class _TYPE_> +CLongWidget<_TYPE_>::~CLongWidget() +{ + DestroyWindow(m_hwnd); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::Create +// +// Description: +// Creates the widget which is a dialog +// +// Parameters: +// hwndDlg - [in] Parent dialog of the widget +// rc - [in] Position of the widget +// ctrlIdBase - [in] Not used +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +template <class _TYPE_> +HRESULT CLongWidget<_TYPE_>::Create +( + HWND hwndDlg, + SRECT& rc, + UINT& ctrlIdBase +) +{ + HRESULT hr = S_OK; + HFONT hFont; + RECT rcClient; + LONG lMin, lMax; + + // Activate the control interface and register for callbacks + hr = CUIWidget::Create(hwndDlg, rc, ctrlIdBase); + IF_FAILED_JUMP(hr, Exit); + + // QI for specific interface + m_spDevSpecControl = m_spControl; + + // Create dialog + m_hwnd = CreateDialogParam( g_hInstance, + MAKEINTRESOURCE(IDD_WIDGET_GEN_LONG), + hwndDlg, + CLongWidget<_TYPE_>::DlgProc, + (LPARAM)this); + + IF_TRUE_ACTION_JUMP((m_hwnd == NULL), hr = HRESULT_FROM_WIN32(GetLastError()), Exit); + + SetWindowPos(m_hwnd, NULL, rc.x, rc.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + GetClientRect(m_hwnd, &rcClient); + rc.w = rcClient.right - rcClient.left; + rc.h = rcClient.bottom - rcClient.top; + + // Make the new controls use the same font as the dialog + hFont = Window_GetFont(hwndDlg); + + m_hwndLabel = GetDlgItem(m_hwnd, IDC_GEN_LABEL); + m_hwndTrackbar = GetDlgItem(m_hwnd, IDC_GEN_SLIDER); + m_hwndValue = GetDlgItem(m_hwnd, IDC_GEN_VALUE); + + Window_SetFont(m_hwndValue, hFont); + Window_SetFont(m_hwndLabel, hFont); + SetWindowText(m_hwndLabel, m_wstrLabel); + + // Set value text + hr = m_spDevSpecControl->Get4BRange(&lMin, &lMax, &m_Stepping); + IF_FAILED_JUMP(hr, Exit); + + m_Min = (_TYPE_)lMin; + m_Max = (_TYPE_)lMax; + + // Set trackbar range and ticks + TrackBar_SetRange(m_hwndTrackbar, 0, 100); + TrackBar_SetTickFrequency(m_hwndTrackbar, 20); // 5 ticks + + // Set trackbar position + HandleControlChangeNotification(); + + // Update the numeric representation of the trackbar + UpdateValueText(); + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::HandleControlChangeNotification +// +// Description: +// Handles widget change notifications +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +template <class _TYPE_> +HRESULT CLongWidget<_TYPE_>::HandleControlChangeNotification() +{ + ATLASSERT(m_spDevSpecControl); + ATLASSERT(m_hwndTrackbar); + + HRESULT hr = S_OK; + DWORD cbValue = sizeof(m_Value); + + // Get current value + hr = m_spDevSpecControl->GetValue(&m_Value, &cbValue); + + if (SUCCEEDED(hr)) + { + float fPos = (float)(m_Value - m_Min) / (float)(m_Max - m_Min); + + // Update trackbar position + TrackBar_SetPos(m_hwndTrackbar, (int)(fPos * 100.f)); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::CommitValue +// +// Description: +// Commit the value set on the control +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +template <class _TYPE_> +HRESULT CLongWidget<_TYPE_>::CommitValue() +{ + ATLASSERT(m_spDevSpecControl); + ATLASSERT(m_hwndTrackbar); + + HRESULT hr = S_OK; + float fPos; + DWORD cbValue = sizeof(m_Value); + + // Calculate new value from trackbar position + fPos = (float)TrackBar_GetPos(m_hwndTrackbar) / 100.f; + m_Value = (_TYPE_)((float)(m_Max - m_Min) * fPos) + m_Min; + + // Commit current setting of trackbar + hr = m_spDevSpecControl->SetValue(&m_Value, cbValue, GetEventContext()); + + return(hr); +}; + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::DlgProc +// +// Description: +// Callback for widget dialog +// +// Parameters: +// hwndDlg - [in] Handle to the widget dialog +// uMsg - [in] Specifies the message +// wParam - [in] Specifies additional message-specific information +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE if it processed the message, FALSE if not +// ---------------------------------------------------------------------------- +template <class _TYPE_> +INT_PTR CALLBACK CLongWidget<_TYPE_>::DlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(wParam); + + CLongWidget<_TYPE_>* pthis = (CLongWidget<_TYPE_>*) + (LONG_PTR)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + BOOL fRet = FALSE; + + switch (msg) + { + case WM_INITDIALOG: + { + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + fRet = FALSE; // don't set focus + break; + } + + case WM_DESTROY: + { + // Don't delete. The Widget destructor is what posts the WM_DESTROY + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, NULL); + fRet = TRUE; + break; + } + + case WM_HSCROLL: + + if (pthis) + { + pthis->m_Value = TrackBar_GetPos(pthis->m_hwndTrackbar); + pthis->UpdateValueText(); + + // Enable the Apply button upon user changing the control + SendMessage(GetParent(GetParent(hwndDlg)), PSM_CHANGED, (WPARAM)(GetParent(hwndDlg)), 0); + } + fRet = TRUE; + break; + } + + return(fRet); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::UpdateValueText +// +// Description: +// Updates the numeric representation of the trackbar +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +template <class _TYPE_> +void CLongWidget<_TYPE_>::UpdateValueText() +{ + WCHAR wsz[12]; + + StringCbPrintfW(wsz, sizeof(wsz), L"0x%08x", m_Value); + + // Set new text + SetWindowText(m_hwndValue, wsz); +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/music.ico b/audio/sysvad/SwapAPO/PropPageExtensions/music.ico Binary files differnew file mode 100644 index 00000000..3207a9dc --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/music.ico diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/resource.h b/audio/sysvad/SwapAPO/PropPageExtensions/resource.h new file mode 100644 index 00000000..a8d36a5a --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/resource.h @@ -0,0 +1,34 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by CplExt.rc +// +#define IDC_DISABLE_SYSFX 5000 // The "Disable System Effects" UI control must have ID 5000 +#define IDS_PROJ_NAME 100 +#define IDR_CPL_EXT 101 +#define IDR_SWAP_PROP_PAGE 102 +#define IDR_ADV_ENDPOINT_PROP_PAGE 103 +#define IDI_AUDIO_ICON 200 +#define IDD_SWAP_PROP_PAGE 201 +#define IDC_ENABLE_SWAP_SFX 203 +#define IDC_ENABLE_SWAP_MFX 204 +#define IDC_SPP_ENDPOINT_NAME 205 +#define IDD_ADV_ENDPOINT_PROP_PAGE 210 +#define IDC_NO_ADV_CONTROLS_FOUND 211 +#define IDC_EPP_ENDPOINT_NAME 212 +#define IDD_WIDGET_GEN_LONG 213 +#define IDC_GEN_LABEL 214 +#define IDC_GEN_SLIDER 215 +#define IDC_GEN_VALUE 216 +#define IDC_ENABLE_DELAY_SFX 217 +#define IDC_ENABLE_DELAY_MFX 218 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 230 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 225 +#define _APS_NEXT_SYMED_VALUE 104 +#endif +#endif diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.cpp new file mode 100644 index 00000000..26e28d90 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.cpp @@ -0,0 +1,18 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: stdafx.cpp +// +// Abstract: Source file that includes just the standard includes +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.h b/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.h new file mode 100644 index 00000000..a116cf07 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.h @@ -0,0 +1,67 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: stdafx.h +// +// Abstract: Include file for standard system include files, or project +// specific include files that are used frequently, but are +// changed infrequently +// +// ---------------------------------------------------------------------------- + + +#pragma once + +#ifndef STRICT +#define STRICT +#endif + +//// Modify the following defines if you have to target a platform prior to the ones specified below. +//// Refer to MSDN for the latest info on corresponding values for different platforms. +//#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later. +//#define WINVER 0x0400 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later. +//#endif +// +//#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later. +//#define _WIN32_WINNT 0x0400 // Change this to the appropriate value to target Windows 2000 or later. +//#endif +// +//#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +//#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +//#endif +// +//#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later. +//#define _WIN32_IE 0x0400 // Change this to the appropriate value to target IE 5.0 or later. +//#endif + +#define _ATL_APARTMENT_THREADED +#define _ATL_NO_AUTOMATIC_NAMESPACE + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + +// Turns off ATL's hiding of some common and often safely ignored warning messages +#define _ATL_ALL_WARNINGS + +#define _ATL_NO_HOSTING + +#include "resource.h" +#include <atlbase.h> +#include <atlcom.h> +#include <atlsync.h> +#include <atlwin.h> // atl windows +using namespace ATL; + +#include <strsafe.h> +#include <commonmacros.h> +#include <tlist.h> +#include <shlobj.h> +#include "CplExt.h" + +extern HINSTANCE g_hInstance; + +_Analysis_mode_(_Analysis_code_type_user_driver_) diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/stdafxsrc.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/stdafxsrc.cpp new file mode 100644 index 00000000..1577c4e3 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/stdafxsrc.cpp @@ -0,0 +1 @@ +#include "stdafx.h"
\ No newline at end of file diff --git a/audio/sysvad/SysVadShared.h b/audio/sysvad/SysVadShared.h new file mode 100644 index 00000000..26cff9e2 --- /dev/null +++ b/audio/sysvad/SysVadShared.h @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + SysvadShared.h + +Abstract: + + Header file for common stuffs between sample SWAP APO and the SysVad sample. +*/ +#ifndef _SYSVADSHARED_H_ +#define _SYSVADSHARED_H_ + +// {D849C827-B24B-4C3D-A26E-338F4FF07ED5} +//DEFINE_GUID(KSPROPSETID_SysVAD, 0xd849c827, 0xb24b, 0x4c3d, 0xa2, 0x6e, 0x33, 0x8f, 0x4f, 0xf0, 0x7e, 0xd5); + + +#define STATIC_KSPROPSETID_SysVAD\ + 0xd849c827, 0xb24b, 0x4c3d, 0xa2, 0x6e, 0x33, 0x8f, 0x4f, 0xf0, 0x7e, 0xd5 +DEFINE_GUIDSTRUCT("D849C827-B24B-4C3D-A26E-338F4FF07ED5", KSPROPSETID_SysVAD); +#define KSPROPSETID_SysVAD DEFINE_GUIDNAMED(KSPROPSETID_SysVAD) + + +typedef enum{ + KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS +} KSPROPERTY_SYSVAD; + +#endif diff --git a/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj new file mode 100644 index 00000000..b2723d08 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj @@ -0,0 +1,295 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{29A5DD25-AB56-4DEE-96AB-21EC2926A300}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <SupportsPackaging>false</SupportsPackaging> + <RequiresPackageProject>true</RequiresPackageProject> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{D75C2887-9174-43D6-993C-B20E6F396D03}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>TabletAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>TabletAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>TabletAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>TabletAudioSample</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS</PreprocessorDefinitions> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\EndpointsCommon</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\adapter.cpp" /> + <ClCompile Include="..\basetopo.cpp" /> + <ClCompile Include="..\common.cpp" /> + <ClCompile Include="..\hw.cpp" /> + <ClCompile Include="..\kshelper.cpp" /> + <ClCompile Include="..\savedata.cpp" /> + <ClCompile Include="..\tonegenerator.cpp" /> + <ClCompile Include="hdmitopo.cpp" /> + <ClCompile Include="micintopo.cpp" /> + <ClCompile Include="spdiftopo.cpp" /> + <ResourceCompile Include="..\sysvad.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj.Filters b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj.Filters new file mode 100644 index 00000000..14b93639 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj.Filters @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{D13BC5E0-7771-4687-83A0-245941715858}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{E07941ED-CD6E-4BAE-A9DE-5B7A970BA57B}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{D9A3BF33-CD62-4610-BA9D-5137A76BD2C9}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{D7BA1DD3-90E0-45B3-9C70-34C9A6F43FEC}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\adapter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\basetopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\common.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\hw.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\kshelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\savedata.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\tonegenerator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="hdmitopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="micintopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="spdiftopo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="..\sysvad.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/TabletAudioSample/hdmitopo.cpp b/audio/sysvad/TabletAudioSample/hdmitopo.cpp new file mode 100644 index 00000000..9deb16cd --- /dev/null +++ b/audio/sysvad/TabletAudioSample/hdmitopo.cpp @@ -0,0 +1,670 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hdmitopo.cpp + +Abstract: + + Implementation of topology miniport for the hdmi endpoint. + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "hdmitopo.h" +#include "hdmitoptable.h" +#include <ntstrsafe.h> + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CreateHdmiMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CHdmiMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CHdmiMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} // CreateHdmiMiniportTopology + +//============================================================================= +CHdmiMiniportTopology::~CHdmiMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CHdmiMiniportTopology::~CHdmiMiniportTopology]")); +} // ~CHdmiMiniportTopology + +//============================================================================= +NTSTATUS +CHdmiMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CHdmiMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CHdmiMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CHdmiMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CHdmiMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= + +#define SINK_DESC_STR L"SinkDescription Sample" + +NTSTATUS +CHdmiMiniportTopology::PropertyHandlerJackSinkInfo +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_SINK_INFO ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackSinkInfo]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_LINEOUT_DEST) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSJACK_SINK_INFORMATION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + size_t cchLength; + + PKSJACK_SINK_INFORMATION sinkInfo = (PKSJACK_SINK_INFORMATION)PropertyRequest->Value; + + RtlZeroMemory(sinkInfo, sizeof(KSJACK_SINK_INFORMATION)); + + // + // Init sink info data (example follows). + // + sinkInfo->ConnType = KSJACK_SINK_CONNECTIONTYPE_HDMI; + sinkInfo->ManufacturerId = 0; + sinkInfo->ProductId = 0; + sinkInfo->AudioLatency = 0; + sinkInfo->HDCPCapable = TRUE; + sinkInfo->AICapable = TRUE; + + cchLength = sizeof(SINK_DESC_STR)/sizeof(WCHAR) - 1; // -1 to remove the null wchar. + if (cchLength < MAX_SINK_DESCRIPTION_NAME_LENGTH) + { + sinkInfo->SinkDescriptionLength = (UCHAR)cchLength; + (void)RtlStringCchCopyW + ( + sinkInfo->SinkDescription, + MAX_SINK_DESCRIPTION_NAME_LENGTH, + SINK_DESC_STR + ); + } + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CHdmiMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + ULONG cJackDescriptions = ARRAYSIZE(HdmiJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HdmiJackDescriptions; + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CHdmiMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(HdmiJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HdmiJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_HdmiTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HdmiTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCHdmiMiniportTopology pMiniport = (PCHdmiMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_SINK_INFO) + { + ntStatus = pMiniport->PropertyHandlerJackSinkInfo(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_HdmiTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/TabletAudioSample/hdmitopo.h b/audio/sysvad/TabletAudioSample/hdmitopo.h new file mode 100644 index 00000000..cf873b70 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/hdmitopo.h @@ -0,0 +1,86 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hdmitopo.h + +Abstract: + + Declaration of topology miniport for the hdmi endpoint. + +--*/ + +#ifndef _SYSVAD_HDMITOPO_H_ +#define _SYSVAD_HDMITOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CHdmiMiniportTopology +// + +class CHdmiMiniportTopology : + public CMiniportTopologySYSVAD, + public IMiniportTopology, + public CUnknown +{ + public: + DECLARE_STD_UNKNOWN(); + CHdmiMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ) + : CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels) + {} + + ~CHdmiMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackSinkInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); +}; + +typedef CHdmiMiniportTopology *PCHdmiMiniportTopology; + + +NTSTATUS +CreateHdmiMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_HdmiTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_HDMITOPO_H_ + diff --git a/audio/sysvad/TabletAudioSample/hdmitoptable.h b/audio/sysvad/TabletAudioSample/hdmitoptable.h new file mode 100644 index 00000000..5fab7cf4 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/hdmitoptable.h @@ -0,0 +1,172 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hdmitoptable.h + +Abstract: + + Declaration of topology tables for the hdmi endpoint. + +--*/ + +#ifndef _SYSVAD_HDMITOPTABLE_H_ +#define _SYSVAD_HDMITOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE HdmiTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE HdmiTopoPinDataRangePointersBridge[] = +{ + &HdmiTopoPinDataRangesBridge[0], + &HdmiTopoPinDataRangesBridge[1] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR HdmiTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HdmiTopoPinDataRangePointersBridge), // DataRangesCount + HdmiTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HdmiTopoPinDataRangePointersBridge), // DataRangesCount + HdmiTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HDMI_INTERFACE, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION HdmiJackDesc = +{ + KSAUDIO_SPEAKER_STEREO, + 0x0000, // no color + eConnTypeOtherDigital, + eGeoLocHDMI, + eGenLocPrimaryBox, + ePortConnJack, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION HdmiJackDescriptions[] = +{ + NULL, + &HdmiJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR HdmiTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHdmiTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HdmiTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HdmiTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_SINK_INFO, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HdmiTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiTopoFilter, PropertiesHdmiTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HdmiTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHdmiTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HdmiTopoMiniportPins), // PinCount + HdmiTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(HdmiTopoMiniportConnections), // ConnectionCount + HdmiTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_HDMITOPTABLE_H_ + + diff --git a/audio/sysvad/TabletAudioSample/hdmiwavtable.h b/audio/sysvad/TabletAudioSample/hdmiwavtable.h new file mode 100644 index 00000000..a5f63c62 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/hdmiwavtable.h @@ -0,0 +1,626 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hdmiwavtable.h + +Abstract: + + Declaration of wave miniport tables for the hdmi endpoint. + + Note: the HDMI in this sample shows how to do h/w loopback + for non-offloaded devices. + +--*/ + +#ifndef _SYSVAD_HDMIWAVTABLE_H_ +#define _SYSVAD_HDMIWAVTABLE_H_ + + +//============================================================================= +// Defines +//============================================================================= + + +#define HDMI_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define HDMI_HOST_MAX_CHANNELS 2 // Max Channels. +#define HDMI_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HDMI_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HDMI_HOST_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define HDMI_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define HDMI_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define HDMI_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HDMI_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HDMI_LOOPBACK_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define HDMI_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define HDMI_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define HDMI_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HDMI_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HDMI_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define HDMI_DOLBY_DIGITAL_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define HDMI_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define HDMI_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HdmiHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_5POINT1_SURROUND, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_5POINT1_SURROUND, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HdmiLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT HdmiHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + NULL, // just an example of no default format for this endpoint/mode + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + NULL, // just an example of no default format for this endpoint/mode + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES HdmiPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + HdmiHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(HdmiHostPinSupportedDeviceFormats), + HdmiHostPinSupportedDeviceModes, + SIZEOF_ARRAY(HdmiHostPinSupportedDeviceModes) + }, + { + RenderLoopbackPin, + HdmiLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(HdmiLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO HdmiPinDataRangesStream[] = +{ + { // 0 - PCM host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HDMI_HOST_MAX_CHANNELS, + HDMI_HOST_MIN_BITS_PER_SAMPLE, + HDMI_HOST_MAX_BITS_PER_SAMPLE, + HDMI_HOST_MIN_SAMPLE_RATE, + HDMI_HOST_MAX_SAMPLE_RATE + }, + { // 1 - PCM loopback + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HDMI_LOOPBACK_MAX_CHANNELS, + HDMI_LOOPBACK_MIN_BITS_PER_SAMPLE, + HDMI_LOOPBACK_MAX_BITS_PER_SAMPLE, + HDMI_LOOPBACK_MIN_SAMPLE_RATE, + HDMI_LOOPBACK_MAX_SAMPLE_RATE + }, + { // 2 - DOLBY-DIGITAL host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HDMI_DOLBY_DIGITAL_MAX_CHANNELS, + HDMI_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE, + HDMI_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE, + HDMI_DOLBY_DIGITAL_MIN_SAMPLE_RATE, + HDMI_DOLBY_DIGITAL_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE HdmiPinDataRangePointersStream[] = +{ + PKSDATARANGE(&HdmiPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&HdmiPinDataRangesStream[2]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE HdmiPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&HdmiPinDataRangesStream[1]) +}; + +//============================================================================= +static +KSDATARANGE HdmiPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE HdmiPinDataRangePointersBridge[] = +{ + &HdmiPinDataRangesBridge[0], + &HdmiPinDataRangesBridge[1] +}; + + +//============================================================================= +static +PCPIN_DESCRIPTOR HdmiWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_SYSTEM + { + HDMI_MAX_INPUT_SYSTEM_STREAMS, + HDMI_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HdmiPinDataRangePointersStream), + HdmiPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_LOOPBACK + { + HDMI_MAX_OUTPUT_LOOPBACK_STREAMS, + HDMI_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HdmiPinDataRangePointersLoopbackStream), + HdmiPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER2_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HdmiPinDataRangePointersBridge), + HdmiPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM HdmiPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiVolume, HdmiPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM HdmiPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiMute, HdmiPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM HdmiPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiPeakMeter, HdmiPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR HdmiWaveMiniportNodes[] = +{ + // KSNODE_WAVE_SUM + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_SUM, // Type + NULL // Name + }, + // KSNODE_WAVE_VOLUME + { + 0, // Flags + &AutomationHdmiVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_WAVE_VOLUME // Name + }, + // KSNODE_WAVE_MUTE + { + 0, // Flags + &AutomationHdmiMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_WAVE_MUTE // Name + }, + // KSNODE_WAVE_PEAKMETER + { + 0, // Flags + &AutomationHdmiPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_WAVE_SUM == 0 ); +C_ASSERT( KSNODE_WAVE_VOLUME == 1 ); +C_ASSERT( KSNODE_WAVE_MUTE == 2 ); +C_ASSERT( KSNODE_WAVE_PEAKMETER == 3 ); + + +//============================================================================= +// +// Note: +// 1) A sum node can be used to 'sum' multiple streams associated to different +// instances of the same pin. +// 2) Any node output has an implicit 'tee' node associated with it. Thus in the +// scenario below it is ok for the peak meter node to have two identical outputs. +// +// ---------------------------------------------------------- +// | | +// System Pin 0 | |-----| |--------| |------| |-----------| |--> 1 Loopback Pin +// [instances:0...n]-->| -->| Sum |--->| Volume |-->| Mute |-->| PeakMeter |--> | +// | |-----| |--------| |------| |-----------| |--> 2 KSPIN_WAVE_RENDER2_SOURCE +// | | +// ---------------------------------------------------------- + +static +PCCONNECTION_DESCRIPTOR HdmiWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER2_SINK_SYSTEM, KSNODE_WAVE_SUM, 1 }, + { KSNODE_WAVE_SUM, 0, KSNODE_WAVE_VOLUME, 1 }, + { KSNODE_WAVE_VOLUME, 0, KSNODE_WAVE_MUTE, 1 }, + { KSNODE_WAVE_MUTE, 0, KSNODE_WAVE_PEAKMETER, 1 }, + { KSNODE_WAVE_PEAKMETER, 2, PCFILTER_NODE, KSPIN_WAVE_RENDER2_SINK_LOOPBACK }, + { KSNODE_WAVE_PEAKMETER, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER2_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHdmiWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiWaveFilter, PropertiesHdmiWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HdmiWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHdmiWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HdmiWaveMiniportPins), // PinCount + HdmiWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(HdmiWaveMiniportNodes), // NodeCount + HdmiWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(HdmiWaveMiniportConnections), // ConnectionCount + HdmiWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_HDMIWAVTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micarray2toptable.h b/audio/sysvad/TabletAudioSample/micarray2toptable.h new file mode 100644 index 00000000..0c03a839 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micarray2toptable.h @@ -0,0 +1,229 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray2toptable.h + +Abstract: + + Declaration of topology tables for the mic array (back). + +--*/ + +#ifndef _SYSVAD_MICARRAY2TOPTABLE_H_ +#define _SYSVAD_MICARRAY2TOPTABLE_H_ + +// +// {3fe0e3e1-ad16-4772-8382-4129169018ce} +DEFINE_GUID(MICARRAY2_CUSTOM_NAME, +0x3fe0e3e1, 0xad16, 0x4772, 0x83, 0x82, 0x41, 0x29, 0x16, 0x90, 0x18, 0xce); + +//============================================================================= +static +KSDATARANGE MicArray2TopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicArray2TopoPinDataRangePointersBridge[] = +{ + &MicArray2TopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray2TopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray2TopoPinDataRangePointersBridge), // DataRangesCount + MicArray2TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE_ARRAY, // Category + &MICARRAY2_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray2TopoPinDataRangePointersBridge), // DataRangesCount + MicArray2TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicArray2PropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray2Volume, MicArray2PropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray2PropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray2Mute, MicArray2PropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray2PropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray2PeakMeter, MicArray2PropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray2TopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicArray2Volume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicArray2Mute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicArray2PeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArray2TopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicArray2PropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray2TopoFilter, MicArray2PropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray2TopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray2TopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray2TopoMiniportPins), // PinCount + MicArray2TopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray2TopologyNodes), // NodeCount + MicArray2TopologyNodes, // Nodes + SIZEOF_ARRAY(MicArray2TopoMiniportConnections),// ConnectionCount + MicArray2TopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICARRAY2TOPTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micarray3toptable.h b/audio/sysvad/TabletAudioSample/micarray3toptable.h new file mode 100644 index 00000000..02a3d76e --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micarray3toptable.h @@ -0,0 +1,228 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray3toptable.h + +Abstract: + + Declaration of topology tables for the mic array (front/back). + +--*/ + +#ifndef _SYSVAD_MICARRAY3TOPTABLE_H_ +#define _SYSVAD_MICARRAY3TOPTABLE_H_ + +// +// {c04bdb7c-2138-48da-9dd4-2af9ff2e58c2} +DEFINE_GUID(MICARRAY3_CUSTOM_NAME, +0xc04bdb7c, 0x2138, 0x48da, 0x9d, 0xd4, 0x2a, 0xf9, 0xff, 0x2e, 0x58, 0xc2); + +//============================================================================= +static +KSDATARANGE MicArray3TopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicArray3TopoPinDataRangePointersBridge[] = +{ + &MicArray3TopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray3TopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray3TopoPinDataRangePointersBridge), // DataRangesCount + MicArray3TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE_ARRAY, // Category + &MICARRAY3_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray3TopoPinDataRangePointersBridge), // DataRangesCount + MicArray3TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicArray3PropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3Volume, MicArray3PropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray3PropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3Mute, MicArray3PropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray3PropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3PeakMeter, MicArray3PropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray3TopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicArray3Volume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicArray3Mute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicArray3PeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArray3TopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicArray3PropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3TopoFilter, MicArray3PropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray3TopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray3TopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray3TopoMiniportPins), // PinCount + MicArray3TopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray3TopologyNodes), // NodeCount + MicArray3TopologyNodes, // Nodes + SIZEOF_ARRAY(MicArray3TopoMiniportConnections),// ConnectionCount + MicArray3TopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICARRAY3TOPTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micarray3wavtable.h b/audio/sysvad/TabletAudioSample/micarray3wavtable.h new file mode 100644 index 00000000..34e3df18 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micarray3wavtable.h @@ -0,0 +1,455 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray3wavtable.h + +Abstract: + + Declaration of wave miniport tables for the combined mic array (front/back). + +--*/ + +#ifndef _SYSVAD_MICARRAY3WAVTABLE_H_ +#define _SYSVAD_MICARRAY3WAVTABLE_H_ + +// +// Mic array range. +// +#define MICARRAY3_RAW_CHANNELS 4 // Channels for raw mode +#define MICARRAY3_PROCESSED_CHANNELS 1 // Channels for default mode +#define MICARRAY3_DEVICE_MAX_CHANNELS 4 // Max channels overall +#define MICARRAY3_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define MICARRAY3_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define MICARRAY3_RAW_SAMPLE_RATE 48000 // Raw sample rate + +// +// Max # of pin instances. +// +#define MICARRAY3_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicArray3PinSupportedDeviceFormats[] = +{ + // 0 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this first element as the proposed DEFAULT format + // 48 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 1 + // 16 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 2 + // 11.025 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 3 + // 8 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 4 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this last element as the proposed RAW format + // 48 KHz 16-bit 4 channels + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 4, + 48000, + 384000, + 8, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + 0, // No channel configuration for unprocessed mic array + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicArray3PinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicArray3PinSupportedDeviceFormats[SIZEOF_ARRAY(MicArray3PinSupportedDeviceFormats)-1].DataFormat + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &MicArray3PinSupportedDeviceFormats[0].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES MicArray3PinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicArray3PinSupportedDeviceFormats, + SIZEOF_ARRAY(MicArray3PinSupportedDeviceFormats), + MicArray3PinSupportedDeviceModes, + SIZEOF_ARRAY(MicArray3PinSupportedDeviceModes) + } +}; + +//============================================================================= +// Data ranges +// +// See CMiniportWaveRT::DataRangeIntersection. +// +// Both mono and two-channel formats are supported for the mic arrays. The +// design of this sample driver's data range intersection handler requires a +// separate data for each supported channel count. +// +static +KSDATARANGE_AUDIO MicArray3PinDataRangesRawStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_RAW_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + MICARRAY3_RAW_SAMPLE_RATE, + MICARRAY3_RAW_SAMPLE_RATE + }, +}; + +static +KSDATARANGE_AUDIO MicArray3PinDataRangesProcessedStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_PROCESSED_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + 48000, + 48000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_PROCESSED_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + 16000, + 16000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_PROCESSED_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + 11025, + 11025 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_PROCESSED_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + 8000, + 8000 + }, +}; + +static +PKSDATARANGE MicArray3PinDataRangePointersStream[] = +{ + PKSDATARANGE(&MicArray3PinDataRangesProcessedStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&MicArray3PinDataRangesRawStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE MicArray3PinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicArray3PinDataRangePointersBridge[] = +{ + &MicArray3PinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray3WaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArray3PinDataRangePointersBridge), + MicArray3PinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVE_HOST + { + MICARRAY3_MAX_INPUT_STREAMS, + MICARRAY3_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArray3PinDataRangePointersStream), + MicArray3PinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray3WaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArray3WaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesMicArray3WaveFilter[] = +{ + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3WaveFilter, PropertiesMicArray3WaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray3WaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray3WaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray3WaveMiniportPins), // PinCount + MicArray3WaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray3WaveMiniportNodes), // NodeCount + MicArray3WaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicArray3WaveMiniportConnections), // ConnectionCount + MicArray3WaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_MICARRAY3WAVTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micintopo.cpp b/audio/sysvad/TabletAudioSample/micintopo.cpp new file mode 100644 index 00000000..6c1de10f --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micintopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micintopo.cpp + +Abstract: + + Implementation of topology miniport for the mic (external: headphone). + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "micintopo.h" +#include "micintoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_MicInJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicInJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(MicInJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = MicInJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicInJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicInJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(MicInJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = MicInJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicInTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicInTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_MicInJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_MicInJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_MicInTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/TabletAudioSample/micintopo.h b/audio/sysvad/TabletAudioSample/micintopo.h new file mode 100644 index 00000000..af4b8be2 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micintopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micintopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: headphone). + +--*/ + +#ifndef _SYSVAD_MICINTOPO_H_ +#define _SYSVAD_MICINTOPO_H_ + +NTSTATUS PropertyHandler_MicInTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_MICINTOPO_H_ diff --git a/audio/sysvad/TabletAudioSample/micintoptable.h b/audio/sysvad/TabletAudioSample/micintoptable.h new file mode 100644 index 00000000..329c2f2b --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micintoptable.h @@ -0,0 +1,250 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micintopotable.h + +Abstract: + + Declaration of topology table for the mic (external: headphone) + +--*/ + +#ifndef _SYSVAD_MICINTOPTABLE_H_ +#define _SYSVAD_MICINTOPTABLE_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_MicInTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +// +// {d48deb08-fd1c-4d1e-b821-9064d49ae96e} +DEFINE_GUID(MICIN_CUSTOM_NAME, +0xd48deb08, 0xfd1c, 0x4d1e, 0xb8, 0x21, 0x90, 0x64, 0xd4, 0x9a, 0xe9, 0x6e); + +//============================================================================= +static +KSJACK_DESCRIPTION MicInJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + JACKDESC_RGB(179, 201, 140), + eConnTypeCombination, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + TRUE +}; + +//============================================================================= +static +KSDATARANGE MicInTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicInTopoPinDataRangePointersBridge[] = +{ + &MicInTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicInTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicInTopoPinDataRangePointersBridge),// DataRangesCount + MicInTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE, // Category + &MICIN_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN - topology filter out-in + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicInTopoPinDataRangePointersBridge),// DataRangesCount + MicInTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION MicInJackDescriptions[] = +{ + &MicInJackDesc, + NULL +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicInPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInVolume, MicInPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicInPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInMute, MicInPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicInPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInPeakMeter, MicInPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicInTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicInVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicInMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicInPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicInMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicInPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicInTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicInTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInTopoFilter, MicInPropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicInTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicInTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicInTopoMiniportPins), // PinCount + MicInTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicInTopologyNodes), // NodeCount + MicInTopologyNodes, // Nodes + SIZEOF_ARRAY(MicInMiniportConnections), // ConnectionCount + MicInMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICINTOPTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micinwavtable.h b/audio/sysvad/TabletAudioSample/micinwavtable.h new file mode 100644 index 00000000..42598c4e --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micinwavtable.h @@ -0,0 +1,440 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micinwavtable.h + +Abstract: + + Declaration of wave miniport tables for the mic (external: headphone). + +--*/ + +#ifndef _SYSVAD_MICINWAVTABLE_H_ +#define _SYSVAD_MICINWAVTABLE_H_ + +// +// Mic in (external: headphone) range. +// +#define MICIN_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define MICIN_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define MICIN_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define MICIN_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define MICIN_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define MICIN_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicInPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 22050, + 44100, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 24000, + 48000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 32000, + 64000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 6 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 7 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicInPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicInPinSupportedDeviceFormats[SIZEOF_ARRAY(MicInPinSupportedDeviceFormats) - 1].DataFormat, + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &MicInPinSupportedDeviceFormats[SIZEOF_ARRAY(MicInPinSupportedDeviceFormats) - 1].DataFormat, + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH, + &MicInPinSupportedDeviceFormats[2].DataFormat, // 16KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &MicInPinSupportedDeviceFormats[4].DataFormat, // 24KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +static +PIN_DEVICE_FORMATS_AND_MODES MicInPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicInPinSupportedDeviceFormats, + SIZEOF_ARRAY(MicInPinSupportedDeviceFormats), + MicInPinSupportedDeviceModes, + SIZEOF_ARRAY(MicInPinSupportedDeviceModes) + } +}; + +//============================================================================= +static +KSDATARANGE MicInPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicInPinDataRangePointersBridge[] = +{ + &MicInPinDataRangesBridge[0] +}; + +//============================================================================= +static +KSDATARANGE_AUDIO MicInPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICIN_DEVICE_MAX_CHANNELS, + MICIN_MIN_BITS_PER_SAMPLE_PCM, + MICIN_MAX_BITS_PER_SAMPLE_PCM, + MICIN_MIN_SAMPLE_RATE, + MICIN_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE MicInPinDataRangePointersStream[] = +{ + PKSDATARANGE(&MicInPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicInWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicInPinDataRangePointersBridge), + MicInPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + MICIN_MAX_INPUT_STREAMS, + MICIN_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicInPinDataRangePointersStream), + MicInPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicInWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicInWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesMicInWaveFilter[] = +{ + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInWaveFilter, PropertiesMicInWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicInWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicInWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicInWaveMiniportPins), // PinCount + MicInWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicInWaveMiniportNodes), // NodeCount + MicInWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicInWaveMiniportConnections), // ConnectionCount + MicInWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_MICINWAVTABLE_H_ diff --git a/audio/sysvad/TabletAudioSample/minipairs.h b/audio/sysvad/TabletAudioSample/minipairs.h new file mode 100644 index 00000000..f35398d4 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/minipairs.h @@ -0,0 +1,512 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minipairs.h + +Abstract: + + Local audio endpoint filter definitions. + +--*/ + +#ifndef _SYSVAD_MINIPAIRS_H_ +#define _SYSVAD_MINIPAIRS_H_ + +#include "speakertopo.h" +#include "speakertoptable.h" +#include "speakerwavtable.h" + +#include "speakerhptopo.h" +#include "speakerhptoptable.h" +#include "speakerhpwavtable.h" + +#include "hdmitopo.h" +#include "hdmitoptable.h" +#include "hdmiwavtable.h" + +#include "spdiftopo.h" +#include "spdiftoptable.h" +#include "spdifwavtable.h" + +#include "micintopo.h" +#include "micintoptable.h" +#include "micinwavtable.h" + +#include "micarraytopo.h" +#include "micarray1toptable.h" +#include "micarray2toptable.h" +#include "micarray3toptable.h" +#include "micarraywavtable.h" +#include "micarray3wavtable.h" + +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +// +// Describe buffer size constraints for WaveRT buffers +// +static struct +{ + KSAUDIO_PACKETSIZE_CONSTRAINTS TransportPacketConstraints; + KSAUDIO_PACKETSIZE_PROCESSINGMODE_CONSTRAINT AdditionalProcessingConstraints[1]; +} SysvadWaveRtPacketSizeConstraintsRender = +{ + { + 1 * HNSTIME_PER_MILLISECOND, // 1 ms minimum processing interval + FILE_256_BYTE_ALIGNMENT, // 256 byte packet size alignment + 0, // reserved + 2, // 2 processing constraints follow + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, // constraint for default processing mode + 128, // 128 samples per processing frame + 0, // NA hns per processing frame + }, + }, + { + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, // constraint for movie processing mode + 1024, // 1024 samples per processing frame + 0, // NA hns per processing frame + }, + } +}; + +const SYSVAD_DEVPROPERTY SysvadWaveFilterInterfacePropertiesRender[] = +{ + { + &DEVPKEY_KsAudio_PacketSize_Constraints, // Key + DEVPROP_TYPE_BINARY, // Type + sizeof(SysvadWaveRtPacketSizeConstraintsRender), // BufferSize + &SysvadWaveRtPacketSizeConstraintsRender, // Buffer + }, +}; + +static struct +{ + KSAUDIO_PACKETSIZE_CONSTRAINTS TransportPacketConstraints; +} SysvadWaveRtPacketSizeConstraintsCapture = +{ + { + 0, // no minimum processing interval + FILE_128_BYTE_ALIGNMENT, // 128 byte packet size alignment + 0, // reserved + 1, // 1 processing constraint follows + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, // constraint for communications processing mode + 0, // NA samples per processing frame + 20 * HNSTIME_PER_MILLISECOND, // 200000 hns (20ms) per processing frame + }, + }, +}; + +const SYSVAD_DEVPROPERTY SysvadWaveFilterInterfacePropertiesCapture[] = +{ + { + &DEVPKEY_KsAudio_PacketSize_Constraints, // Key + DEVPROP_TYPE_BINARY, // Type + sizeof(SysvadWaveRtPacketSizeConstraintsCapture), // BufferSize + &SysvadWaveRtPacketSizeConstraintsCapture, // Buffer + }, +}; + +// +// Render miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for speaker (internal) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerMiniports = +{ + eSpeakerDevice, + L"TopologySpeaker", // make sure this name matches with KSNAME_TopologySpeaker in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &SpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpeaker", // make sure this name matches with KSNAME_WaveSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpeakerWaveMiniportFilterDescriptor, + ARRAYSIZE(SysvadWaveFilterInterfacePropertiesRender), // Interface properties + SysvadWaveFilterInterfacePropertiesRender, + SPEAKER_DEVICE_MAX_CHANNELS, + SpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerPinDeviceFormatsAndModes), + SpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for speaker (external:headphone) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerHpTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerHpMiniports = +{ + eSpeakerHpDevice, + L"TopologySpeakerHeadphone", // make sure this name matches with KSNAME_TopologySpeakerHeadphone in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &SpeakerHpTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpeakerHeadphone", // make sure this name matches with KSNAME_WaveSpeakerHeadphone in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpeakerHpWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + SPEAKERHP_DEVICE_MAX_CHANNELS, + SpeakerHpPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerHpPinDeviceFormatsAndModes), + SpeakerHpTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerHpTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for hdmi endpoint * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 1|---> Loopback | | * +* | | | | * +* | 2|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE HdmiTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER2_SOURCE, // WaveOut (no offloading) + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR HdmiMiniports = +{ + eHdmiRenderDevice, + L"TopologyHdmi", // make sure this name matches with KSNAME_TopologyHdmi in the inf's [Strings] section + CreateHdmiMiniportTopology, + &HdmiTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveHdmi", // make sure this name matches with KSNAME_WaveHdmi in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &HdmiWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + HDMI_DEVICE_MAX_CHANNELS, + HdmiPinDeviceFormatsAndModes, + SIZEOF_ARRAY(HdmiPinDeviceFormatsAndModes), + HdmiTopologyPhysicalConnections, + SIZEOF_ARRAY(HdmiTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +/********************************************************************* +* Topology/Wave bridge connection for spdif (internal) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpdifTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpdifMiniports = +{ + eSpdifRenderDevice, + L"TopologySpdif", // make sure this name matches with KSNAME_TopologySpeaker in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &SpdifTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpdif", // make sure this name matches with KSNAME_WaveSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpdifWaveMiniportFilterDescriptor, + ARRAYSIZE(SysvadWaveFilterInterfacePropertiesRender), // Interface properties + SysvadWaveFilterInterfacePropertiesRender, + SPDIF_DEVICE_MAX_CHANNELS, + SpdifPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpdifPinDeviceFormatsAndModes), + SpdifTopologyPhysicalConnections, + SIZEOF_ARRAY(SpdifTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +// +// Capture miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic in * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicInTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicInMiniports = +{ + eMicInDevice, + L"TopologyMicIn", // make sure this name matches with KSNAME_TopologyMicIn in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &MicInTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicIn", // make sure this name matches with KSNAME_WaveMicIn in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicInWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICIN_DEVICE_MAX_CHANNELS, + MicInPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicInPinDeviceFormatsAndModes), + MicInTopologyPhysicalConnections, + SIZEOF_ARRAY(MicInTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +/********************************************************************* +* Topology/Wave bridge connection for mic array 1 (front) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray1TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray1Miniports = +{ + eMicArrayDevice1, + L"TopologyMicArray1", // make sure this name matches with KSNAME_TopologyMicArray1 in the inf's [Strings] section + CreateMicArrayMiniportTopology, + &MicArray1TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray1", // make sure this name matches with KSNAME_WaveMicArray1 in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicArrayWaveMiniportFilterDescriptor, + ARRAYSIZE(SysvadWaveFilterInterfacePropertiesCapture), // Interface properties + SysvadWaveFilterInterfacePropertiesCapture, + MICARRAY_DEVICE_MAX_CHANNELS, + MicArrayPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArrayPinDeviceFormatsAndModes), + MicArray1TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray1TopologyPhysicalConnections), + ENDPOINT_SOUNDDETECTOR_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for mic array 2 (back) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray2TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray2Miniports = +{ + eMicArrayDevice2, + L"TopologyMicArray2", // make sure this name matches with KSNAME_TopologyMicArray2 in the inf's [Strings] section + CreateMicArrayMiniportTopology, + &MicArray2TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray2", // make sure this name matches with KSNAME_WaveMicArray2 in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicArrayWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICARRAY_DEVICE_MAX_CHANNELS, + MicArrayPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArrayPinDeviceFormatsAndModes), + MicArray2TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray2TopologyPhysicalConnections), + ENDPOINT_SOUNDDETECTOR_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for mic array 3 (combined) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray3TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray3Miniports = +{ + eMicArrayDevice3, + L"TopologyMicArray3", // make sure this name matches with KSNAME_TopologyMicArray3 in the inf's [Strings] section + CreateMicArrayMiniportTopology, + &MicArray3TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray3", // make sure this name matches with KSNAME_WaveMicArray3 in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicArray3WaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICARRAY3_DEVICE_MAX_CHANNELS, + MicArray3PinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArray3PinDeviceFormatsAndModes), + MicArray3TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray3TopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +//============================================================================= +// +// Render miniport pairs. +// +static +PENDPOINT_MINIPAIR g_RenderEndpoints[] = +{ + &SpeakerMiniports, + &SpeakerHpMiniports, + &HdmiMiniports, + &SpdifMiniports, +}; + +#define g_cRenderEndpoints (SIZEOF_ARRAY(g_RenderEndpoints)) + +//============================================================================= +// +// Capture miniport pairs. +// +static +PENDPOINT_MINIPAIR g_CaptureEndpoints[] = +{ + &MicInMiniports, + &MicArray1Miniports, + &MicArray2Miniports, + &MicArray3Miniports, +}; + +#define g_cCaptureEndpoints (SIZEOF_ARRAY(g_CaptureEndpoints)) + +//============================================================================= +// +// Total miniports = # endpoints * 2 (topology + wave). +// +#define g_MaxMiniports ((g_cRenderEndpoints + g_cCaptureEndpoints) * 2) + +#endif // _SYSVAD_MINIPAIRS_H_ + diff --git a/audio/sysvad/TabletAudioSample/spdiftopo.cpp b/audio/sysvad/TabletAudioSample/spdiftopo.cpp new file mode 100644 index 00000000..f715e41d --- /dev/null +++ b/audio/sysvad/TabletAudioSample/spdiftopo.cpp @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + spdiftopo.cpp + +Abstract: + + Implementation of topology miniport for the spdif (internal). + +--*/ + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "spdiftopo.h" +#include "spdiftoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_SpdifTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpdifTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription( + PropertyRequest, + ARRAYSIZE(SpdifJackDescriptions), + SpdifJackDescriptions + ); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2( + PropertyRequest, + ARRAYSIZE(SpdifJackDescriptions), + SpdifJackDescriptions, + 0 // jack capabilities + ); + } + } + + return ntStatus; +} // PropertyHandler_SpdifTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/TabletAudioSample/spdiftopo.h b/audio/sysvad/TabletAudioSample/spdiftopo.h new file mode 100644 index 00000000..ffaefb5c --- /dev/null +++ b/audio/sysvad/TabletAudioSample/spdiftopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertopo.h + +Abstract: + + Declaration of topology miniport for the speaker (internal). + +--*/ + +#ifndef _SYSVAD_SPDIFTOPO_H_ +#define _SYSVAD_SPDIFTOPO_H_ + +NTSTATUS PropertyHandler_SpdifTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_SPDIFTOPO_H_ diff --git a/audio/sysvad/TabletAudioSample/spdiftoptable.h b/audio/sysvad/TabletAudioSample/spdiftoptable.h new file mode 100644 index 00000000..8e1376a8 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/spdiftoptable.h @@ -0,0 +1,166 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + spdiftoptable.h + +Abstract: + + Declaration of topology tables for the SPDIF endpoint. + +--*/ + +#ifndef _SYSVAD_SPDIFTOPTABLE_H_ +#define _SYSVAD_SPDIFTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE SpdifTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpdifTopoPinDataRangePointersBridge[] = +{ + &SpdifTopoPinDataRangesBridge[0], + &SpdifTopoPinDataRangesBridge[1] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpdifTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpdifTopoPinDataRangePointersBridge), // DataRangesCount + SpdifTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpdifTopoPinDataRangePointersBridge), // DataRangesCount + SpdifTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_SPDIF_INTERFACE, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION SpdifJackDesc = +{ + KSAUDIO_SPEAKER_STEREO, + 0x0000, // no color + eConnTypeOtherDigital, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpdifJackDescriptions[] = +{ + NULL, + &SpdifJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR SpdifTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpdifTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpdifTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpdifTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpdifTopoFilter, PropertiesSpdifTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpdifTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpdifTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpdifTopoMiniportPins), // PinCount + SpdifTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpdifTopoMiniportConnections), // ConnectionCount + SpdifTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_SPDIFTOPTABLE_H_ + + diff --git a/audio/sysvad/TabletAudioSample/spdifwavtable.h b/audio/sysvad/TabletAudioSample/spdifwavtable.h new file mode 100644 index 00000000..963822a8 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/spdifwavtable.h @@ -0,0 +1,854 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + spdifwavtable.h + +Abstract: + + Declaration of wave miniport tables for the SPDIF endpoint. + +--*/ + +#ifndef _SYSVAD_SPDIFWAVTABLE_H_ +#define _SYSVAD_SPDIFWAVTABLE_H_ + + +//============================================================================= +// Defines +//============================================================================= + + +#define SPDIF_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPDIF_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPDIF_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPDIF_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPDIF_HOST_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPDIF_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPDIF_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define SPDIF_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPDIF_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPDIF_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPDIF_OFFLOAD_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPDIF_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define SPDIF_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPDIF_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPDIF_LOOPBACK_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPDIF_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPDIF_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define SPDIF_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPDIF_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPDIF_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPDIF_DOLBY_DIGITAL_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPDIF_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define SPDIF_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define SPDIF_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpdifAudioEngineSupportedDeviceFormats[] = +{ + { // 0 : First entry in this table is the default format for the audio engine + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpdifHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_5POINT1_SURROUND, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_5POINT1_SURROUND, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpdifOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpdifLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpdifHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + NULL, // just an example of no default format for this endpoint/mode + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + NULL, // just an example of no default format for this endpoint/mode + } +}; + +static +MODE_AND_DEFAULT_FORMAT SpdifOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + NULL, // just an example of no default format for this endpoint/mode + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + NULL, // just an example of no default format for this endpoint/mode + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpdifPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpdifHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpdifHostPinSupportedDeviceFormats), + SpdifHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpdifHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + SpdifOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpdifOffloadPinSupportedDeviceFormats), + SpdifOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(SpdifOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + SpdifLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpdifLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + SpdifAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(SpdifAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpdifPinDataRangesStream[] = +{ + { // 0 - PCM host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPDIF_HOST_MAX_CHANNELS, + SPDIF_HOST_MIN_BITS_PER_SAMPLE, + SPDIF_HOST_MAX_BITS_PER_SAMPLE, + SPDIF_HOST_MIN_SAMPLE_RATE, + SPDIF_HOST_MAX_SAMPLE_RATE + }, + { // 1 - PCM offload + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPDIF_OFFLOAD_MAX_CHANNELS, + SPDIF_OFFLOAD_MIN_BITS_PER_SAMPLE, + SPDIF_OFFLOAD_MAX_BITS_PER_SAMPLE, + SPDIF_OFFLOAD_MIN_SAMPLE_RATE, + SPDIF_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 - PCM loopback + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPDIF_LOOPBACK_MAX_CHANNELS, + SPDIF_LOOPBACK_MIN_BITS_PER_SAMPLE, + SPDIF_LOOPBACK_MAX_BITS_PER_SAMPLE, + SPDIF_LOOPBACK_MIN_SAMPLE_RATE, + SPDIF_LOOPBACK_MAX_SAMPLE_RATE + }, + { // 3 - DOLBY-DIGITAL host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPDIF_DOLBY_DIGITAL_MAX_CHANNELS, + SPDIF_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE, + SPDIF_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE, + SPDIF_DOLBY_DIGITAL_MIN_SAMPLE_RATE, + SPDIF_DOLBY_DIGITAL_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpdifPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpdifPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&SpdifPinDataRangesStream[3]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpdifPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&SpdifPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList), + +}; + +static +PKSDATARANGE SpdifPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&SpdifPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE SpdifPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpdifPinDataRangePointersBridge[] = +{ + &SpdifPinDataRangesBridge[0], + &SpdifPinDataRangesBridge[1] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesSpdifOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpdifOffloadPin, PropertiesSpdifOffloadPin); + + +//============================================================================= +static +PCPIN_DESCRIPTOR SpdifWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_SYSTEM + { + SPDIF_MAX_INPUT_SYSTEM_STREAMS, + SPDIF_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpdifPinDataRangePointersStream), + SpdifPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + SPDIF_MAX_INPUT_OFFLOAD_STREAMS, + SPDIF_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationSpdifOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpdifPinDataRangePointersOffloadStream), + SpdifPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_LOOPBACK + { + SPDIF_MAX_OUTPUT_LOOPBACK_STREAMS, + SPDIF_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpdifPinDataRangePointersLoopbackStream), + SpdifPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER2_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpdifPinDataRangePointersBridge), + SpdifPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR SpdifWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; + +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpdifWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpdifWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_SysVAD, + KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpdifWaveFilter, PropertiesSpdifWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpdifWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpdifWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpdifWaveMiniportPins), // PinCount + SpdifWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpdifWaveMiniportNodes), // NodeCount + SpdifWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(SpdifWaveMiniportConnections), // ConnectionCount + SpdifWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_SPDIFWAVTABLE_H_ + diff --git a/audio/sysvad/ToneGenerator.cpp b/audio/sysvad/ToneGenerator.cpp new file mode 100644 index 00000000..24a85eba --- /dev/null +++ b/audio/sysvad/ToneGenerator.cpp @@ -0,0 +1,261 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + ToneGenerator + +Abstract: + + Implementation of SYSVAD sine wave generator + + +--*/ +#include <sysvad.h> +#include "ToneGenerator.h" + +const double TONE_AMPLITUDE = 0.5; // Scalar value, should be between 0.0 - 1.0 +const double TWO_PI = M_PI * 2; + +// +// Double to short conversion. +// +short ConvertToShort(double Value) +{ + return (short)(Value * _I16_MAX); +}; + +// +// Double to char conversion. +// +unsigned char ConvertToUChar(double Value) +{ + const double F_127_5 = 127.5; + return (unsigned char)(Value * F_127_5 + F_127_5); +}; + +// +// Ctor: basic init. +// +ToneGenerator::ToneGenerator() +: m_Frequency(0), + m_ChannelCount(0), + m_BitsPerSample(0), + m_SamplesPerSecond(0), + m_Mute(false), + m_PartialFrame(NULL), + m_PartialFrameBytes(0), + m_FrameSize(0) +{ + // Theta (double) and SampleIncrement (double) are init in the Init() method + // after saving the floating point state. +} + +// +// Dtor: free resources. +// +ToneGenerator::~ToneGenerator() +{ + if (m_PartialFrame) + { + ExFreePoolWithTag(m_PartialFrame, SYSVAD_POOLTAG); + m_PartialFrame = NULL; + m_PartialFrameBytes = 0; + } +} + +// +// Init a new frame. +// Note: caller will save and restore the floatingpoint state. +// +#pragma warning(push) +// Caller wraps this routine between KeSaveFloatingPointState/KeRestoreFloatingPointState calls. +#pragma warning(disable: 28110) + +VOID ToneGenerator::InitNewFrame +( + _Out_writes_bytes_(FrameSize) BYTE* Frame, + _In_ DWORD FrameSize +) +{ + double sinValue = TONE_AMPLITUDE * sin( m_Theta ); + + if (FrameSize != (DWORD)m_ChannelCount * m_BitsPerSample/8) + { + ASSERT(FALSE); + RtlZeroMemory(Frame, FrameSize); + return; + } + + for(ULONG i = 0; i < m_ChannelCount; ++i) + { + if (m_BitsPerSample == 8) + { + unsigned char *dataBuffer = reinterpret_cast<unsigned char *>(Frame); + dataBuffer[i] = ConvertToUChar(sinValue); + } + else // 16 bits per sample + { + short *dataBuffer = reinterpret_cast<short *>(Frame); + dataBuffer[i] = ConvertToShort(sinValue); + } + } + + m_Theta += m_SampleIncrement; + if (m_Theta >= TWO_PI) + { + m_Theta -= TWO_PI; + } +} +#pragma warning(pop) + +// +// GenerateSamples() +// +// Generate a sine wave that fits into the specified buffer. +// +// Buffer - Buffer to hold the samples +// BufferLength - Length of the buffer. +// +// Note: this function supports 16bit and 8bit samples only. +// +void ToneGenerator::GenerateSine +( + _Out_writes_bytes_(BufferLength) BYTE *Buffer, + _In_ size_t BufferLength +) +{ + NTSTATUS status; + KFLOATING_SAVE saveData; + BYTE * buffer; + size_t length; + size_t copyBytes; + + if (m_Mute) + { + goto ZeroBuffer; + } + + status = KeSaveFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + goto ZeroBuffer; + } + + buffer = Buffer; + length = BufferLength; + + // + // Check if we have any residual frame bytes from the last time. + // + if (m_PartialFrameBytes) + { + ASSERT(m_FrameSize > m_PartialFrameBytes); + DWORD offset = m_FrameSize - m_PartialFrameBytes; + copyBytes = MIN(m_PartialFrameBytes, length); + RtlCopyMemory(buffer, m_PartialFrame + offset, copyBytes); + RtlZeroMemory(m_PartialFrame + offset, copyBytes); + length -= copyBytes; + buffer += copyBytes; + m_PartialFrameBytes = 0; + } + + IF_TRUE_JUMP(length == 0, Done); + + // + // Copy all the aligned frames. + // + + size_t frames = length/m_FrameSize; + + for (size_t i = 0; i < frames; ++i) + { + InitNewFrame(buffer, m_FrameSize); + buffer += m_FrameSize; + length -= m_FrameSize; + } + + IF_TRUE_JUMP(length == 0, Done); + + // + // Copy any partial frame at the end. + // + ASSERT(m_FrameSize > length); + InitNewFrame(m_PartialFrame, m_FrameSize); + RtlCopyMemory(buffer, m_PartialFrame, length); + RtlZeroMemory(m_PartialFrame, length); + m_PartialFrameBytes = m_FrameSize - (DWORD)length; + +Done: + KeRestoreFloatingPointState(&saveData); + return; + +ZeroBuffer: + RtlZeroMemory(Buffer, BufferLength); + return; +} + +NTSTATUS ToneGenerator::Init +( + _In_ DWORD ToneFrequency, + _In_ PWAVEFORMATEXTENSIBLE WfExt +) +{ + NTSTATUS status = STATUS_SUCCESS; + KFLOATING_SAVE saveData; + + // + // This sample supports PCM 16bit formats only. + // + if ((WfExt->Format.wFormatTag != WAVE_FORMAT_PCM && + !(WfExt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && + IsEqualGUIDAligned(WfExt->SubFormat, KSDATAFORMAT_SUBTYPE_PCM))) || + (WfExt->Format.wBitsPerSample != 16 && + WfExt->Format.wBitsPerSample != 8)) + { + status = STATUS_NOT_SUPPORTED; + } + IF_FAILED_JUMP(status, Done); + + // + // Save floating state (just in case). + // + status = KeSaveFloatingPointState(&saveData); + IF_FAILED_JUMP(status, Done); + + // + // Basic init. + // + RtlZeroMemory(&m_Theta, sizeof(m_Theta)); + m_Frequency = ToneFrequency; + m_ChannelCount = WfExt->Format.nChannels; // # channels. + m_BitsPerSample = WfExt->Format.wBitsPerSample; // bits per sample. + m_SamplesPerSecond = WfExt->Format.nSamplesPerSec; // samples per sec. + m_Mute = false; + m_SampleIncrement = (m_Frequency * TWO_PI) / (double)m_SamplesPerSecond; + m_FrameSize = (DWORD)m_ChannelCount * m_BitsPerSample/8; + ASSERT(m_FrameSize == WfExt->Format.nBlockAlign); + + // + // Restore floating state. + // + KeRestoreFloatingPointState(&saveData); + + // + // Allocate a buffer to hold a partial frame. + // + m_PartialFrame = (BYTE*)ExAllocatePoolWithTag( + NonPagedPoolNx, + m_FrameSize, + SYSVAD_POOLTAG); + + IF_TRUE_ACTION_JUMP(m_PartialFrame == NULL, status = STATUS_INSUFFICIENT_RESOURCES, Done); + + status = STATUS_SUCCESS; + +Done: + return status; +} + + diff --git a/audio/sysvad/ToneGenerator.h b/audio/sysvad/ToneGenerator.h new file mode 100644 index 00000000..af4cb310 --- /dev/null +++ b/audio/sysvad/ToneGenerator.h @@ -0,0 +1,72 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + ToneGenerator.h + +Abstract: + + Declaration of SYSVAD sine wave generator. + + +--*/ +#ifndef _SYSVAD_TONEGENERATOR_H +#define _SYSVAD_TONEGENERATOR_H + +#define _USE_MATH_DEFINES +#include <math.h> +#include <limits.h> + +class ToneGenerator +{ +public: + DWORD m_Frequency; + WORD m_ChannelCount; + WORD m_BitsPerSample; + DWORD m_SamplesPerSecond; + double m_Theta; + double m_SampleIncrement; + bool m_Mute; + BYTE* m_PartialFrame; + DWORD m_PartialFrameBytes; + DWORD m_FrameSize; + +public: + ToneGenerator(); + ~ToneGenerator(); + + NTSTATUS + Init + ( + _In_ DWORD ToneFrequency, + _In_ PWAVEFORMATEXTENSIBLE WfExt + ); + + VOID + GenerateSine + ( + _Out_writes_bytes_(BufferLength) BYTE *Buffer, + _In_ size_t BufferLength + ); + + VOID + SetMute + ( + _In_ bool Value + ) + { + m_Mute = Value; + } + +private: + VOID InitNewFrame + ( + _Out_writes_bytes_(FrameSize) BYTE* Frame, + _In_ DWORD FrameSize + ); +}; + +#endif // _SYSVAD_TONEGENERATOR_H + diff --git a/audio/sysvad/UnittestData.h b/audio/sysvad/UnittestData.h new file mode 100644 index 00000000..e8a34237 --- /dev/null +++ b/audio/sysvad/UnittestData.h @@ -0,0 +1,17 @@ + +#ifndef _SYSVAD_UNITTESTDATA_H_ +#define _SYSVAD_UNITTESTDATA_H_ + +#define WAVERT_CURRENT_WRITE_POSITION_DATA 100 +#define STREAM_PRESENTATION_POSITION_DATA 1000 +#define LINEAR_BUFFER_POSITION_DATA 10000 + + +#define BUFFER_SIZE_RANGE_MIN 1200390 +#define BUFFER_SIZE_RANGE_MAX 1200393 + +#define MS_PER_SEC 1000 +#define MIN_BUFFER_DURATION_MS 10 +#define MAX_BUFFER_DURATION_MS 2000 + +#endif // _SYSVAD_UNITTESTDATA_H_
\ No newline at end of file diff --git a/audio/sysvad/adapter.cpp b/audio/sysvad/adapter.cpp new file mode 100644 index 00000000..08e83a45 --- /dev/null +++ b/audio/sysvad/adapter.cpp @@ -0,0 +1,1079 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + adapter.cpp + +Abstract: + + Setup and miniport installation. No resources are used by sysvad. + This sample is to demonstrate how to develop a full featured audio miniport driver. + +--*/ + +#pragma warning (disable : 4127) + +// +// All the GUIDS for all the miniports end up in this object. +// +#define PUT_GUIDS_HERE + +#include <sysvad.h> +#include <ContosoKeywordDetector.h> +#include "IHVPrivatePropertySet.h" + +#include "simple.h" +#include "minipairs.h" +#ifdef SYSVAD_BTH_BYPASS +#include "bthhfpminipairs.h" +#endif // SYSVAD_BTH_BYPASS + + + +typedef void (*fnPcDriverUnload) (PDRIVER_OBJECT); +fnPcDriverUnload gPCDriverUnloadRoutine = NULL; +extern "C" DRIVER_UNLOAD DriverUnload; + +#ifdef _USE_SingleComponentMultiFxStates +C_ASSERT(sizeof(POHANDLE) == sizeof(PVOID)); + +//============================================================================= +// +// The number of F-states, the transition latency and residency requirement values +// used here are for illustration purposes only. The driver should use values that +// are appropriate for its device. +// +#define SYSVAD_FSTATE_COUNT 4 + +#define SYSVAD_F0_LATENCY_IN_MS 0 +#define SYSVAD_F0_RESIDENCY_IN_SEC 0 + +#define SYSVAD_F1_LATENCY_IN_MS 200 +#define SYSVAD_F1_RESIDENCY_IN_SEC 3 + +#define SYSVAD_F2_LATENCY_IN_MS 400 +#define SYSVAD_F2_RESIDENCY_IN_SEC 6 + +#define SYSVAD_F3_LATENCY_IN_MS 800 +#define SYSVAD_F3_RESIDENCY_IN_SEC 12 + +#define SYSVAD_DEEPEST_FSTATE_LATENCY_IN_MS SYSVAD_F3_LATENCY_IN_MS +#define SYSVAD_DEEPEST_FSTATE_RESIDENCY_IN_SEC SYSVAD_F3_RESIDENCY_IN_SEC + + +//----------------------------------------------------------------------------- +// PoFx - Single Component - Multi Fx States support. +//----------------------------------------------------------------------------- + +PO_FX_COMPONENT_IDLE_STATE_CALLBACK PcPowerFxComponentIdleStateCallback; +PO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK PcPowerFxComponentActiveConditionCallback; +PO_FX_COMPONENT_IDLE_CONDITION_CALLBACK PcPowerFxComponentIdleConditionCallback; +PO_FX_POWER_CONTROL_CALLBACK PcPowerFxPowerControlCallback; +EVT_PC_POST_PO_FX_REGISTER_DEVICE PcPowerFxRegisterDevice; +EVT_PC_PRE_PO_FX_UNREGISTER_DEVICE PcPowerFxUnregisterDevice; + +#pragma code_seg("PAGE") +_Function_class_(EVT_PC_POST_PO_FX_REGISTER_DEVICE) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PcPowerFxRegisterDevice( + _In_ PVOID PoFxDeviceContext, + _In_ POHANDLE PoHandle + ) +{ + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)PoFxDeviceContext; + PortClassDeviceContext* pExtension = static_cast<PortClassDeviceContext*>(DeviceObject->DeviceExtension); + + PAGED_CODE(); + + DPF(D_VERBOSE, ("PcPowerFxRegisterDevice Context %p, PoHandle %p", PoFxDeviceContext, PoHandle)); + + pExtension->m_poHandle = PoHandle; + + // + // Set latency and residency hints so that the power framework chooses lower + // powered F-states when we are idle. + // The values used here are for illustration purposes only. The driver + // should use values that are appropriate for its device. + // + PoFxSetComponentLatency( + PoHandle, + 0, // Component + (WDF_ABS_TIMEOUT_IN_MS(SYSVAD_DEEPEST_FSTATE_LATENCY_IN_MS) + 1) + ); + PoFxSetComponentResidency( + PoHandle, + 0, // Component + (WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_DEEPEST_FSTATE_RESIDENCY_IN_SEC) + 1) + ); + + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +_Function_class_(EVT_PC_PRE_PO_FX_UNREGISTER_DEVICE) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PcPowerFxUnregisterDevice( + _In_ PVOID PoFxDeviceContext, + _In_ POHANDLE PoHandle + ) +{ + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)PoFxDeviceContext; + PortClassDeviceContext* pExtension = static_cast<PortClassDeviceContext*>(DeviceObject->DeviceExtension); + + UNREFERENCED_PARAMETER(PoHandle); + + PAGED_CODE(); + + DPF(D_VERBOSE, ("PcPowerFxUnregisterDevice Context %p, PoHandle %p", PoFxDeviceContext, PoHandle)); + + // + // Driver must not use the PoHandle after this call. + // + ASSERT(pExtension->m_poHandle == PoHandle); + pExtension->m_poHandle = NULL; +} + +#pragma code_seg() +__drv_functionClass(PO_FX_COMPONENT_IDLE_STATE_CALLBACK) +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PcPowerFxComponentIdleStateCallback( + _In_ PVOID Context, + _In_ ULONG Component, + _In_ ULONG State + ) +{ + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context; + PortClassDeviceContext* pExtension = static_cast<PortClassDeviceContext*>(DeviceObject->DeviceExtension); + + UNREFERENCED_PARAMETER(State); + + DPF(D_VERBOSE, ("PcPowerFxComponentIdleStateCallback Context %p, Component %d, State %d", + Context, Component, State)); + + PoFxCompleteIdleState(pExtension->m_poHandle, Component); +} + +#pragma code_seg() +__drv_functionClass(PO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK) +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PcPowerFxComponentActiveConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +{ + UNREFERENCED_PARAMETER(Context); + UNREFERENCED_PARAMETER(Component); + + DPF(D_VERBOSE, ("PcPowerFxComponentActiveConditionCallback Context %p, Component %d", + Context, Component)); +} + +#pragma code_seg() +__drv_functionClass(PO_FX_COMPONENT_IDLE_CONDITION_CALLBACK) +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PcPowerFxComponentIdleConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +{ + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context; + PortClassDeviceContext* pExtension = static_cast<PortClassDeviceContext*>(DeviceObject->DeviceExtension); + + DPF(D_VERBOSE, ("PcPowerFxComponentIdleConditionCallback Context %p, Component %d", + Context, Component)); + + PoFxCompleteIdleCondition(pExtension->m_poHandle, Component); +} + +#pragma code_seg() +__drv_functionClass(PO_FX_POWER_CONTROL_CALLBACK) +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PcPowerFxPowerControlCallback( + _In_ PVOID DeviceContext, + _In_ LPCGUID PowerControlCode, + _In_reads_bytes_opt_(InBufferSize) PVOID InBuffer, + _In_ SIZE_T InBufferSize, + _Out_writes_bytes_opt_(OutBufferSize) PVOID OutBuffer, + _In_ SIZE_T OutBufferSize, + _Out_opt_ PSIZE_T BytesReturned +) +{ + UNREFERENCED_PARAMETER(DeviceContext); + UNREFERENCED_PARAMETER(PowerControlCode); + UNREFERENCED_PARAMETER(InBuffer); + UNREFERENCED_PARAMETER(InBufferSize); + UNREFERENCED_PARAMETER(OutBuffer); + UNREFERENCED_PARAMETER(OutBufferSize); + UNREFERENCED_PARAMETER(BytesReturned); + + DPF(D_VERBOSE, ("PcPowerFxPowerControlCallback Context %p", DeviceContext)); + + return STATUS_SUCCESS; +} +#endif // _USE_SingleComponentMultiFxStates + +//----------------------------------------------------------------------------- +// Referenced forward. +//----------------------------------------------------------------------------- + +DRIVER_ADD_DEVICE AddDevice; + +NTSTATUS +StartDevice +( + _In_ PDEVICE_OBJECT, + _In_ PIRP, + _In_ PRESOURCELIST +); + +_Dispatch_type_(IRP_MJ_PNP) +DRIVER_DISPATCH PnpHandler; + +// +// Rendering streams are saved to a file by default. Use the registry value +// DoNotCreateDataFiles (DWORD) > 0 to override this default. +// +DWORD g_DoNotCreateDataFiles = 0; + + +#ifdef SYSVAD_BTH_BYPASS +// +// This driver listens for arrival/removal of the bth sco bypass interfaces by +// default. Use the registry value DisableBthScoBypass (DWORD) > 0 to override +// this default. +// +DWORD g_DisableBthScoBypass = 0; +#endif // SYSVAD_BTH_BYPASS + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +//============================================================================= +#pragma code_seg("PAGE") +extern "C" +void DriverUnload +( + _In_ PDRIVER_OBJECT DriverObject +) +/*++ + +Routine Description: + + Our driver unload routine. This just frees the WDF driver object. + +Arguments: + + DriverObject - pointer to the driver object + +Environment: + + PASSIVE_LEVEL + +--*/ +{ + PAGED_CODE(); + + DPF(D_TERSE, ("[DriverUnload]")); + + if (DriverObject == NULL) + { + goto Done; + } + + // + // Invoke first the port unload. + // + if (gPCDriverUnloadRoutine != NULL) + { + gPCDriverUnloadRoutine(DriverObject); + } + + // + // Unload WDF driver object. + // + if (WdfGetDriver() != NULL) + { + WdfDriverMiniportUnload(WdfGetDriver()); + } + +Done: + return; +} + +//============================================================================= +#pragma code_seg("INIT") +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +GetRegistrySettings( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Initialize Driver Framework settings from the driver + specific registry settings under + + \REGISTRY\MACHINE\SYSTEM\ControlSetxxx\Services\<driver>\Parameters + +Arguments: + + RegistryPath - Registry path passed to DriverEntry + +Returns: + + NTSTATUS - SUCCESS if able to configure the framework + +--*/ + +{ + NTSTATUS ntStatus; + UNICODE_STRING parametersPath; + RTL_QUERY_REGISTRY_TABLE paramTable[4]; + + DPF(D_TERSE, ("[GetRegistrySettings]")); + + PAGED_CODE(); + + RtlInitUnicodeString(¶metersPath, NULL); + + parametersPath.MaximumLength = + RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(WCHAR); + + parametersPath.Buffer = (PWCH) ExAllocatePoolWithTag(PagedPool, parametersPath.MaximumLength, MINADAPTER_POOLTAG); + if (parametersPath.Buffer == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(parametersPath.Buffer, parametersPath.MaximumLength); + + RtlAppendUnicodeToString(¶metersPath, RegistryPath->Buffer); + RtlAppendUnicodeToString(¶metersPath, L"\\Parameters"); + + RtlZeroMemory(¶mTable[0], sizeof(paramTable)); + + g_DoNotCreateDataFiles = 0; // default is off. + + paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[0].Name = L"DoNotCreateDataFiles"; + paramTable[0].EntryContext = &g_DoNotCreateDataFiles; + paramTable[0].DefaultType = REG_DWORD; + paramTable[0].DefaultData = &g_DoNotCreateDataFiles; + paramTable[0].DefaultLength = sizeof(ULONG); + +#ifdef SYSVAD_BTH_BYPASS + g_DisableBthScoBypass = 0; // default is off. + + paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[1].Name = L"DisableBthScoBypass"; + paramTable[1].EntryContext = &g_DisableBthScoBypass; + paramTable[1].DefaultType = REG_DWORD; + paramTable[1].DefaultData = &g_DisableBthScoBypass; + paramTable[1].DefaultLength = sizeof(ULONG); +#endif // SYSVAD_BTH_BYPASS + + + ntStatus = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + parametersPath.Buffer, + ¶mTable[0], + NULL, + NULL + ); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RtlQueryRegistryValues failed, using default values, 0x%x", ntStatus)); + // + // Don't return error because we will operate with default values. + // + } + + // + // Dump settings. + // + DPF(D_VERBOSE, ("DoNotCreateDataFiles: %u", g_DoNotCreateDataFiles)); +#ifdef SYSVAD_BTH_BYPASS + DPF(D_VERBOSE, ("DisableBthScoBypass: %u", g_DisableBthScoBypass)); +#endif // SYSVAD_BTH_BYPASS + + // + // Cleanup. + // + ExFreePool(parametersPath.Buffer); + + return STATUS_SUCCESS; +} + +#pragma code_seg("INIT") +extern "C" DRIVER_INITIALIZE DriverEntry; +extern "C" NTSTATUS +DriverEntry +( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPathName +) +{ +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + + All audio adapter drivers can use this code without change. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ + NTSTATUS ntStatus; + WDF_DRIVER_CONFIG config; + + DPF(D_TERSE, ("[DriverEntry]")); + + // + // Get registry configuration. + // + ntStatus = GetRegistrySettings(RegistryPathName); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Registry Configuration error 0x%x", ntStatus)), + Done); + + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + // + // Set WdfDriverInitNoDispatchOverride flag to tell the framework + // not to provide dispatch routines for the driver. In other words, + // the framework must not intercept IRPs that the I/O manager has + // directed to the driver. In this case, they will be handled by Audio + // port driver. + // + config.DriverInitFlags |= WdfDriverInitNoDispatchOverride; + config.DriverPoolTag = MINADAPTER_POOLTAG; + + ntStatus = WdfDriverCreate(DriverObject, + RegistryPathName, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("WdfDriverCreate failed, 0x%x", ntStatus)), + Done); + + // + // Tell the class driver to initialize the driver. + // + ntStatus = PcInitializeAdapterDriver(DriverObject, + RegistryPathName, + (PDRIVER_ADD_DEVICE)AddDevice); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("PcInitializeAdapterDriver failed, 0x%x", ntStatus)), + Done); + + // + // To intercept stop/remove/surprise-remove. + // + DriverObject->MajorFunction[IRP_MJ_PNP] = PnpHandler; + + // + // Hook the port class unload function + // + gPCDriverUnloadRoutine = DriverObject->DriverUnload; + DriverObject->DriverUnload = DriverUnload; + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + if (!NT_SUCCESS(ntStatus)) + { + if (WdfGetDriver() != NULL) + { + WdfDriverMiniportUnload(WdfGetDriver()); + } + } + + return ntStatus; +} // DriverEntry + +#pragma code_seg() +// disable prefast warning 28152 because +// DO_DEVICE_INITIALIZING is cleared in PcAddAdapterDevice +#pragma warning(disable:28152) +#pragma code_seg("PAGE") +//============================================================================= +NTSTATUS AddDevice +( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject +) +/*++ + +Routine Description: + + The Plug & Play subsystem is handing us a brand new PDO, for which we + (by means of INF registration) have been asked to provide a driver. + + We need to determine if we need to be in the driver stack for the device. + Create a function device object to attach to the stack + Initialize that device object + Return status success. + + All audio adapter drivers can use this code without change. + +Arguments: + + DriverObject - pointer to a driver object + + PhysicalDeviceObject - pointer to a device object created by the + underlying bus driver. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + ULONG maxObjects; + + DPF(D_TERSE, ("[AddDevice]")); + + maxObjects = g_MaxMiniports; + +#ifdef SYSVAD_BTH_BYPASS + // + // Allow three (3) Bluetooth hands-free profile devices. + // + maxObjects += g_MaxBthHfpMiniports * 3; +#endif // SYSVAD_BTH_BYPASS + + // Tell the class driver to add the device. + // + ntStatus = + PcAddAdapterDevice + ( + DriverObject, + PhysicalDeviceObject, + PCPFNSTARTDEVICE(StartDevice), + maxObjects, + 0 + ); + + + return ntStatus; +} // AddDevice + +#pragma code_seg() +NTSTATUS +_IRQL_requires_max_(DISPATCH_LEVEL) +PowerControlCallback +( + _In_ LPCGUID PowerControlCode, + _In_opt_ PVOID InBuffer, + _In_ SIZE_T InBufferSize, + _Out_writes_bytes_to_(OutBufferSize, *BytesReturned) PVOID OutBuffer, + _In_ SIZE_T OutBufferSize, + _Out_opt_ PSIZE_T BytesReturned, + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(PowerControlCode); + UNREFERENCED_PARAMETER(BytesReturned); + UNREFERENCED_PARAMETER(InBuffer); + UNREFERENCED_PARAMETER(OutBuffer); + UNREFERENCED_PARAMETER(OutBufferSize); + UNREFERENCED_PARAMETER(InBufferSize); + UNREFERENCED_PARAMETER(Context); + + return STATUS_NOT_IMPLEMENTED; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallEndpointRenderFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon, + _In_ PENDPOINT_MINIPAIR _pAeMiniports + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopology = NULL; + PUNKNOWN unknownWave = NULL; + PPORTCLSETWHELPER pPortClsEtwHelper = NULL; +#ifdef _USE_IPortClsRuntimePower + PPORTCLSRUNTIMEPOWER pPortClsRuntimePower = NULL; +#endif // _USE_IPortClsRuntimePower + PPORTCLSStreamResourceManager pPortClsResMgr = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_pDeviceObject); + + ntStatus = _pAdapterCommon->InstallEndpointFilters( + _pIrp, + _pAeMiniports, + NULL, + &unknownTopology, + &unknownWave); + + if (unknownWave) // IID_IPortClsEtwHelper and IID_IPortClsRuntimePower interfaces are only exposed on the WaveRT port. + { + ntStatus = unknownWave->QueryInterface (IID_IPortClsEtwHelper, (PVOID *)&pPortClsEtwHelper); + if (NT_SUCCESS(ntStatus)) + { + _pAdapterCommon->SetEtwHelper(pPortClsEtwHelper); + ASSERT(pPortClsEtwHelper != NULL); + pPortClsEtwHelper->Release(); + } + +#ifdef _USE_IPortClsRuntimePower + // Let's get the runtime power interface on PortCls. + ntStatus = unknownWave->QueryInterface(IID_IPortClsRuntimePower, (PVOID *)&pPortClsRuntimePower); + if (NT_SUCCESS(ntStatus)) + { + // This interface would typically be stashed away for later use. Instead, + // let's just send an empty control with GUID_NULL. + NTSTATUS ntStatusTest = + pPortClsRuntimePower->SendPowerControl + ( + _pDeviceObject, + &GUID_NULL, + NULL, + 0, + NULL, + 0, + NULL + ); + + if (NT_SUCCESS(ntStatusTest) || STATUS_NOT_IMPLEMENTED == ntStatusTest || STATUS_NOT_SUPPORTED == ntStatusTest) + { + ntStatus = pPortClsRuntimePower->RegisterPowerControlCallback(_pDeviceObject, &PowerControlCallback, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pPortClsRuntimePower->UnregisterPowerControlCallback(_pDeviceObject); + } + } + else + { + ntStatus = ntStatusTest; + } + + pPortClsRuntimePower->Release(); + } +#endif // _USE_IPortClsRuntimePower + + // + // Test: add and remove current thread as streaming audio resource. + // In a real driver you should only add interrupts and driver-owned threads + // (i.e., do not add the current thread as streaming resource). + // + ntStatus = unknownWave->QueryInterface(IID_IPortClsStreamResourceManager, (PVOID *)&pPortClsResMgr); + if (NT_SUCCESS(ntStatus)) + { + PCSTREAMRESOURCE_DESCRIPTOR res; + PCSTREAMRESOURCE hRes = NULL; + PDEVICE_OBJECT pdo = NULL; + + PcGetPhysicalDeviceObject(_pDeviceObject, &pdo); + PCSTREAMRESOURCE_DESCRIPTOR_INIT(&res); + res.Pdo = pdo; + res.Type = ePcStreamResourceThread; + res.Resource.Thread = PsGetCurrentThread(); + + NTSTATUS ntStatusTest = pPortClsResMgr->AddStreamResource(NULL, &res, &hRes); + if (NT_SUCCESS(ntStatusTest)) + { + pPortClsResMgr->RemoveStreamResource(hRes); + hRes = NULL; + } + + pPortClsResMgr->Release(); + pPortClsResMgr = NULL; + } + } + + SAFE_RELEASE(unknownTopology); + SAFE_RELEASE(unknownWave); + + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallAllRenderFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon + ) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR* ppAeMiniports = g_RenderEndpoints; + + PAGED_CODE(); + + for(ULONG i = 0; i < g_cRenderEndpoints; ++i, ++ppAeMiniports) + { + ntStatus = InstallEndpointRenderFilters(_pDeviceObject, _pIrp, _pAdapterCommon, *ppAeMiniports); + IF_FAILED_JUMP(ntStatus, Exit); + } + + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallEndpointCaptureFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon, + _In_ PENDPOINT_MINIPAIR _pAeMiniports + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_pDeviceObject); + + ntStatus = _pAdapterCommon->InstallEndpointFilters( + _pIrp, + _pAeMiniports, + NULL, + NULL, + NULL); + + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallAllCaptureFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon + ) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR* ppAeMiniports = g_CaptureEndpoints; + + PAGED_CODE(); + + for(ULONG i = 0; i < g_cCaptureEndpoints; ++i, ++ppAeMiniports) + { + ntStatus = InstallEndpointCaptureFilters(_pDeviceObject, _pIrp, _pAdapterCommon, *ppAeMiniports); + IF_FAILED_JUMP(ntStatus, Exit); + } + + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} + +#ifdef _USE_SingleComponentMultiFxStates +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UseSingleComponentMultiFxStates( + _In_ PDEVICE_OBJECT DeviceObject + +) +{ + NTSTATUS status; + PC_POWER_FRAMEWORK_SETTINGS poFxSettings; + PO_FX_COMPONENT component; + PO_FX_COMPONENT_IDLE_STATE idleStates[SYSVAD_FSTATE_COUNT]; + + // + // Note that we initialize the 'idleStates' array below based on the + // assumption that SYSVAD_FSTATE_COUNT is 4. + // If we increase the value of SYSVAD_FSTATE_COUNT, we need to initialize those + // additional F-states below. If we decrease the value of SYSVAD_FSTATE_COUNT, + // we need to remove the corresponding initializations below. + // + C_ASSERT(SYSVAD_FSTATE_COUNT == 4); + + PAGED_CODE(); + + // + // Initialization + // + RtlZeroMemory(&component, sizeof(component)); + RtlZeroMemory(idleStates, sizeof(idleStates)); + + // + // F0 + // + idleStates[0].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(SYSVAD_F0_LATENCY_IN_MS); + idleStates[0].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_F0_RESIDENCY_IN_SEC); + idleStates[0].NominalPower = 0; + + // + // F1 + // + idleStates[1].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(SYSVAD_F1_LATENCY_IN_MS); + idleStates[1].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_F1_RESIDENCY_IN_SEC); + idleStates[1].NominalPower = 0; + + // + // F2 + // + idleStates[2].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(SYSVAD_F2_LATENCY_IN_MS); + idleStates[2].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_F2_RESIDENCY_IN_SEC); + idleStates[2].NominalPower = 0; + + // + // F3 + // + idleStates[3].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(SYSVAD_F3_LATENCY_IN_MS); + idleStates[3].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_F3_RESIDENCY_IN_SEC); + idleStates[3].NominalPower = 0; + + // + // Component 0 (the only component) + // + component.IdleStateCount = SYSVAD_FSTATE_COUNT; + component.IdleStates = idleStates; + + PC_POWER_FRAMEWORK_SETTINGS_INIT(&poFxSettings); + + poFxSettings.EvtPcPostPoFxRegisterDevice = PcPowerFxRegisterDevice; + poFxSettings.EvtPcPrePoFxUnregisterDevice = PcPowerFxUnregisterDevice; + poFxSettings.ComponentIdleStateCallback = PcPowerFxComponentIdleStateCallback; + poFxSettings.ComponentActiveConditionCallback = PcPowerFxComponentActiveConditionCallback; + poFxSettings.ComponentIdleConditionCallback = PcPowerFxComponentIdleConditionCallback; + poFxSettings.PowerControlCallback = PcPowerFxPowerControlCallback; + + poFxSettings.Component = &component; + poFxSettings.PoFxDeviceContext = (PVOID) DeviceObject; + + status = PcAssignPowerFrameworkSettings(DeviceObject, &poFxSettings); + if (!NT_SUCCESS(status)) { + DPF(D_ERROR, ("PcAssignPowerFrameworkSettings failed with status 0x%x", status)); + } + + return status; +} +#endif // _USE_SingleComponentMultiFxStates + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +StartDevice +( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_ PRESOURCELIST ResourceList +) +{ +/*++ + +Routine Description: + + This function is called by the operating system when the device is + started. + It is responsible for starting the miniports. This code is specific to + the adapter because it calls out miniports for functions that are specific + to the adapter. + +Arguments: + + DeviceObject - pointer to the driver object + + Irp - pointer to the irp + + ResourceList - pointer to the resource list assigned by PnP manager + +Return Value: + + NT status code. + +--*/ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(Irp); + ASSERT(ResourceList); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + PADAPTERCOMMON pAdapterCommon = NULL; + PUNKNOWN pUnknownCommon = NULL; + PortClassDeviceContext* pExtension = static_cast<PortClassDeviceContext*>(DeviceObject->DeviceExtension); + + DPF_ENTER(("[StartDevice]")); + + // + // create a new adapter common object + // + ntStatus = NewAdapterCommon( + &pUnknownCommon, + IID_IAdapterCommon, + NULL, + NonPagedPoolNx + ); + IF_FAILED_JUMP(ntStatus, Exit); + + ntStatus = pUnknownCommon->QueryInterface( IID_IAdapterCommon,(PVOID *) &pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + + ntStatus = pAdapterCommon->Init(DeviceObject); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // register with PortCls for power-management services + ntStatus = PcRegisterAdapterPowerManagement( PUNKNOWN(pAdapterCommon), DeviceObject); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // Install wave+topology filters for render devices + // + ntStatus = InstallAllRenderFilters(DeviceObject, Irp, pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // Install wave+topology filters for capture devices + // + ntStatus = InstallAllCaptureFilters(DeviceObject, Irp, pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + +#ifdef SYSVAD_BTH_BYPASS + if (!g_DisableBthScoBypass) + { + // + // Init infrastructure for Bluetooth HFP - SCO Bypass devices. + // + ntStatus = pAdapterCommon->InitBthScoBypass(); + IF_FAILED_JUMP(ntStatus, Exit); + } +#endif // SYSVAD_BTH_BYPASS + +#ifdef _USE_SingleComponentMultiFxStates + // + // Init single component - multi Fx states + // + ntStatus = UseSingleComponentMultiFxStates(DeviceObject); + IF_FAILED_JUMP(ntStatus, Exit); +#endif // _USE_SingleComponentMultiFxStates + +Exit: + + // + // Stash the adapter common object in the device extension so + // we can access it for cleanup on stop/removal. + // + if (pAdapterCommon) + { + ASSERT(pExtension != NULL); + pExtension->m_pCommon = pAdapterCommon; + } + + // + // Release the adapter IUnknown interface. + // + SAFE_RELEASE(pUnknownCommon); + + return ntStatus; +} // StartDevice + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PnpHandler +( + _In_ DEVICE_OBJECT *_DeviceObject, + _Inout_ IRP *_Irp +) +/*++ + +Routine Description: + + Handles PnP IRPs + +Arguments: + + _DeviceObject - Functional Device object pointer. + + _Irp - The Irp being passed + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + IO_STACK_LOCATION *stack; + PortClassDeviceContext *ext; + + PAGED_CODE(); + + ASSERT(_DeviceObject); + ASSERT(_Irp); + + // + // Check for the REMOVE_DEVICE irp. If we're being unloaded, + // uninstantiate our devices and release the adapter common + // object. + // + stack = IoGetCurrentIrpStackLocation(_Irp); + + + if ((IRP_MN_REMOVE_DEVICE == stack->MinorFunction) || + (IRP_MN_SURPRISE_REMOVAL == stack->MinorFunction) || + (IRP_MN_STOP_DEVICE == stack->MinorFunction)) + { + ext = static_cast<PortClassDeviceContext*>(_DeviceObject->DeviceExtension); + + if (ext->m_pCommon != NULL) + { + ext->m_pCommon->Cleanup(); + + ext->m_pCommon->Release(); + ext->m_pCommon = NULL; + } + } + + ntStatus = PcDispatchIrp(_DeviceObject, _Irp); + + return ntStatus; +} + +#pragma code_seg() + diff --git a/audio/sysvad/basetopo.cpp b/audio/sysvad/basetopo.cpp new file mode 100644 index 00000000..1ceb414a --- /dev/null +++ b/audio/sysvad/basetopo.cpp @@ -0,0 +1,691 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + basetopo.cpp + +Abstract: + + Implementation of topology miniport. This the base class for + all SYSVAD samples + +--*/ + +//4127: conditional expression is constant +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "basetopo.h" + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportTopologySYSVAD::CMiniportTopologySYSVAD +( + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels +) +/*++ + +Routine Description: + + Topology miniport constructor + +Arguments: + + FilterDesc - + + DeviceMaxChannels - + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + m_AdapterCommon = NULL; + + ASSERT(FilterDesc != NULL); + m_FilterDescriptor = FilterDesc; + m_PortEvents = NULL; + + ASSERT(DeviceMaxChannels > 0); + m_DeviceMaxChannels = DeviceMaxChannels; +} // CMiniportTopologySYSVAD + +CMiniportTopologySYSVAD::~CMiniportTopologySYSVAD +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + SAFE_RELEASE(m_AdapterCommon); + SAFE_RELEASE(m_PortEvents); +} // ~CMiniportTopologySYSVAD + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest + quality intersection of two data ranges. Topology miniport does nothing. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request + + MyDataRange - Pin's data range to be compared with client's data range + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter + + ResultantFormat - Pointer to value where the resultant format should be + returned + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(PinId); + UNREFERENCED_PARAMETER(ClientDataRange); + UNREFERENCED_PARAMETER(MyDataRange); + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(ResultantFormat); + UNREFERENCED_PARAMETER(ResultantFormatLength); + + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + return (STATUS_NOT_IMPLEMENTED); +} // DataRangeIntersection + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + *OutFilterDescriptor = m_FilterDescriptor; + + return (STATUS_SUCCESS); +} // GetDescription + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::Init +( + _In_ PUNKNOWN UnknownAdapter_, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + Initializes the topology miniport. + +Arguments: + + UnknownAdapter - + + Port_ - Pointer to topology port + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(UnknownAdapter_); + ASSERT(Port_); + + DPF_ENTER(("[CMiniportTopologySYSVAD::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + UnknownAdapter_->QueryInterface( + IID_IAdapterCommon, + (PVOID *) &m_AdapterCommon); + + if (NT_SUCCESS(ntStatus)) + { + // + // Get the port event interface. + // + ntStatus = Port_->QueryInterface( + IID_IPortEvents, + (PVOID *)&m_PortEvents); + } + + if (NT_SUCCESS(ntStatus)) + { + m_AdapterCommon->MixerReset(); + } + + if (!NT_SUCCESS(ntStatus)) + { + // clean up AdapterCommon + SAFE_RELEASE(m_AdapterCommon); + SAFE_RELEASE(m_PortEvents); + } + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::PropertyHandlerGeneric +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles all properties for this miniport. + +Arguments: + + PropertyRequest - property request structure + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_AUDIO_VOLUMELEVEL: + ntStatus = PropertyHandler_Volume( + m_AdapterCommon, + PropertyRequest, + m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_MUTE: + ntStatus = PropertyHandler_Mute( + m_AdapterCommon, + PropertyRequest, + m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_PEAKMETER2: + ntStatus = PropertyHandler_PeakMeter2( + m_AdapterCommon, + PropertyRequest, + m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_CPU_RESOURCES: + ntStatus = PropertyHandler_CpuResources(PropertyRequest); + break; + + case KSPROPERTY_AUDIO_MUX_SOURCE: + ntStatus = PropertyHandlerMuxSource(PropertyRequest); + break; + + case KSPROPERTY_AUDIO_DEV_SPECIFIC: + ntStatus = PropertyHandlerDevSpecific(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandlerGeneric: Invalid Device Request]")); + } + + return ntStatus; +} // PropertyHandlerGeneric + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::PropertyHandlerMuxSource +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + PropertyHandler for KSPROPERTY_AUDIO_MUX_SOURCE. + +Arguments: + + PropertyRequest - property request structure + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // Validate node + // This property is only valid for WAVEIN_MUX node. + // + // TODO if (WAVEIN_MUX == PropertyRequest->Node) + { + if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + PULONG pulMuxValue = PULONG(PropertyRequest->Value); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *pulMuxValue = m_AdapterCommon->MixerMuxRead(); + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + m_AdapterCommon->MixerMuxWrite(*pulMuxValue); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_ALL, + VT_I4 + ); + } + } + else + { + DPF(D_TERSE, ("[PropertyHandlerMuxSource - Invalid parameter]")); + ntStatus = STATUS_INVALID_PARAMETER; + } + } + + return ntStatus; +} // PropertyHandlerMuxSource + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::PropertyHandlerDevSpecific +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_DEV_SPECIFIC + +Arguments: + + PropertyRequest - property request structure + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus=STATUS_SUCCESS; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + if( DEV_SPECIFIC_VT_BOOL == PropertyRequest->Node ) + { + ntStatus = PropertyHandler_BasicSupport(PropertyRequest,KSPROPERTY_TYPE_ALL,VT_BOOL); + } + else + { + ULONG ExpectedSize = sizeof( KSPROPERTY_DESCRIPTION ) + + sizeof( KSPROPERTY_MEMBERSHEADER ) + + sizeof( KSPROPERTY_BOUNDS_LONG ); + DWORD ulPropTypeSetId; + + if( DEV_SPECIFIC_VT_I4 == PropertyRequest->Node ) + { + ulPropTypeSetId = VT_I4; + } + else if ( DEV_SPECIFIC_VT_UI4 == PropertyRequest->Node ) + { + ulPropTypeSetId = VT_UI4; + } + else + { + ulPropTypeSetId = VT_ILLEGAL; + ntStatus = STATUS_INVALID_PARAMETER; + } + + if( NT_SUCCESS(ntStatus)) + { + if ( !PropertyRequest->ValueSize ) + { + PropertyRequest->ValueSize = ExpectedSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + PropDesc->DescriptionSize = ExpectedSize; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = ulPropTypeSetId; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 0; + PropDesc->Reserved = 0; + + if ( PropertyRequest->ValueSize >= ExpectedSize ) + { + // Extra information to return + PropDesc->MembersListCount = 1; + + PKSPROPERTY_MEMBERSHEADER MembersHeader = ( PKSPROPERTY_MEMBERSHEADER )( PropDesc + 1); + MembersHeader->MembersFlags = KSPROPERTY_MEMBER_RANGES; + MembersHeader->MembersCount = 1; + MembersHeader->MembersSize = sizeof( KSPROPERTY_BOUNDS_LONG ); + MembersHeader->Flags = 0; + + PKSPROPERTY_BOUNDS_LONG PeakMeterBounds = (PKSPROPERTY_BOUNDS_LONG)( MembersHeader + 1); + if(VT_I4 == ulPropTypeSetId ) + { + PeakMeterBounds->SignedMinimum = 0; + PeakMeterBounds->SignedMaximum = 0x7fffffff; + } + else + { + PeakMeterBounds->UnsignedMinimum = 0; + PeakMeterBounds->UnsignedMaximum = 0xffffffff; + } + + // set the return value size + PropertyRequest->ValueSize = ExpectedSize; + } + else + { + // No extra information to return. + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + // + *(PULONG(PropertyRequest->Value)) = KSPROPERTY_TYPE_ALL; + + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + } + } + else + { + // switch on node id + switch( PropertyRequest->Node ) + { + case DEV_SPECIFIC_VT_BOOL: + { + PBOOL pbDevSpecific; + + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(BOOL), 0); + + if (NT_SUCCESS(ntStatus)) + { + pbDevSpecific = PBOOL (PropertyRequest->Value); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *pbDevSpecific = m_AdapterCommon->bDevSpecificRead(); + PropertyRequest->ValueSize = sizeof(BOOL); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + m_AdapterCommon->bDevSpecificWrite(*pbDevSpecific); + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + break; + case DEV_SPECIFIC_VT_I4: + { + INT* piDevSpecific; + + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(int), 0); + + if (NT_SUCCESS(ntStatus)) + { + piDevSpecific = PINT (PropertyRequest->Value); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *piDevSpecific = m_AdapterCommon->iDevSpecificRead(); + PropertyRequest->ValueSize = sizeof(int); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + m_AdapterCommon->iDevSpecificWrite(*piDevSpecific); + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + break; + case DEV_SPECIFIC_VT_UI4: + { + UINT* puiDevSpecific; + + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(UINT), 0); + + if (NT_SUCCESS(ntStatus)) + { + puiDevSpecific = PUINT (PropertyRequest->Value); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *puiDevSpecific = m_AdapterCommon->uiDevSpecificRead(); + PropertyRequest->ValueSize = sizeof(UINT); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + m_AdapterCommon->uiDevSpecificWrite(*puiDevSpecific); + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + break; + default: + ntStatus = STATUS_INVALID_PARAMETER; + break; + } + + + if( !NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerDevSpecific + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CMiniportTopologySYSVAD::AddEventToEventList +( + _In_ PKSEVENT_ENTRY EventEntry +) +/*++ + +Routine Description: + + The AddEventToEventList method adds an event to the port driver's event list + +Arguments: + + EventEntry - + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CMiniportTopology::AddEventToEventList]")); + + ASSERT(m_PortEvents != NULL); + + m_PortEvents->AddEventToEventList(EventEntry); +} + +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopologySYSVAD::GenerateEventList +( + _In_opt_ GUID *Set, + _In_ ULONG EventId, + _In_ BOOL PinEvent, + _In_ ULONG PinId, + _In_ BOOL NodeEvent, + _In_ ULONG NodeId +) +/*++ + +Routine Description: + + The GenerateEventList method notifies clients through the port driver's list + of event entries that a particular event has occurred. + +Arguments: + + Set - + + EventId - + + PinEvent - + + PinId - + + NodeEvent - + + NodeId - + +--*/ +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::GenerateEventList]")); + + ASSERT(m_PortEvents != NULL); + + m_PortEvents->GenerateEventList( + Set, + EventId, + PinEvent, + PinId, + NodeEvent, + NodeId); +} + +#pragma code_seg() + diff --git a/audio/sysvad/basetopo.h b/audio/sysvad/basetopo.h new file mode 100644 index 00000000..429c05eb --- /dev/null +++ b/audio/sysvad/basetopo.h @@ -0,0 +1,98 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + basetopo.h + +Abstract: + + Declaration of topology miniport. + +--*/ + +#ifndef _SYSVAD_BASETOPO_H_ +#define _SYSVAD_BASETOPO_H_ + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMiniportTopologySYSVAD +// + +class CMiniportTopologySYSVAD +{ + protected: + PADAPTERCOMMON m_AdapterCommon; // Adapter common object. + PPCFILTER_DESCRIPTOR m_FilterDescriptor; // Filter descriptor. + PPORTEVENTS m_PortEvents; // Event interface. + USHORT m_DeviceMaxChannels; // Max device channels. + + public: + CMiniportTopologySYSVAD( + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ); + + ~CMiniportTopologySYSVAD(); + + NTSTATUS GetDescription + ( + _Out_ PPCFILTER_DESCRIPTOR * Description + ); + + NTSTATUS DataRangeIntersection + ( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength + ); + + NTSTATUS Init + ( + _In_ PUNKNOWN UnknownAdapter, + _In_ PPORTTOPOLOGY Port_ + ); + + // PropertyHandlers. + NTSTATUS PropertyHandlerGeneric + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerMuxSource + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerDevSpecific + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + VOID AddEventToEventList + ( + _In_ PKSEVENT_ENTRY EventEntry + ); + + VOID GenerateEventList + ( + _In_opt_ GUID *Set, + _In_ ULONG EventId, + _In_ BOOL PinEvent, + _In_ ULONG PinId, + _In_ BOOL NodeEvent, + _In_ ULONG NodeId + ); +}; + +#endif + diff --git a/audio/sysvad/common.cpp b/audio/sysvad/common.cpp new file mode 100644 index 00000000..dae94f3d --- /dev/null +++ b/audio/sysvad/common.cpp @@ -0,0 +1,6376 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + common.cpp + +Abstract: + + Implementation of the AdapterCommon class. + +--*/ + +#pragma warning (disable : 4127) + +#include <initguid.h> +#include <sysvad.h> +#include "hw.h" +#include "savedata.h" +#include "IHVPrivatePropertySet.h" +#include "simple.h" + +#ifdef SYSVAD_BTH_BYPASS +#include <limits.h> +#include <bthhfpddi.h> +#include <wdmguid.h> // guild-arrival/removal +#include <devpkey.h> +#include "bthhfpminipairs.h" + +// # of sec before sync request is cancelled. +#define BTH_HFP_SYNC_REQ_TIMEOUT_IN_SEC 60 +#define BTH_HFP_NOTIFICATION_MAX_ERROR_COUNT 5 + +#endif // SYSVAD_BTH_BYPASS + +//----------------------------------------------------------------------------- +// CSaveData statics +//----------------------------------------------------------------------------- + +PSAVEWORKER_PARAM CSaveData::m_pWorkItems = NULL; +PDEVICE_OBJECT CSaveData::m_pDeviceObject = NULL; + +//============================================================================= +// Classes +//============================================================================= +#ifdef SYSVAD_BTH_BYPASS +class BthHfpDevice; // Forward declaration. +#endif // SYSVAD_BTH_BYPASS + +/////////////////////////////////////////////////////////////////////////////// +// CAdapterCommon +// +class CAdapterCommon : + public IAdapterCommon, + public IAdapterPowerManagement, + public CUnknown +{ + private: + PSERVICEGROUP m_pServiceGroupWave; + PDEVICE_OBJECT m_pDeviceObject; + PDEVICE_OBJECT m_pPhysicalDeviceObject; + WDFDEVICE m_WdfDevice; // Wdf device. + DEVICE_POWER_STATE m_PowerState; + + PCSYSVADHW m_pHW; // Virtual SYSVAD HW object + PPORTCLSETWHELPER m_pPortClsEtwHelper; + + static LONG m_AdapterInstances; // # of adapter objects. + + DWORD m_dwIdleRequests; + + public: + //===================================================================== + // Default CUnknown + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(CAdapterCommon); + ~CAdapterCommon(); + + //===================================================================== + // Default IAdapterPowerManagement + IMP_IAdapterPowerManagement; + + //===================================================================== + // IAdapterCommon methods + + STDMETHODIMP_(NTSTATUS) Init + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + + STDMETHODIMP_(PDEVICE_OBJECT) GetDeviceObject(void); + + STDMETHODIMP_(PDEVICE_OBJECT) GetPhysicalDeviceObject(void); + + STDMETHODIMP_(WDFDEVICE) GetWdfDevice(void); + + STDMETHODIMP_(void) SetWaveServiceGroup + ( + _In_ PSERVICEGROUP ServiceGroup + ); + + STDMETHODIMP_(BOOL) bDevSpecificRead(); + + STDMETHODIMP_(void) bDevSpecificWrite + ( + _In_ BOOL bDevSpecific + ); + STDMETHODIMP_(INT) iDevSpecificRead(); + + STDMETHODIMP_(void) iDevSpecificWrite + ( + _In_ INT iDevSpecific + ); + STDMETHODIMP_(UINT) uiDevSpecificRead(); + + STDMETHODIMP_(void) uiDevSpecificWrite + ( + _In_ UINT uiDevSpecific + ); + + STDMETHODIMP_(BOOL) MixerMuteRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(void) MixerMuteWrite + ( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value + ); + + STDMETHODIMP_(ULONG) MixerMuxRead(void); + + STDMETHODIMP_(void) MixerMuxWrite + ( + _In_ ULONG Index + ); + + STDMETHODIMP_(void) MixerReset(void); + + STDMETHODIMP_(LONG) MixerVolumeRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(void) MixerVolumeWrite + ( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value + ); + + STDMETHODIMP_(LONG) MixerPeakMeterRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(NTSTATUS) WriteEtwEvent + ( + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 + ); + + STDMETHODIMP_(VOID) SetEtwHelper + ( + PPORTCLSETWHELPER _pPortClsEtwHelper + ); + + STDMETHODIMP_(NTSTATUS) InstallSubdevice + ( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY * pProperties, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PRESOURCELIST ResourceList, + _In_ REFGUID PortInterfaceId, + _Out_opt_ PUNKNOWN * OutPortInterface, + _Out_opt_ PUNKNOWN * OutPortUnknown, + _Out_opt_ PUNKNOWN * OutMiniportUnknown + ); + + STDMETHODIMP_(NTSTATUS) UnregisterSubdevice + ( + _In_opt_ PUNKNOWN UnknownPort + ); + + STDMETHODIMP_(NTSTATUS) ConnectTopologies + ( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHODIMP_(NTSTATUS) DisconnectTopologies + ( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHODIMP_(NTSTATUS) InstallEndpointFilters + ( + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave + ); + + STDMETHODIMP_(NTSTATUS) RemoveEndpointFilters + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave + ); + + STDMETHODIMP_(NTSTATUS) GetFilters + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN *UnknownTopologyPort, + _Out_opt_ PUNKNOWN *UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN *UnknownWavePort, + _Out_opt_ PUNKNOWN *UnknownWaveMiniport + ); + + STDMETHODIMP_(NTSTATUS) SetIdlePowerManagement + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnabled + ); + +#ifdef SYSVAD_BTH_BYPASS + STDMETHODIMP_(NTSTATUS) InitBthScoBypass(); + + STDMETHODIMP_(VOID) CleanupBthScoBypass(); +#endif // SYSVAD_BTH_BYPASS + + STDMETHODIMP_(VOID) Cleanup(); + + //===================================================================== + // friends + friend NTSTATUS NewAdapterCommon + ( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType + ); + +#ifdef SYSVAD_BTH_BYPASS + //===================================================================== + // Bluetooth Hands-free Profile SCO Bypass support. + + private: + PVOID m_BthHfpScoNotificationHandle; + FAST_MUTEX m_BthHfpFastMutex; // To serialize access. + WDFWORKITEM m_BthHfpWorkItem; // Async work-item. + LIST_ENTRY m_BthHfpWorkTasks; // Work-item's tasks. + LIST_ENTRY m_BthHfpDevices; // Bth HFP devices. + NPAGED_LOOKASIDE_LIST m_BhtHfpWorkTaskPool; // LookasideList + size_t m_BhtHfpWorkTaskPoolElementSize; + BOOL m_BthHfpEnableCleanup; // Do cleanup if true. + + private: + static + DRIVER_NOTIFICATION_CALLBACK_ROUTINE EvtBthHfpScoBypassInterfaceChange; + + static + EVT_WDF_WORKITEM EvtBthHfpScoBypassInterfaceWorkItem; + + protected: + BthHfpDevice * BthHfpDeviceFind + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); + + NTSTATUS BthHfpScoInterfaceArrival + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); + + NTSTATUS BthHfpScoInterfaceRemoval + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); +#endif // SYSVAD_BTH_BYPASS + + private: + + LIST_ENTRY m_SubdeviceCache; + + NTSTATUS GetCachedSubdevice + ( + _In_ PWSTR Name, + _Out_opt_ PUNKNOWN *OutUnknownPort, + _Out_opt_ PUNKNOWN *OutUnknownMiniport + ); + + NTSTATUS CacheSubdevice + ( + _In_ PWSTR Name, + _In_ PUNKNOWN UnknownPort, + _In_ PUNKNOWN UnknownMiniport + ); + + NTSTATUS RemoveCachedSubdevice + ( + _In_ PWSTR Name + ); + + VOID EmptySubdeviceCache(); + + NTSTATUS CreateAudioInterfaceWithProperties + ( + _In_ PCWSTR ReferenceString, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties, + _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName + ); +}; + +typedef struct _MINIPAIR_UNKNOWN +{ + LIST_ENTRY ListEntry; + WCHAR Name[MAX_PATH]; + PUNKNOWN PortInterface; + PUNKNOWN MiniportInterface; + PADAPTERPOWERMANAGEMENT PowerInterface; +} MINIPAIR_UNKNOWN; + +// +// Used to implement the singleton pattern. +// +LONG CAdapterCommon::m_AdapterInstances = 0; + + +#ifdef SYSVAD_BTH_BYPASS + +//===================================================================== +// +// CAdapterCommon: Bluetooth Hands-Free Profile SCO Bypass definitions. +// + +struct BthHfpWorkItemContext +{ + CAdapterCommon * Adapter; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpWorkItemContext, + GetBthHfpWorkItemContext +) + +// start/stop the device +enum eBthHfpTaskAction +{ + eBthHfpTaskStart = 1, + eBthHfpTaskStop = 2, +}; + +struct BthHfpWorkTask +{ + LIST_ENTRY ListEntry; + BthHfpDevice * Device; + eBthHfpTaskAction Action; +}; + + +//===================================================================== +// +// Device: Bluetooth Hands-Free Profile SCO Bypass definitions. +// + +// BTH HFP device's notification work-item context. +struct BthHfpDeviceNotificationWorkItemContext +{ + BthHfpDevice * BthHfpDevice; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpDeviceNotificationWorkItemContext, + GetBthHfpDeviceNotificationWorkItemContext +) + +// BTH HFP device's notification request context. +union BthHfpDeviceNotificationBuffer +{ + BOOL bImmediate; + LONG Volume; + BOOL BoolStatus; + NTSTATUS NtStatus; +}; + +struct BthHfpDeviceNotificationReqContext +{ + BthHfpDevice * BthHfpDevice; + LONG Errors; + BthHfpDeviceNotificationBuffer Buffer; + WDFMEMORY MemIn; + WDFMEMORY MemOut; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpDeviceNotificationReqContext, + GetBthHfpDeviceNotificationReqContext +) + +enum eBthHfpState +{ + eBthHfpStateInvalid = 0, + eBthHfpStateInitializing = 1, + eBthHfpStateRunning = 2, + eBthHfpStateStopping = 3, + eBthHfpStateStopped = 4, + eBthHfpStateFailed = 5, +}; + +// To support event notification. +struct BthHfpEventCallback +{ + PFNEVENTNOTIFICATION Handler; + PVOID Context; +}; + +// +// This class represents a the Bluetooth Hands-Free Profile SCO Bypass device. +// There is one class for each interface (id = symbolic-link-name). +// +class BthHfpDevice : + IBthHfpDeviceCommon, + public CUnknown +{ + private: + eBthHfpState m_State; + + CAdapterCommon * m_Adapter; + WDFIOTARGET m_WdfIoTarget; + + LIST_ENTRY m_ListEntry; + UNICODE_STRING m_SymbolicLinkName; + + // + // The Topo Filter Desc and Topo Pins structures referenced from + // the Miniports structure are deep copies: they start as copies of the + // static structures and are modified to allow per-SCO-Device pin + // categories based on the info obtained from + // IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2 + // + PENDPOINT_MINIPAIR m_SpeakerMiniports; + PENDPOINT_MINIPAIR m_MicMiniports; + + PUNKNOWN m_UnknownSpeakerTopology; + PUNKNOWN m_UnknownSpeakerWave; + PUNKNOWN m_UnknownMicTopology; + PUNKNOWN m_UnknownMicWave; + + PBTHHFP_DESCRIPTOR2 m_Descriptor; + PKSPROPERTY_VALUES m_VolumePropValues; + LONG m_SpeakerVolumeLevel; + LONG m_MicVolumeLevel; + union{ + BOOL m_ConnectionStatus; + LONG m_ConnectionStatusLong; + }; // unnamed. + + union{ + NTSTATUS m_StreamStatus; + LONG m_StreamStatusLong; + }; // unnamed. + + KEVENT m_StreamStatusEvent; + + // + // Set to TRUE when the HF (remote device) wants to disable the + // NR + EC of the AG (local system). + // + LONG m_NRECDisableStatusLong; + + WDFREQUEST m_StreamReq; + WDFREQUEST m_SpeakerVolumeReq; + WDFREQUEST m_MicVolumeReq; + WDFREQUEST m_ConnectionReq; + WDFREQUEST m_NRECDisableStatusReq; + WDFWORKITEM m_WorkItem; + WDFCOLLECTION m_ReqCollection; + KSPIN_LOCK m_Lock; + + LONG m_nStreams; // # of open streams. + + BthHfpEventCallback m_SpeakerVolumeCallback; + BthHfpEventCallback m_SpeakerConnectionStatusCallback; + BthHfpEventCallback m_MicVolumeCallback; + BthHfpEventCallback m_MicConnectionStatusCallback; + + public: + //===================================================================== + // Default CUnknown + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(BthHfpDevice); + ~BthHfpDevice(); + + NTSTATUS Init + ( + _In_ CAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName + ); + + public: + //===================================================================== + // + // Public functions used by CAdapterCommon object. + // + VOID Start(); + VOID Stop(); + + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + + PUNICODE_STRING GetSymbolicLinkName() + { + return &m_SymbolicLinkName; + } + + static + BthHfpDevice * + GetBthHfpDevice + ( + _In_ PLIST_ENTRY le + ) + { + return CONTAINING_RECORD(le, BthHfpDevice, m_ListEntry); + } + + public: + //===================================================================== + // + // IBthHfpDeviceCommon functions. + // + STDMETHODIMP_(BOOL) IsVolumeSupported(); + + STDMETHODIMP_(PKSPROPERTY_VALUES) GetVolumeSettings + ( + _Out_ PULONG Size + ); + + STDMETHODIMP_(LONG) GetSpeakerVolume(); + + STDMETHODIMP_(NTSTATUS) SetSpeakerVolume + ( + _In_ ULONG Volume + ); + + STDMETHODIMP_(LONG) GetMicVolume(); + + STDMETHODIMP_(NTSTATUS) SetMicVolume + ( + _In_ ULONG Volume + ); + + STDMETHODIMP_(BOOL) GetConnectionStatus(); + + STDMETHODIMP_(NTSTATUS) Connect(); + + STDMETHODIMP_(NTSTATUS) Disconnect(); + + STDMETHODIMP_(BOOL) GetStreamStatus(); + + STDMETHODIMP_(NTSTATUS) StreamOpen(); + + STDMETHODIMP_(NTSTATUS) StreamClose(); + + STDMETHODIMP_(GUID) GetContainerId(); + + STDMETHODIMP_(VOID) SetSpeakerVolumeHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetSpeakerConnectionStatusHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetMicVolumeHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetMicConnectionStatusHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(BOOL) IsNRECSupported(); + + STDMETHODIMP_(BOOL) GetNRECDisableStatus(); + + private: + //===================================================================== + // + // Helper functions. + // + NTSTATUS SendIoCtrlSynchronously + ( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0 || OutLength > 0, _In_) + _When_(InLength == 0 && OutLength == 0, _In_opt_) + PVOID Buffer + ); + + NTSTATUS SendIoCtrlAsynchronously + ( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_opt_ WDFMEMORY MemIn, + _In_opt_ WDFMEMORY MemOut, + _In_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine, + _In_ WDFCONTEXT Context + ); + + NTSTATUS GetBthHfpDescriptor + ( + _Out_ PBTHHFP_DESCRIPTOR2 * Descriptor + ); + + NTSTATUS EnableBthHfpNrecDisableStatusNotification(); + + NTSTATUS GetBthHfpVolumePropertyValues + ( + _In_ ULONG Length, + _Out_ PKSPROPERTY_VALUES * PropValues + ); + + NTSTATUS SetBthHfpSpeakerVolume + ( + _In_ LONG Volume + ); + + NTSTATUS GetBthHfpSpeakerVolume + ( + _Out_ LONG * Volume + ); + + NTSTATUS EnableBthHfpSpeakerVolumeStatusNotification(); + + NTSTATUS SetBthHfpMicVolume + ( + _In_ LONG Volume + ); + + NTSTATUS GetBthHfpMicVolume + ( + _Out_ LONG * Volume + ); + + NTSTATUS EnableBthHfpMicVolumeStatusNotification(); + + NTSTATUS GetBthHfpConnectionStatus + ( + _Out_ BOOL * ConnectionStatus + ); + + NTSTATUS EnableBthHfpConnectionStatusNotification(); + + static + NTSTATUS CreateCustomEndpointMinipair + ( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair + ); + + static + NTSTATUS UpdateCustomEndpointCategory + ( + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory + ); + + static + VOID DeleteCustomEndpointMinipair + ( + _In_ PENDPOINT_MINIPAIR CustomMinipair + ); + + NTSTATUS GetBthHfpCodecId + ( + _Out_ UCHAR * CodecId + ); + + NTSTATUS SetBthHfpConnect(); + + NTSTATUS SetBthHfpDisconnect(); + + NTSTATUS SetBthHfpStreamOpen(); + + NTSTATUS SetBthHfpStreamClose(); + + NTSTATUS EnableBthHfpStreamStatusNotification(); + + NTSTATUS StopBthHfpStreamStatusNotification(); + + // + // WDF I/O Target callback. + // + static + EVT_WDF_IO_TARGET_QUERY_REMOVE EvtBthHfpTargetQueryRemove; + + static + EVT_WDF_IO_TARGET_REMOVE_CANCELED EvtBthHfpTargetRemoveCanceled; + + static + EVT_WDF_IO_TARGET_REMOVE_COMPLETE EvtBthHfpTargetRemoveComplete; + + // + // Status notifications callbacks. + // + static + EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtBthHfpDeviceStreamStatusCompletion; + + static + EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtBthHfpDeviceNotificationStatusCompletion; + + static + EVT_WDF_WORKITEM EvtBthHfpDeviceNotificationStatusWorkItem; +}; +#endif // SYSVAD_BTH_BYPASS + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS SysvadIoSetDeviceInterfacePropertyDataMultiple +( + _In_ PUNICODE_STRING SymbolicLinkName, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties +) +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + for (ULONG i = 0; i < cPropertyCount; i++) + { + ntStatus = IoSetDeviceInterfacePropertyData( + SymbolicLinkName, + pProperties[i].PropertyKey, + LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, + pProperties[i].Type, + pProperties[i].BufferSize, + pProperties[i].Buffer); + + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +NewAdapterCommon +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType +) +/*++ + +Routine Description: + + Creates a new CAdapterCommon + +Arguments: + + Unknown - + + UnknownOuter - + + PoolType + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + + NTSTATUS ntStatus; + + // + // This sample supports only one instance of this object. + // (b/c of CSaveData's static members and Bluetooth HFP logic). + // + if (CAdapterCommon::m_AdapterInstances != 0) + { + ntStatus = STATUS_DEVICE_BUSY; + DPF(D_ERROR, ("NewAdapterCommon failed, only one instance is allowed")); + goto Done; + } + + CAdapterCommon::m_AdapterInstances++; + + // + // Allocate an adapter object. + // + CAdapterCommon *p = new(PoolType, MINADAPTER_POOLTAG) CAdapterCommon(UnknownOuter); + if (p == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + DPF(D_ERROR, ("NewAdapterCommon failed, 0x%x", ntStatus)); + goto Done; + } + + // + // Success. + // + *Unknown = PUNKNOWN((PADAPTERCOMMON)(p)); + (*Unknown)->AddRef(); + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} // NewAdapterCommon + +//============================================================================= +#pragma code_seg("PAGE") +CAdapterCommon::~CAdapterCommon +( + void +) +/*++ + +Routine Description: + + Destructor for CAdapterCommon. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::~CAdapterCommon]")); + + if (m_pHW) + { + delete m_pHW; + m_pHW = NULL; + } + + CSaveData::DestroyWorkItems(); + + SAFE_RELEASE(m_pPortClsEtwHelper); + SAFE_RELEASE(m_pServiceGroupWave); + + if (m_WdfDevice) + { + WdfObjectDelete(m_WdfDevice); + m_WdfDevice = NULL; + } + + CAdapterCommon::m_AdapterInstances--; + ASSERT(CAdapterCommon::m_AdapterInstances == 0); +} // ~CAdapterCommon + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(PDEVICE_OBJECT) +CAdapterCommon::GetDeviceObject +( + void +) +/*++ + +Routine Description: + + Returns the deviceobject + +Arguments: + +Return Value: + + PDEVICE_OBJECT + +--*/ +{ + PAGED_CODE(); + + return m_pDeviceObject; +} // GetDeviceObject + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(PDEVICE_OBJECT) +CAdapterCommon::GetPhysicalDeviceObject +( + void +) +/*++ + +Routine Description: + + Returns the PDO. + +Arguments: + +Return Value: + + PDEVICE_OBJECT + +--*/ +{ + PAGED_CODE(); + + return m_pPhysicalDeviceObject; +} // GetPhysicalDeviceObject + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(WDFDEVICE) +CAdapterCommon::GetWdfDevice +( + void +) +/*++ + +Routine Description: + + Returns the associated WDF miniport device. Note that this is NOT an audio + miniport. The WDF miniport device is the WDF device associated with the + adapter. + +Arguments: + +Return Value: + + WDFDEVICE + +--*/ +{ + PAGED_CODE(); + + return m_WdfDevice; +} // GetWdfDevice + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::Init +( + _In_ PDEVICE_OBJECT DeviceObject +) +/*++ + +Routine Description: + + Initialize adapter common object. + +Arguments: + + DeviceObject - pointer to the device object + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::Init]")); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + +#ifdef SYSVAD_BTH_BYPASS + m_BthHfpEnableCleanup = FALSE; +#endif // SYSVAD_BTH_BYPASS + + m_pServiceGroupWave = NULL; + m_pDeviceObject = DeviceObject; + m_pPhysicalDeviceObject = NULL; + m_WdfDevice = NULL; + m_PowerState = PowerDeviceD0; + m_pHW = NULL; + m_pPortClsEtwHelper = NULL; + + InitializeListHead(&m_SubdeviceCache); + + // + // Get the PDO. + // + ntStatus = PcGetPhysicalDeviceObject(DeviceObject, &m_pPhysicalDeviceObject); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("PcGetPhysicalDeviceObject failed, 0x%x", ntStatus)), + Done); + + // + // Create a WDF miniport to represent the adapter. Note that WDF miniports + // are NOT audio miniports. An audio adapter is associated with a single WDF + // miniport. This driver uses WDF to simplify the handling of the Bluetooth + // SCO HFP Bypass interface. + // + ntStatus = WdfDeviceMiniportCreate( WdfGetDriver(), + WDF_NO_OBJECT_ATTRIBUTES, + DeviceObject, // FDO + NULL, // Next device. + NULL, // PDO + &m_WdfDevice); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("WdfDeviceMiniportCreate failed, 0x%x", ntStatus)), + Done); + + // Initialize HW. + // + m_pHW = new (NonPagedPoolNx, SYSVAD_POOLTAG) CSYSVADHW; + if (!m_pHW) + { + DPF(D_TERSE, ("Insufficient memory for SYSVAD HW")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_JUMP(ntStatus, Done); + + m_pHW->MixerReset(); + + // + // Initialize SaveData class. + // + CSaveData::SetDeviceObject(DeviceObject); //device object is needed by CSaveData + ntStatus = CSaveData::InitializeWorkItems(DeviceObject); + IF_FAILED_JUMP(ntStatus, Done); + +Done: + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::MixerReset +( + void +) +/*++ + +Routine Description: + + Reset mixer registers from registry. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + if (m_pHW) + { + m_pHW->MixerReset(); + } +} // MixerReset + +//============================================================================= +/* Here are the definitions of the standard miniport events. + +Event type : eMINIPORT_IHV_DEFINED +Parameter 1 : Defined and used by IHVs +Parameter 2 : Defined and used by IHVs +Parameter 3 :Defined and used by IHVs +Parameter 4 :Defined and used by IHVs + +Event type: eMINIPORT_BUFFER_COMPLETE +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Data length completed +Parameter 4:0 + +Event type: eMINIPORT_PIN_STATE +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Pin State 0->KS_STOP, 1->KS_ACQUIRE, 2->KS_PAUSE, 3->KS_RUN +Parameter 4:0 + +Event type: eMINIPORT_GET_STREAM_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: 0 +Parameter 4:0 + + +Event type: eMINIPORT_SET_WAVERT_BUFFER_WRITE_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: the arget WaveRtBufferWritePosition received from portcls +Parameter 4:0 + +Event type: eMINIPORT_GET_PRESENTATION_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Presentation position +Parameter 4:0 + +Event type: eMINIPORT_PROGRAM_DMA +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Starting WaveRt buffer offset +Parameter 4: Data length + +Event type: eMINIPORT_GLITCH_REPORT +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: major glitch code: 1:WaveRT buffer is underrun, + 2:decoder errors, + 3:receive the same wavert buffer two in a row in event driven mode +Parameter 4: minor code for the glitch cause + +Event type: eMINIPORT_LAST_BUFFER_RENDERED +Parameter 1: Current linear buffer position +Parameter 2: the very last WaveRtBufferWritePosition that the driver received +Parameter 3: 0 +Parameter 4: 0 + +*/ +#pragma code_seg() +STDMETHODIMP +CAdapterCommon::WriteEtwEvent +( + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (m_pPortClsEtwHelper) + { + ntStatus = m_pPortClsEtwHelper->MiniportWriteEtwEvent( miniportEventType, ullData1, ullData2, ullData3, ullData4) ; + } + return ntStatus; +} // WriteEtwEvent + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::SetEtwHelper +( + PPORTCLSETWHELPER _pPortClsEtwHelper +) +{ + PAGED_CODE(); + + SAFE_RELEASE(m_pPortClsEtwHelper); + + m_pPortClsEtwHelper = _pPortClsEtwHelper; + + if (m_pPortClsEtwHelper) + { + m_pPortClsEtwHelper->AddRef(); + } +} // SetEtwHelper + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CAdapterCommon::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface routine for AdapterCommon + +Arguments: + + Interface - + + Object - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PADAPTERCOMMON(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IAdapterCommon)) + { + *Object = PVOID(PADAPTERCOMMON(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IAdapterPowerManagement)) + { + *Object = PVOID(PADAPTERPOWERMANAGEMENT(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::SetWaveServiceGroup +( + _In_ PSERVICEGROUP ServiceGroup +) +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::SetWaveServiceGroup]")); + + SAFE_RELEASE(m_pServiceGroupWave); + + m_pServiceGroupWave = ServiceGroup; + + if (m_pServiceGroupWave) + { + m_pServiceGroupWave->AddRef(); + } +} // SetWaveServiceGroup + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(BOOL) +CAdapterCommon::bDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + BOOL - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->bGetDevSpecific(); + } + + return FALSE; +} // bDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::bDevSpecificWrite +( + _In_ BOOL bDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + bDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->bSetDevSpecific(bDevSpecific); + } +} // DevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(INT) +CAdapterCommon::iDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + INT - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->iGetDevSpecific(); + } + + return 0; +} // iDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::iDevSpecificWrite +( + _In_ INT iDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + iDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->iSetDevSpecific(iDevSpecific); + } +} // iDevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(UINT) +CAdapterCommon::uiDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + UINT - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->uiGetDevSpecific(); + } + + return 0; +} // uiDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::uiDevSpecificWrite +( + _In_ UINT uiDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + uiDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->uiSetDevSpecific(uiDevSpecific); + } +} // uiDevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(BOOL) +CAdapterCommon::MixerMuteRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + +Return Value: + + BOOL - mixer mute setting for this node + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerMute(Index, Channel); + } + + return 0; +} // MixerMuteRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerMuteWrite +( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerMute(Index, Channel, Value); + } +} // MixerMuteWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(ULONG) +CAdapterCommon::MixerMuxRead() +/*++ + +Routine Description: + + Return the mux selection + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerMux(); + } + + return 0; +} // MixerMuxRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerMuxWrite +( + _In_ ULONG Index +) +/*++ + +Routine Description: + + Store the new mux selection + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerMux(Index); + } +} // MixerMuxWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(LONG) +CAdapterCommon::MixerVolumeRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Return the value in mixer register array. + +Arguments: + + Index - node id + + Channel = which channel + +Return Value: + + Byte - mixer volume settings for this line + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerVolume(Index, Channel); + } + + return 0; +} // MixerVolumeRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerVolumeWrite +( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + + Channel - which channel + + Value - new volume level + +Return Value: + + void + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerVolume(Index, Channel, Value); + } +} // MixerVolumeWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(LONG) +CAdapterCommon::MixerPeakMeterRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Return the value in mixer register array. + +Arguments: + + Index - node id + + Channel = which channel + +Return Value: + + Byte - mixer sample peak meter settings for this line + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerPeakMeter(Index, Channel); + } + + return 0; +} // MixerVolumeRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::PowerChangeState +( + _In_ POWER_STATE NewState +) +/*++ + +Routine Description: + + +Arguments: + + NewState - The requested, new power state for the device. + +Return Value: + + void + +Note: + From MSDN: + + To assist the driver, PortCls will pause any active audio streams prior to calling + this method to place the device in a sleep state. After calling this method, PortCls + will unpause active audio streams, to wake the device up. Miniports can opt for + additional notification by utilizing the IPowerNotify interface. + + The miniport driver must perform the requested change to the device's power state + before it returns from the PowerChangeState call. If the miniport driver needs to + save or restore any device state before a power-state change, the miniport driver + should support the IPowerNotify interface, which allows it to receive advance warning + of any such change. Before returning from a successful PowerChangeState call, the + miniport driver should cache the new power state. + + While the miniport driver is in one of the sleep states (any state other than + PowerDeviceD0), it must avoid writing to the hardware. The miniport driver must cache + any hardware accesses that need to be deferred until the device powers up again. If + the power state is changing from one of the sleep states to PowerDeviceD0, the + miniport driver should perform any deferred hardware accesses after it has powered up + the device. If the power state is changing from PowerDeviceD0 to a sleep state, the + miniport driver can perform any necessary hardware accesses during the PowerChangeState + call before it powers down the device. + + While powered down, a miniport driver is never asked to create a miniport driver object + or stream object. PortCls always places the device in the PowerDeviceD0 state before + calling the miniport driver's NewStream method. + +--*/ +{ + DPF_ENTER(("[CAdapterCommon::PowerChangeState]")); + + // Notify all registered miniports of a power state change + PLIST_ENTRY le = NULL; + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (pRecord->PowerInterface) + { + pRecord->PowerInterface->PowerChangeState(NewState); + } + } + + // is this actually a state change?? + // + if (NewState.DeviceState != m_PowerState) + { + // switch on new state + // + switch (NewState.DeviceState) + { + case PowerDeviceD0: + case PowerDeviceD1: + case PowerDeviceD2: + case PowerDeviceD3: + m_PowerState = NewState.DeviceState; + + DPF + ( + D_VERBOSE, + ("Entering D%u", ULONG(m_PowerState) - ULONG(PowerDeviceD0)) + ); + + break; + + default: + + DPF(D_VERBOSE, ("Unknown Device Power State")); + break; + } + } +} // PowerStateChange + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::QueryDeviceCapabilities +( + _Inout_updates_bytes_(sizeof(DEVICE_CAPABILITIES)) PDEVICE_CAPABILITIES PowerDeviceCaps +) +/*++ + +Routine Description: + + Called at startup to get the caps for the device. This structure provides + the system with the mappings between system power state and device power + state. This typically will not need modification by the driver. + +Arguments: + + PowerDeviceCaps - The device's capabilities. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(PowerDeviceCaps); + + DPF_ENTER(("[CAdapterCommon::QueryDeviceCapabilities]")); + + return (STATUS_SUCCESS); +} // QueryDeviceCapabilities + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::QueryPowerChangeState +( + _In_ POWER_STATE NewStateQuery +) +/*++ + +Routine Description: + + Query to see if the device can change to this power state + +Arguments: + + NewStateQuery - The requested, new power state for the device + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + DPF_ENTER(("[CAdapterCommon::QueryPowerChangeState]")); + + // query each miniport for it's power state, we're finished if even one indicates + // it cannot go to this power state. + PLIST_ENTRY le = NULL; + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && NT_SUCCESS(status); le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (pRecord->PowerInterface) + { + status = pRecord->PowerInterface->QueryPowerChangeState(NewStateQuery); + } + } + + return status; +} // QueryPowerChangeState + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::CreateAudioInterfaceWithProperties +( + _In_ PCWSTR ReferenceString, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties, + _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName +) +/*++ + +Routine Description: + +Create the audio interface (in disabled mode). + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CreateAudioInterfaceWithProperties]")); + + NTSTATUS ntStatus; + UNICODE_STRING referenceString; + + RtlInitUnicodeString(&referenceString, ReferenceString); + + // + // Reset output value. + // + RtlZeroMemory(AudioSymbolicLinkName, sizeof(UNICODE_STRING)); + + // + // Register an audio interface if not already present. + // + ntStatus = IoRegisterDeviceInterface( + GetPhysicalDeviceObject(), + &KSCATEGORY_AUDIO, + &referenceString, + AudioSymbolicLinkName); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: IoRegisterDeviceInterface(KSCATEGORY_AUDIO): failed, 0x%x", ntStatus)), + Done); + + // + // Set properties on the interface + // + ntStatus = SysvadIoSetDeviceInterfacePropertyDataMultiple(AudioSymbolicLinkName, cPropertyCount, pProperties); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: SysvadIoSetDeviceInterfacePropertyDataMultiple(...): failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + RtlFreeUnicodeString(AudioSymbolicLinkName); + RtlZeroMemory(AudioSymbolicLinkName, sizeof(UNICODE_STRING)); + } + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::InstallSubdevice +( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY * pProperties, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PRESOURCELIST ResourceList, + _In_ REFGUID PortInterfaceId, + _Out_opt_ PUNKNOWN * OutPortInterface, + _Out_opt_ PUNKNOWN * OutPortUnknown, + _Out_opt_ PUNKNOWN * OutMiniportUnknown +) +{ +/*++ + +Routine Description: + + This function creates and registers a subdevice consisting of a port + driver, a minport driver and a set of resources bound together. It will + also optionally place a pointer to an interface on the port driver in a + specified location before initializing the port driver. This is done so + that a common ISR can have access to the port driver during + initialization, when the ISR might fire. + +Arguments: + + Irp - pointer to the irp object. + + Name - name of the miniport. Passes to PcRegisterSubDevice + + PortClassId - port class id. Passed to PcNewPort. + + MiniportClassId - miniport class id. Passed to PcNewMiniport. + + MiniportCreate - pointer to a miniport creation function. If NULL, + PcNewMiniport is used. + + DeviceContext - deviceType specific. + + MiniportPair - endpoint configuration info. + + ResourceList - pointer to the resource list. + + PortInterfaceId - GUID that represents the port interface. + + OutPortInterface - pointer to store the port interface + + OutPortUnknown - pointer to store the unknown port interface. + + OutMiniportUnknown - pointer to store the unknown miniport interface + +Return Value: + + NT status code. + +--*/ + PAGED_CODE(); + DPF_ENTER(("[InstallSubDevice %S]", Name)); + + ASSERT(Name != NULL); + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus; + PPORT port = NULL; + PUNKNOWN miniport = NULL; + PADAPTERCOMMON adapterCommon = NULL; + UNICODE_STRING symbolicLink = { 0 }; + + adapterCommon = PADAPTERCOMMON(this); + + ntStatus = CreateAudioInterfaceWithProperties(Name, cPropertyCount, pProperties, &symbolicLink); + if (NT_SUCCESS(ntStatus)) + { + // Currently have no use for the symbolic link + RtlFreeUnicodeString(&symbolicLink); + + // Create the port driver object + // + ntStatus = PcNewPort(&port, PortClassId); + } + + // Create the miniport object + // + if (NT_SUCCESS(ntStatus)) + { + if (MiniportCreate) + { + ntStatus = + MiniportCreate + ( + &miniport, + MiniportClassId, + NULL, + NonPagedPoolNx, + adapterCommon, + DeviceContext, + MiniportPair + ); + } + else + { + ntStatus = + PcNewMiniport + ( + (PMINIPORT *) &miniport, + MiniportClassId + ); + } + } + + // Init the port driver and miniport in one go. + // + if (NT_SUCCESS(ntStatus)) + { +#pragma warning(push) + // IPort::Init's annotation on ResourceList requires it to be non-NULL. However, + // for dynamic devices, we may no longer have the resource list and this should + // still succeed. + // +#pragma warning(disable:6387) + ntStatus = + port->Init + ( + m_pDeviceObject, + Irp, + miniport, + adapterCommon, + ResourceList + ); +#pragma warning (pop) + + if (NT_SUCCESS(ntStatus)) + { + // Register the subdevice (port/miniport combination). + // + ntStatus = + PcRegisterSubdevice + ( + m_pDeviceObject, + Name, + port + ); + } + } + + // Deposit the port interfaces if it's needed. + // + if (NT_SUCCESS(ntStatus)) + { + if (OutPortUnknown) + { + ntStatus = + port->QueryInterface + ( + IID_IUnknown, + (PVOID *)OutPortUnknown + ); + } + + if (OutPortInterface) + { + ntStatus = + port->QueryInterface + ( + PortInterfaceId, + (PVOID *) OutPortInterface + ); + } + + if (OutMiniportUnknown) + { + ntStatus = + miniport->QueryInterface + ( + IID_IUnknown, + (PVOID *)OutMiniportUnknown + ); + } + + } + + if (port) + { + port->Release(); + } + + if (miniport) + { + miniport->Release(); + } + + return ntStatus; +} // InstallSubDevice + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::UnregisterSubdevice +( + _In_opt_ PUNKNOWN UnknownPort +) +/*++ + +Routine Description: + + Unregisters and releases the specified subdevice. + +Arguments: + + UnknownPort - Wave or topology port interface. + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::UnregisterSubdevice]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNREGISTERSUBDEVICE unregisterSubdevice = NULL; + + if (NULL == UnknownPort) + { + return ntStatus; + } + + // + // Get the IUnregisterSubdevice interface. + // + ntStatus = UnknownPort->QueryInterface( + IID_IUnregisterSubdevice, + (PVOID *)&unregisterSubdevice); + + // + // Unregister the port object. + // + if (NT_SUCCESS(ntStatus)) + { + ntStatus = unregisterSubdevice->UnregisterSubdevice( + m_pDeviceObject, + UnknownPort); + + // + // Release the IUnregisterSubdevice interface. + // + unregisterSubdevice->Release(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::ConnectTopologies +( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount +) +/*++ + +Routine Description: + + Connects the bridge pins between the wave and mixer topologies. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::ConnectTopologies]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // register wave <=> topology connections + // This will connect bridge pins of wave and topology + // miniports. + // + for (ULONG i = 0; i < PhysicalConnectionCount && NT_SUCCESS(ntStatus); i++) + { + + switch(PhysicalConnections[i].eType) + { + case CONNECTIONTYPE_TOPOLOGY_OUTPUT: + ntStatus = + PcRegisterPhysicalConnection + ( + m_pDeviceObject, + UnknownTopology, + PhysicalConnections[i].ulTopology, + UnknownWave, + PhysicalConnections[i].ulWave + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("ConnectTopologies: PcRegisterPhysicalConnection(render) failed, 0x%x", ntStatus)); + } + break; + case CONNECTIONTYPE_WAVE_OUTPUT: + ntStatus = + PcRegisterPhysicalConnection + ( + m_pDeviceObject, + UnknownWave, + PhysicalConnections[i].ulWave, + UnknownTopology, + PhysicalConnections[i].ulTopology + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("ConnectTopologies: PcRegisterPhysicalConnection(capture) failed, 0x%x", ntStatus)); + } + break; + } + } + + // + // Cleanup in case of error. + // + if (!NT_SUCCESS(ntStatus)) + { + // disconnect all connections on error, ignore error code because not all + // connections may have been made + DisconnectTopologies(UnknownTopology, UnknownWave, PhysicalConnections, PhysicalConnectionCount); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::DisconnectTopologies +( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount +) +/*++ + +Routine Description: + + Disconnects the bridge pins between the wave and mixer topologies. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::DisconnectTopologies]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + NTSTATUS ntStatus2 = STATUS_SUCCESS; + PUNREGISTERPHYSICALCONNECTION unregisterPhysicalConnection = NULL; + + // + // Get the IUnregisterPhysicalConnection interface + // + ntStatus = UnknownTopology->QueryInterface( + IID_IUnregisterPhysicalConnection, + (PVOID *)&unregisterPhysicalConnection); + + if (NT_SUCCESS(ntStatus)) + { + for (ULONG i = 0; i < PhysicalConnectionCount; i++) + { + switch(PhysicalConnections[i].eType) + { + case CONNECTIONTYPE_TOPOLOGY_OUTPUT: + ntStatus = + unregisterPhysicalConnection->UnregisterPhysicalConnection( + m_pDeviceObject, + UnknownTopology, + PhysicalConnections[i].ulTopology, + UnknownWave, + PhysicalConnections[i].ulWave + ); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(render) failed, 0x%x", ntStatus)); + } + break; + case CONNECTIONTYPE_WAVE_OUTPUT: + ntStatus = + unregisterPhysicalConnection->UnregisterPhysicalConnection( + m_pDeviceObject, + UnknownWave, + PhysicalConnections[i].ulWave, + UnknownTopology, + PhysicalConnections[i].ulTopology + ); + if (!NT_SUCCESS(ntStatus2)) + { + DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(capture) failed, 0x%x", ntStatus2)); + } + break; + } + + // cache and return the first error encountered, as it's likely the most relevent + if (NT_SUCCESS(ntStatus)) + { + ntStatus = ntStatus2; + } + } + } + + // + // Release the IUnregisterPhysicalConnection interface. + // + SAFE_RELEASE(unregisterPhysicalConnection); + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::GetCachedSubdevice +( + _In_ PWSTR Name, + _Out_opt_ PUNKNOWN *OutUnknownPort, + _Out_opt_ PUNKNOWN *OutUnknownMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::GetCachedSubdevice]")); + + // search list, return interface to device if found, fail if not found + PLIST_ENTRY le = NULL; + BOOL bFound = FALSE; + + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && !bFound; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (0 == wcscmp(Name, pRecord->Name)) + { + if (OutUnknownPort) + { + *OutUnknownPort = pRecord->PortInterface; + (*OutUnknownPort)->AddRef(); + } + + if (OutUnknownMiniport) + { + *OutUnknownMiniport = pRecord->MiniportInterface; + (*OutUnknownMiniport)->AddRef(); + } + + bFound = TRUE; + } + } + + return bFound?STATUS_SUCCESS:STATUS_OBJECT_NAME_NOT_FOUND; +} + + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::CacheSubdevice +( + _In_ PWSTR Name, + _In_ PUNKNOWN UnknownPort, + _In_ PUNKNOWN UnknownMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CacheSubdevice]")); + + // add the item with this name/interface to the list + NTSTATUS ntStatus = STATUS_SUCCESS; + MINIPAIR_UNKNOWN *pNewSubdevice = NULL; + + pNewSubdevice = new(NonPagedPoolNx, MINADAPTER_POOLTAG) MINIPAIR_UNKNOWN; + + if (!pNewSubdevice) + { + DPF(D_TERSE, ("Insufficient memory to cache subdevice")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS(ntStatus)) + { + memset(pNewSubdevice, 0, sizeof(MINIPAIR_UNKNOWN)); + + ntStatus = RtlStringCchCopyW(pNewSubdevice->Name, SIZEOF_ARRAY(pNewSubdevice->Name), Name); + } + + if (NT_SUCCESS(ntStatus)) + { + pNewSubdevice->PortInterface = UnknownPort; + pNewSubdevice->PortInterface->AddRef(); + + pNewSubdevice->MiniportInterface = UnknownMiniport; + pNewSubdevice->MiniportInterface->AddRef(); + + // cache the IAdapterPowerManagement interface (if available) from the filter. Some endpoints, + // like FM and cellular, have their own power requirements that we must track. If this fails, + // it just means this filter doesn't do power management. + UnknownMiniport->QueryInterface(IID_IAdapterPowerManagement, (PVOID *)&(pNewSubdevice->PowerInterface)); + + InsertTailList(&m_SubdeviceCache, &pNewSubdevice->ListEntry); + } + + if (!NT_SUCCESS(ntStatus)) + { + if (pNewSubdevice) + { + delete pNewSubdevice; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::RemoveCachedSubdevice +( + _In_ PWSTR Name +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::RemoveCachedSubdevice]")); + + // search list, remove the entry from the list + + PLIST_ENTRY le = NULL; + BOOL bRemoved = FALSE; + + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && !bRemoved; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (0 == wcscmp(Name, pRecord->Name)) + { + SAFE_RELEASE(pRecord->PortInterface); + SAFE_RELEASE(pRecord->MiniportInterface); + SAFE_RELEASE(pRecord->PowerInterface); + memset(pRecord->Name, 0, sizeof(pRecord->Name)); + RemoveEntryList(le); + bRemoved = TRUE; + delete pRecord; + break; + } + } + + return bRemoved?STATUS_SUCCESS:STATUS_OBJECT_NAME_NOT_FOUND; +} + +#pragma code_seg("PAGE") +VOID +CAdapterCommon::EmptySubdeviceCache() +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::EmptySubdeviceCache]")); + + while (!IsListEmpty(&m_SubdeviceCache)) + { + PLIST_ENTRY le = RemoveHeadList(&m_SubdeviceCache); + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + SAFE_RELEASE(pRecord->PortInterface); + SAFE_RELEASE(pRecord->MiniportInterface); + SAFE_RELEASE(pRecord->PowerInterface); + memset(pRecord->Name, 0, sizeof(pRecord->Name)); + + delete pRecord; + } +} + +#pragma code_seg("PAGE") +VOID +CAdapterCommon::Cleanup() +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::Cleanup]")); + +#ifdef SYSVAD_BTH_BYPASS + // + // This ensures Bluetooth HFP notifications are turned off when port class + // cleanups and unregisters the static subdevices. + // + CleanupBthScoBypass(); +#endif // SYSVAD_BTH_BYPASS + + EmptySubdeviceCache(); +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::InstallEndpointFilters +( + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::InstallEndpointFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopology = NULL; + PUNKNOWN unknownWave = NULL; + BOOL bTopologyCreated = FALSE; + BOOL bWaveCreated = FALSE; + + if (UnknownTopology) + { + *UnknownTopology = NULL; + } + + if (UnknownWave) + { + *UnknownWave = NULL; + } + + ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopology, NULL); + if (!NT_SUCCESS(ntStatus) || NULL == unknownTopology) + { + PUNKNOWN unknownMiniportTopology = NULL; + + bTopologyCreated = TRUE; + + // Install SYSVAD topology miniport for the render endpoint. + // + ntStatus = InstallSubdevice(Irp, + MiniportPair->TopoName, // make sure this name matches with SYSVAD.<TopoName>.szPname in the inf's [Strings] section + CLSID_PortTopology, + CLSID_PortTopology, + MiniportPair->TopoCreateCallback, + MiniportPair->TopoInterfacePropertyCount, + MiniportPair->TopoInterfaceProperties, + DeviceContext, + MiniportPair, + NULL, + IID_IPortTopology, + NULL, + &unknownTopology, + &unknownMiniportTopology + ); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = CacheSubdevice(MiniportPair->TopoName, unknownTopology, unknownMiniportTopology); + } + + SAFE_RELEASE(unknownMiniportTopology); + } + + ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWave, NULL); + if (!NT_SUCCESS(ntStatus) || NULL == unknownWave) + { + PUNKNOWN unknownMiniportWave = NULL; + + bWaveCreated = TRUE; + + // Install SYSVAD wave miniport for the render endpoint. + // + ntStatus = InstallSubdevice(Irp, + MiniportPair->WaveName, // make sure this name matches with SYSVAD.<WaveName>.szPname in the inf's [Strings] section + CLSID_PortWaveRT, + CLSID_PortWaveRT, + MiniportPair->WaveCreateCallback, + MiniportPair->WaveInterfacePropertyCount, + MiniportPair->WaveInterfaceProperties, + DeviceContext, + MiniportPair, + NULL, + IID_IPortWaveRT, + NULL, + &unknownWave, + &unknownMiniportWave + ); + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = CacheSubdevice(MiniportPair->WaveName, unknownWave, unknownMiniportWave); + } + + SAFE_RELEASE(unknownMiniportWave); + } + + if (unknownTopology && unknownWave) + { + // + // register wave <=> topology connections + // This will connect bridge pins of wave and topology + // miniports. + // + ntStatus = ConnectTopologies( + unknownTopology, + unknownWave, + MiniportPair->PhysicalConnections, + MiniportPair->PhysicalConnectionCount); + } + + if (NT_SUCCESS(ntStatus)) + { + // + // Set output parameters. + // + if (UnknownTopology != NULL && unknownTopology != NULL) + { + unknownTopology->AddRef(); + *UnknownTopology = unknownTopology; + } + + if (UnknownWave != NULL && unknownWave != NULL) + { + unknownWave->AddRef(); + *UnknownWave = unknownWave; + } + } + else + { + if (bTopologyCreated && unknownTopology != NULL) + { + UnregisterSubdevice(unknownTopology); + RemoveCachedSubdevice(MiniportPair->TopoName); + } + + if (bWaveCreated && unknownWave != NULL) + { + UnregisterSubdevice(unknownWave); + RemoveCachedSubdevice(MiniportPair->WaveName); + } + } + + SAFE_RELEASE(unknownTopology); + SAFE_RELEASE(unknownWave); + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::RemoveEndpointFilters +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::RemoveEndpointFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (UnknownTopology != NULL && UnknownWave != NULL) + { + ntStatus = DisconnectTopologies( + UnknownTopology, + UnknownWave, + MiniportPair->PhysicalConnections, + MiniportPair->PhysicalConnectionCount); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: DisconnectTopologies failed: 0x%x", ntStatus)); + } + } + + + RemoveCachedSubdevice(MiniportPair->WaveName); + + ntStatus = UnregisterSubdevice(UnknownWave); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(wave) failed: 0x%x", ntStatus)); + } + + RemoveCachedSubdevice(MiniportPair->TopoName); + + ntStatus = UnregisterSubdevice(UnknownTopology); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(topology) failed: 0x%x", ntStatus)); + } + + // + // All Done. + // + ntStatus = STATUS_SUCCESS; + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::GetFilters +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN * UnknownTopologyPort, + _Out_opt_ PUNKNOWN * UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN * UnknownWavePort, + _Out_opt_ PUNKNOWN * UnknownWaveMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::GetFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopologyPort = NULL; + PUNKNOWN unknownTopologyMiniport = NULL; + PUNKNOWN unknownWavePort = NULL; + PUNKNOWN unknownWaveMiniport = NULL; + + // if the client requested the topology filter, find it and return it + if (UnknownTopologyPort != NULL || UnknownTopologyMiniport != NULL) + { + ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopologyPort, &unknownTopologyMiniport); + if (NT_SUCCESS(ntStatus)) + { + if (UnknownTopologyPort) + { + *UnknownTopologyPort = unknownTopologyPort; + } + + if (UnknownTopologyMiniport) + { + *UnknownTopologyMiniport = unknownTopologyMiniport; + } + } + } + + // if the client requested the wave filter, find it and return it + if (NT_SUCCESS(ntStatus) && (UnknownWavePort != NULL || UnknownWaveMiniport != NULL)) + { + ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWavePort, &unknownWaveMiniport); + if (NT_SUCCESS(ntStatus)) + { + if (UnknownWavePort) + { + *UnknownWavePort = unknownWavePort; + } + + if (UnknownWaveMiniport) + { + *UnknownWaveMiniport = unknownWaveMiniport; + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::SetIdlePowerManagement +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnabled +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::SetIdlePowerManagement]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + IUnknown *pUnknown = NULL; + PPORTCLSPOWER pPortClsPower = NULL; + // refcounting disable requests. Each miniport is responsible for calling this in pairs, + // disable on the first request to disable, enable on the last request to enable. + + // make sure that we always call SetIdlePowerManagment using the IPortClsPower + // from the requesting port, so we don't cache a reference to a port + // indefinitely, preventing it from ever unloading. + ntStatus = GetFilters(MiniportPair, NULL, NULL, &pUnknown, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = + pUnknown->QueryInterface + ( + IID_IPortClsPower, + (PVOID*) &pPortClsPower + ); + } + + if (NT_SUCCESS(ntStatus)) + { + if (bEnabled) + { + m_dwIdleRequests--; + + if (0 == m_dwIdleRequests) + { + pPortClsPower->SetIdlePowerManagement(m_pDeviceObject, TRUE); + } + } + else + { + if (0 == m_dwIdleRequests) + { + pPortClsPower->SetIdlePowerManagement(m_pDeviceObject, FALSE); + } + + m_dwIdleRequests++; + } + } + + SAFE_RELEASE(pUnknown); + SAFE_RELEASE(pPortClsPower); + + return ntStatus; +} + +#ifdef SYSVAD_BTH_BYPASS +// +// CAdapterCommon Bluetooth Hands-Free Profile function implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CAdapterCommon::EvtBthHfpScoBypassInterfaceWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + The function handles the arrival or removal of a HFP SCO Bypass interface. + +Arguments: + + WorkItem - WDF work-item object. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[EvtBthHfpScoBypassInterfaceWorkItem]")); + + CAdapterCommon * This; + + if (WorkItem == NULL) + { + return; + } + + This = GetBthHfpWorkItemContext(WorkItem)->Adapter; + ASSERT(This != NULL); + + for (;;) + { + PLIST_ENTRY le = NULL; + BthHfpWorkTask * task = NULL; + + // + // Retrieve a taask. + // + ExAcquireFastMutex(&This->m_BthHfpFastMutex); + if (!IsListEmpty(&This->m_BthHfpWorkTasks)) + { + le = RemoveHeadList(&This->m_BthHfpWorkTasks); + task = CONTAINING_RECORD(le, BthHfpWorkTask, ListEntry); + InitializeListHead(le); + } + ExReleaseFastMutex(&This->m_BthHfpFastMutex); + + if (task == NULL) + { + break; + } + + ASSERT(task->Device != NULL); + _Analysis_assume_(task->Device != NULL); + + // + // Process the task. + // + switch(task->Action) + { + case eBthHfpTaskStart: + task->Device->Start(); + break; + + case eBthHfpTaskStop: + task->Device->Stop(); + break; + + default: + DPF(D_ERROR, ("EvtBthHfpScoBypassInterfaceWorkItem: invalid action %d", task->Action)); + break; + } + + // + // Release the ref we took on the device when we inserted the task in the queue. + // For a stop operation this may be the last reference. + // + SAFE_RELEASE(task->Device); + + // + // Free the task. + // + ExFreeToNPagedLookasideList(&This->m_BhtHfpWorkTaskPool, task); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +BthHfpDevice * +CAdapterCommon::BthHfpDeviceFind +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function looks for the specified device in the adapter's list. + +Arguments: + + SymbolicLinkName - interface's symbolic link. + +Return Value: + + BthHfpDevice pointer or NULL. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpDeviceFind]")); + + PLIST_ENTRY le = NULL; + BthHfpDevice * bthDevice = NULL; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + for (le = m_BthHfpDevices.Flink; le != &m_BthHfpDevices; le = le->Flink) + { + BthHfpDevice * tmpBthDevice = BthHfpDevice::GetBthHfpDevice(le); + ASSERT(tmpBthDevice != NULL); + + PUNICODE_STRING unicodeStr = tmpBthDevice->GetSymbolicLinkName(); + ASSERT(unicodeStr != NULL); + + if (unicodeStr->Length == SymbolicLinkName->Length && + 0 == wcsncmp(unicodeStr->Buffer, SymbolicLinkName->Buffer, unicodeStr->Length/sizeof(WCHAR))) + { + // Found it! + bthDevice = tmpBthDevice; + bthDevice->AddRef(); + break; + } + } + + ExReleaseFastMutex(&m_BthHfpFastMutex); + + return bthDevice; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::BthHfpScoInterfaceArrival +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function handles the arrival of a new HFP SCO Bypass interface. + +Arguments: + + SymbolicLinkName - new interface's symbolic link. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpScoInterfaceArrival]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * bthDevice = NULL; + BthHfpWorkTask * bthWorkTask = NULL; + + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: SymbolicLinkName %wZ", SymbolicLinkName)); + + // + // Check if the Bluetooth device is already present. + // According to the docs it is possible to receive two notifications for the same + // interface. + // + bthDevice = BthHfpDeviceFind(SymbolicLinkName); + if (bthDevice != NULL) + { + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: Bluetooth HFP device already present")); + SAFE_RELEASE(bthDevice); + ntStatus = STATUS_SUCCESS; + goto Done; + } + + // + // Alloc a new structure for this Bluetooth hands-free device. + // + bthDevice = new (NonPagedPoolNx, MINADAPTER_POOLTAG) BthHfpDevice(NULL); // NULL -> OuterUnknown + if (NULL == bthDevice) + { + DPF(D_ERROR, ("BthHfpScoInterfaceArrival: unable to allocate BthHfpDevice, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: created BthHfpDevice 0x%p ", bthDevice)); + + // + // Basic initialization of the Bluetooth Hands-Free Profile interface. + // The audio miniport creation is done later by the BthHfpDevice.Start() + // which is invoked asynchronously by a worker thread. + // BthHfpDevice->Init() must be invoked just after the creation of the object. + // + ntStatus = bthDevice->Init(this, SymbolicLinkName); + IF_FAILED_JUMP(ntStatus, Done); + + // + // Get and init a work task. + // + bthWorkTask = (BthHfpWorkTask*)ExAllocateFromNPagedLookasideList(&m_BhtHfpWorkTaskPool); + if (NULL == bthWorkTask) + { + DPF(D_ERROR, ("BthHfpScoInterfaceArrival: unable to allocate BthHfpWorkTask, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + RtlZeroMemory(bthWorkTask, sizeof(*bthWorkTask)); + bthWorkTask->Action = eBthHfpTaskStart; + InitializeListHead(&bthWorkTask->ListEntry); + // Note that bthDevice has one reference at this point. + bthWorkTask->Device = bthDevice; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + // + // Insert this new Bluetooth HFP device in our list. + // + InsertTailList(&m_BthHfpDevices, bthDevice->GetListEntry()); + + // + // Add a new task for the worker thread. + // + InsertTailList(&m_BthHfpWorkTasks, &bthWorkTask->ListEntry); + bthDevice->AddRef(); // released when task runs. + + // + // Schedule a work-item if not already running. + // + WdfWorkItemEnqueue(m_BthHfpWorkItem); + + ExReleaseFastMutex(&m_BthHfpFastMutex); + +Done: + if (!NT_SUCCESS(ntStatus)) + { + // Release the last ref, this will delete the BthHfpDevice + SAFE_RELEASE(bthDevice); + + if (bthWorkTask != NULL) + { + ExFreeToNPagedLookasideList(&m_BhtHfpWorkTaskPool, bthWorkTask); + bthWorkTask = NULL; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::BthHfpScoInterfaceRemoval +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function handles the removal of a HFP SCO Bypass interface. + +Arguments: + + SymbolicLinkName - interface's symbolic link to remove. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpScoInterfaceRemoval]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * bthDevice = NULL; + BthHfpWorkTask * bthWorkTask = NULL; + + DPF(D_VERBOSE, ("BthHfpScoInterfaceRemoval: SymbolicLinkName %wZ", SymbolicLinkName)); + + // + // Check if the Bluetooth device is present. + // + bthDevice = BthHfpDeviceFind(SymbolicLinkName); + if (bthDevice == NULL) + { + // This can happen if the init/start of the BthHfpDevice failed. + DPF(D_VERBOSE, ("BthHfpScoInterfaceRemoval: Bluetooth HFP device not found")); + ntStatus = STATUS_SUCCESS; + goto Done; + } + + // + // Init a work task. + // + bthWorkTask = (BthHfpWorkTask*)ExAllocateFromNPagedLookasideList(&m_BhtHfpWorkTaskPool); + if (NULL == bthWorkTask) + { + DPF(D_ERROR, ("BthHfpScoInterfaceRemoval: unable to allocate BthHfpWorkTask, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + RtlZeroMemory(bthWorkTask, sizeof(*bthWorkTask)); + bthWorkTask->Action = eBthHfpTaskStop; + InitializeListHead(&bthWorkTask->ListEntry); + // Work-item callback will release the reference we got above from BthHfpDeviceFind. + bthWorkTask->Device = bthDevice; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + // + // Remove this Bluetooth device from our list and release the associated reference. + // + RemoveEntryList(bthDevice->GetListEntry()); + InitializeListHead(bthDevice->GetListEntry()); + bthDevice->Release(); // This is not the last ref. + + // + // Add a new task for the worker thread. + // + InsertTailList(&m_BthHfpWorkTasks, &bthWorkTask->ListEntry); + + // + // Schedule a work-item if not already running. + // + WdfWorkItemEnqueue(m_BthHfpWorkItem); + + ExReleaseFastMutex(&m_BthHfpFastMutex); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + if (!NT_SUCCESS(ntStatus)) + { + // Release the ref we got in find. + SAFE_RELEASE(bthDevice); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::EvtBthHfpScoBypassInterfaceChange( + _In_ PVOID NotificationPointer, + _Inout_opt_ PVOID Context + ) +/*++ + +Routine Description: + + This callback is invoked when a new HFP SCO Bypass interface is added or removed. + +Arguments: + NotificationPointer - Interface change notification + Context - CAdapterCommon ptr. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[EvtBthHfpScoBypassInterfaceChange]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + CAdapterCommon * This = NULL; + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) NotificationPointer; + + // + // Make sure this is the interface class we extect. Any other class guid + // is an error, but let it go since it is not fatal to the machine. + // + if (!IsEqualGUID(Notification->InterfaceClassGuid, GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS)) + { + DPF(D_VERBOSE, ("EvtBthHfpScoBypassInterfaceChange: bad interface ClassGuid")); + ASSERTMSG("EvtBthHfpScoBypassInterfaceChange: bad interface ClassGuid ", FALSE); + + goto Done; + } + + This = (CAdapterCommon *)Context; + ASSERT(This != NULL); + _Analysis_assume_(This != NULL); + + // + // Take action based on the event. Any other event type is an error, + // but let it go since it is not fatal to the machine. + // + if (IsEqualGUID(Notification->Event, GUID_DEVICE_INTERFACE_ARRIVAL)) + { + ntStatus = This->BthHfpScoInterfaceArrival(Notification->SymbolicLinkName); + } + else if (IsEqualGUID(Notification->Event, GUID_DEVICE_INTERFACE_REMOVAL)) + { + ntStatus = This->BthHfpScoInterfaceRemoval(Notification->SymbolicLinkName); + } + else + { + DPF(D_VERBOSE, ("EvtBthHfpScoBypassInterfaceChange: bad " + "GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS event")); + ASSERTMSG("EvtBthHfpScoBypassInterfaceChange: bad " + "GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS event ", FALSE); + + goto Done; + } + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::InitBthScoBypass() +/*++ + +Routine Description: + + Initialize the bluetooth bypass environment. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::InitBluetoothBypass]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDF_WORKITEM_CONFIG wiConfig; + WDF_OBJECT_ATTRIBUTES attributes; + BthHfpWorkItemContext * wiContext; + + // + // Init spin-lock, linked lists, work-item, event, etc. + // Init all members to default values. This basic init should not fail. + // + m_BthHfpWorkItem = NULL; + m_BthHfpScoNotificationHandle = NULL; + ExInitializeFastMutex(&m_BthHfpFastMutex); + InitializeListHead(&m_BthHfpWorkTasks); + InitializeListHead(&m_BthHfpDevices); + m_BhtHfpWorkTaskPoolElementSize = sizeof(BthHfpWorkTask); + ExInitializeNPagedLookasideList(&m_BhtHfpWorkTaskPool, + NULL, + NULL, + POOL_NX_ALLOCATION, + m_BhtHfpWorkTaskPoolElementSize, + MINADAPTER_POOLTAG, + 0); + // + // Enable Bluetooth HFP SCO-Bypass Cleanup. + // Do any allocation/initialization that can fail after this point. + // + m_BthHfpEnableCleanup = TRUE; + + // + // Allocate a WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtBthHfpScoBypassInterfaceWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpWorkItemContext); + attributes.ParentObject = GetWdfDevice(); + ntStatus = WdfWorkItemCreate( &wiConfig, + &attributes, + &m_BthHfpWorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitBthScoBypass: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiContext = GetBthHfpWorkItemContext(m_BthHfpWorkItem); + wiContext->Adapter = this; // weak ref. + + // + // Register for bluetooth heandsfree profile interface changes. + // + ntStatus = IoRegisterPlugPlayNotification ( + EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS, + m_pDeviceObject->DriverObject, + EvtBthHfpScoBypassInterfaceChange, + (PVOID)this, + &m_BthHfpScoNotificationHandle); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitBthScoBypass: IoRegisterPlugPlayNotification(GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS) failed: 0x%x", ntStatus)), + Done); + + // + // Initialization completed. + // + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CAdapterCommon::CleanupBthScoBypass() +/*++ + +Routine Description: + + Cleanup the bluetooth bypass environment. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CleanupBthScoBypass]")); + + // + // Do nothing if Bluetooth HFP environment was not correctly initialized. + // + if (m_BthHfpEnableCleanup == FALSE) + { + return; + } + + // + // Unregister for bluetooth heandsfree profile interface changes. + // + if (m_BthHfpScoNotificationHandle != NULL) + { + (void)IoUnregisterPlugPlayNotificationEx(m_BthHfpScoNotificationHandle); + m_BthHfpScoNotificationHandle = NULL; + } + + // + // Wait for the Bluetooth hands-free profile worker thread to be done. + // + if (m_BthHfpWorkItem != NULL) + { + WdfWorkItemFlush(m_BthHfpWorkItem); + WdfObjectDelete(m_BthHfpWorkItem); + m_BthHfpWorkItem = NULL; + } + + ASSERT(IsListEmpty(&m_BthHfpWorkTasks)); + + // + // Stop and delete all BthHfpDevices. We are the only thread accessing this list, + // so there is no need to acquire the mutex. + // + while (!IsListEmpty(&m_BthHfpDevices)) + { + BthHfpDevice * bthDevice = NULL; + PLIST_ENTRY le = NULL; + + le = RemoveHeadList(&m_BthHfpDevices); + + bthDevice = BthHfpDevice::GetBthHfpDevice(le); + InitializeListHead(le); + + // bthDevice is invalid after this call. + bthDevice->Stop(); + + // This should be the last reference. + bthDevice->Release(); + } + + ASSERT(IsListEmpty(&m_BthHfpDevices)); + + // + // General cleanup. + // + ExDeleteNPagedLookasideList(&m_BhtHfpWorkTaskPool); +} +#endif // SYSVAD_BTH_BYPASS + +#ifdef SYSVAD_BTH_BYPASS +// +// BthHfpDevice implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +BthHfpDevice::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface routine for BthHfpDevice + +Arguments: + + Interface - + + Object - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PBTHHFPDEVICECOMMON(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IBthHfpDeviceCommon)) + { + *Object = PVOID(PBTHHFPDEVICECOMMON(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +// Dummy stubs to override the default WDF behavior of closing the target +// on query remove. This driver closes and deletes the supporting objects +// when the target removes the BTH HFP SCO Bypass interface. +// + +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::EvtBthHfpTargetQueryRemove +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetQueryRemove]")); + + UNREFERENCED_PARAMETER(IoTarget); + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +VOID +BthHfpDevice::EvtBthHfpTargetRemoveCanceled +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetRemoveCanceled]")); + + UNREFERENCED_PARAMETER(IoTarget); +} +#pragma code_seg("PAGE") +VOID +BthHfpDevice::EvtBthHfpTargetRemoveComplete +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetRemoveComplete]")); + + UNREFERENCED_PARAMETER(IoTarget); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Init +( + _In_ CAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Init]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationWorkItemContext *wiCtx = NULL; + BthHfpDeviceNotificationReqContext *reqCtx = NULL; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_IO_TARGET_OPEN_PARAMS openParams; + WDF_WORKITEM_CONFIG wiConfig; + + AddRef(); // first ref. + + // + // Basic init of all the class' members. + // + m_State = eBthHfpStateInitializing; + m_Adapter = Adapter; + + // Static config. + m_WdfIoTarget = NULL; + m_SpeakerMiniports = NULL; + m_MicMiniports = NULL; + m_UnknownSpeakerTopology = NULL; + m_UnknownSpeakerWave = NULL; + m_UnknownMicTopology = NULL; + m_UnknownMicWave = NULL; + m_Descriptor = NULL; + m_VolumePropValues = NULL; + + // Notification updates. + m_SpeakerVolumeLevel = 0; + m_MicVolumeLevel = 0; + m_ConnectionStatusLong = FALSE; + m_StreamStatusLong = STATUS_INVALID_DEVICE_STATE; // Sco stream is not open. + m_NRECDisableStatusLong = FALSE; + + m_StreamReq = NULL; + m_SpeakerVolumeReq = NULL; + m_MicVolumeReq = NULL; + m_ConnectionReq = NULL; + m_NRECDisableStatusReq = NULL; + + m_WorkItem = NULL; + m_ReqCollection = NULL; + + m_nStreams = 0; + + KeInitializeEvent(&m_StreamStatusEvent, NotificationEvent, TRUE); + + InitializeListHead(&m_ListEntry); + KeInitializeSpinLock(&m_Lock); + + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + + RtlZeroMemory(&m_SpeakerVolumeCallback, sizeof(m_SpeakerVolumeCallback)); + RtlZeroMemory(&m_SpeakerConnectionStatusCallback, sizeof(m_SpeakerConnectionStatusCallback)); + RtlZeroMemory(&m_MicVolumeCallback, sizeof(m_MicVolumeCallback)); + RtlZeroMemory(&m_MicConnectionStatusCallback, sizeof(m_MicConnectionStatusCallback)); + + // + // Allocate a notification WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtBthHfpDeviceNotificationStatusWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationWorkItemContext); + attributes.ParentObject = Adapter->GetWdfDevice(); + ntStatus = WdfWorkItemCreate( &wiConfig, + &attributes, + &m_WorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiCtx = GetBthHfpDeviceNotificationWorkItemContext(m_WorkItem); + wiCtx->BthHfpDevice = this; // weak ref. + + // + // Allocate a collection to hold notification requests for the notification work-item. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfCollectionCreate( + &attributes, + &m_ReqCollection); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfCollectionCreate failed: 0x%x", ntStatus)), + Done); + + // + // Open the target interface. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfIoTargetCreate(m_Adapter->GetWdfDevice(), + &attributes, + &m_WdfIoTarget); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfIoTargetCreate failed: 0x%x", ntStatus)), + Done); + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + SymbolicLinkName, + STANDARD_RIGHTS_ALL); + + openParams.EvtIoTargetQueryRemove = EvtBthHfpTargetQueryRemove; + openParams.EvtIoTargetRemoveCanceled = EvtBthHfpTargetRemoveCanceled; + openParams.EvtIoTargetRemoveComplete = EvtBthHfpTargetRemoveComplete; + + ntStatus = WdfIoTargetOpen(m_WdfIoTarget, &openParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfIoTargetOpen(%wZ) failed: 0x%x", SymbolicLinkName, ntStatus)), + Done); + + // + // Make a copy of the symbolic link list. + // + m_SymbolicLinkName.MaximumLength = SymbolicLinkName->MaximumLength; + m_SymbolicLinkName.Length = SymbolicLinkName->Length; + m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePoolWithTag(NonPagedPoolNx, + SymbolicLinkName->MaximumLength, + MINADAPTER_POOLTAG); + if (m_SymbolicLinkName.Buffer == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: ExAllocatePoolWithTag failed, out of memory")), + Done); + + RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName); + + // + // Allocate the WDF requests for status notifications. + // + + // + // IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_NRECDisableStatusReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Nrec-disable status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_NRECDisableStatusReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.BoolStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Speaker-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_MicVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Mic-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_ConnectionReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Connection-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.BoolStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_StreamReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Stream-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.NtStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // This remote device is now in running state. No need to use interlock operations + // b/c at this time this is the only thread accessing this info. + // + m_State = eBthHfpStateRunning; + + // + // Init successful. + // + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +BthHfpDevice::~BthHfpDevice +( + void +) +/*++ + +Routine Description: + + Destructor for BthHfpDevice. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::~BthHfpDevice]")); + + ASSERT(m_State != eBthHfpStateRunning); + ASSERT(IsListEmpty(&m_ListEntry)); + + // + // Release ref to remote stack. + // + if (m_WdfIoTarget != NULL) + { + WdfObjectDelete(m_WdfIoTarget); + m_WdfIoTarget = NULL; + } + + // + // Free symbolic links. + // + if (m_SymbolicLinkName.Buffer != NULL) + { + ExFreePoolWithTag(m_SymbolicLinkName.Buffer, MINADAPTER_POOLTAG); + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + } + + DeleteCustomEndpointMinipair(m_SpeakerMiniports); + m_SpeakerMiniports = NULL; + + DeleteCustomEndpointMinipair(m_MicMiniports); + m_MicMiniports = NULL; + + if (m_Descriptor != NULL) + { + ExFreePoolWithTag(m_Descriptor, MINADAPTER_POOLTAG); + m_Descriptor = NULL; + } + + if (m_VolumePropValues != NULL) + { + ExFreePoolWithTag(m_VolumePropValues, MINADAPTER_POOLTAG); + m_VolumePropValues = NULL; + } + + // + // Free Irps. + // + if (m_SpeakerVolumeReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_SpeakerVolumeReq); + m_SpeakerVolumeReq = NULL; + } + + if (m_MicVolumeReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_MicVolumeReq); + m_MicVolumeReq = NULL; + } + + if (m_ConnectionReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_ConnectionReq); + m_ConnectionReq = NULL; + } + + if (m_StreamReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_StreamReq); + m_StreamReq = NULL; + } + + // + // Notification work-item. + // + if (m_WorkItem != NULL) + { + WdfObjectDelete(m_WorkItem); + m_WorkItem = NULL; + } + + // + // Notification req. collection. + // + if (m_ReqCollection != NULL) + { + WdfObjectDelete(m_ReqCollection); + m_ReqCollection = NULL; + } + + ASSERT(m_UnknownSpeakerTopology == NULL); + SAFE_RELEASE(m_UnknownSpeakerTopology); + + ASSERT(m_UnknownSpeakerWave == NULL); + SAFE_RELEASE(m_UnknownSpeakerWave); + + ASSERT(m_UnknownMicTopology == NULL); + SAFE_RELEASE(m_UnknownMicTopology); + + ASSERT(m_UnknownMicWave == NULL); + SAFE_RELEASE(m_UnknownMicWave); + + ASSERT(m_nStreams == 0); + + ASSERT(m_SpeakerVolumeCallback.Handler == NULL); + ASSERT(m_SpeakerConnectionStatusCallback.Handler == NULL); + ASSERT(m_MicVolumeCallback.Handler == NULL); + ASSERT(m_MicConnectionStatusCallback.Handler == NULL); +} // ~CAdapterCommon + +// +// IBthHfpDeviceCommon implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::IsVolumeSupported() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::IsVolumeSupported]")); + + return m_Descriptor->SupportsVolume; +} + +//============================================================================= +#pragma code_seg("PAGE") +PKSPROPERTY_VALUES +BthHfpDevice::GetVolumeSettings +( + _Out_ PULONG Size +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetVolumeSettings]")); + + ASSERT(Size != NULL); + + *Size = m_Descriptor->VolumePropertyValuesSize; + + return m_VolumePropValues; +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +BthHfpDevice::GetSpeakerVolume() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetSpeakerVolume]")); + + return InterlockedCompareExchange(&m_SpeakerVolumeLevel, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetSpeakerVolume +( + _In_ ULONG Volume +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerVolume]")); + + return SetBthHfpSpeakerVolume(Volume); +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +BthHfpDevice::GetMicVolume() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetMicVolume]")); + + return InterlockedCompareExchange(&m_MicVolumeLevel, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetMicVolume +( + _In_ ULONG Volume +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicVolume]")); + + return SetBthHfpMicVolume(Volume); +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::GetConnectionStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetConnectionStatus]")); + + return (BOOL)InterlockedCompareExchange(&m_ConnectionStatusLong, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpCodecId(_Out_ UCHAR * CodecId) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpCodecId]")); + + ASSERT(CodecId != NULL); + + typedef enum _HFP_BYPASS_CODEC_ID_VERSION { + REQ_HFP_BYPASS_CODEC_ID_V1 = 1, + } HFP_BYPASS_CODEC_ID_VERSION, *PHFP_BYPASS_CODEC_ID_VERSION; + + typedef struct _HFP_BYPASS_CODEC_ID_V1 { + UCHAR CodecId; + } HFP_BYPASS_CODEC_ID_V1, *PHFP_BYPASS_CODEC_ID_V1; + + NTSTATUS ntStatus = STATUS_SUCCESS; + + union { + HFP_BYPASS_CODEC_ID_V1 CodecIdV1; + HFP_BYPASS_CODEC_ID_VERSION Version; + } value; + + *CodecId = 0; + + value.Version = REQ_HFP_BYPASS_CODEC_ID_V1; + + // + // Get the Bth HFP SCO Codec ID. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_CODEC_ID, + sizeof(value.Version), + sizeof(value.CodecIdV1), + &value); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpCodecId: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_CODEC_ID) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *CodecId = value.CodecIdV1.CodecId; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Connect() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Connect]")); + + return SetBthHfpConnect(); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Disconnect() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Disconnect]")); + + return SetBthHfpDisconnect(); +} + +//============================================================================= +#pragma code_seg() +BOOL +BthHfpDevice::GetStreamStatus() +{ + DPF_ENTER(("[BthHfpDevice::GetStreamStatus]")); + + NTSTATUS ntStatus; + + ntStatus = (NTSTATUS)InterlockedCompareExchange(&m_StreamStatusLong, 0, 0); + + return NT_SUCCESS(ntStatus) ? TRUE : FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamOpen() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StreamOpen]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nStreams >= 0); + + nStreams = InterlockedIncrement(&m_nStreams); + if (nStreams == 1) + { + BOOLEAN streamOpen = FALSE; + + ntStatus = SetBthHfpStreamOpen(); + if (NT_SUCCESS(ntStatus)) + { + streamOpen = TRUE; + m_StreamStatus = STATUS_SUCCESS; + ntStatus = EnableBthHfpStreamStatusNotification(); + } + + // + // Cleanup if any error. + // + if (!NT_SUCCESS(ntStatus)) + { + nStreams = InterlockedDecrement(&m_nStreams); + ASSERT(nStreams == 0); + UNREFERENCED_VAR(nStreams); + + if (streamOpen) + { + SetBthHfpStreamClose(); + } + + m_StreamStatus = STATUS_INVALID_DEVICE_STATE; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamClose() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StreamClose]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nStreams > 0); + + nStreams = InterlockedDecrement(&m_nStreams); + if (nStreams == 0) + { + ntStatus = SetBthHfpStreamClose(); + + StopBthHfpStreamStatusNotification(); + + m_StreamStatus = STATUS_INVALID_DEVICE_STATE; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +GUID +BthHfpDevice::GetContainerId() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetContainerId]")); + + return m_Descriptor->ContainerId; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetSpeakerVolumeHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) + { + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerVolumeHandler]")); + + ASSERT(EventHandler == NULL || m_SpeakerVolumeCallback.Handler == NULL); + + m_SpeakerVolumeCallback.Handler = EventHandler; // weak ref. + m_SpeakerVolumeCallback.Context = EventHandlerContext; + } + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetSpeakerConnectionStatusHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerConnectionStatusHandler]")); + + ASSERT(EventHandler == NULL || m_SpeakerConnectionStatusCallback.Handler == NULL); + + m_SpeakerConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_SpeakerConnectionStatusCallback.Context = EventHandlerContext; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetMicVolumeHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicVolumeHandler]")); + + ASSERT(EventHandler == NULL || m_MicVolumeCallback.Handler == NULL); + + m_MicVolumeCallback.Handler = EventHandler; // weak ref. + m_MicVolumeCallback.Context = EventHandlerContext; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetMicConnectionStatusHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicConnectionStatusHandler]")); + + ASSERT(EventHandler == NULL || m_MicConnectionStatusCallback.Handler == NULL); + + m_MicConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_MicConnectionStatusCallback.Context = EventHandlerContext; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::IsNRECSupported() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::IsNRECSupported]")); + + return m_Descriptor->SupportsNREC; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::GetNRECDisableStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetNRECDisableStatus]")); + + // Return TRUE if HF wants to disable the NREC on the AG. + return (BOOL)InterlockedCompareExchange(&m_NRECDisableStatusLong, 0, 0); +} + +// +// Helper functions. +// + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::SendIoCtrlAsynchronously +( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_opt_ WDFMEMORY MemIn, + _In_opt_ WDFMEMORY MemOut, + _In_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine, + _In_ WDFCONTEXT Context +) +/*++ + +Routine Description: + + This function aynchronously sends an I/O Ctrl request to the BTH HFP + SCO Bypass device. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::SendIoCtrlAsynchronously]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOLEAN fSent = FALSE; + + // + // Format and send the request. + // + ntStatus = WdfIoTargetFormatRequestForIoctl( + m_WdfIoTarget, + Request, + IoControlCode, + MemIn, + NULL, + MemOut, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SendIoCtrlAsynchronously: WdfIoTargetFormatRequestForIoctl(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + + WdfRequestSetCompletionRoutine( + Request, + CompletionRoutine, + Context); + + fSent = WdfRequestSend(Request, m_WdfIoTarget, NULL); // no options. + if (fSent == FALSE) + { + ntStatus = WdfRequestGetStatus(Request); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("SendIoCtrlAsynchronously: WdfRequestSend(0x%x) failed, 0x%x", IoControlCode, ntStatus)); + goto Done; + } + + // + // All Done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SendIoCtrlSynchronously +( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0 || OutLength > 0, _In_) + _When_(InLength == 0 && OutLength == 0, _In_opt_) + PVOID Buffer +) +/*++ + +Routine Description: + + This function inits and synchronously sends an I/O Ctrl request to the BTH HFP + SCO Bypass device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SendIoCtrlSynchronously]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PWDF_MEMORY_DESCRIPTOR memInPtr = NULL; + PWDF_MEMORY_DESCRIPTOR memOutPtr = NULL; + WDF_MEMORY_DESCRIPTOR memIn; + WDF_MEMORY_DESCRIPTOR memOut; + WDF_REQUEST_SEND_OPTIONS reqOpts; + + // + // Format and send the request. + // + if (InLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn, Buffer, InLength); + memInPtr = &memIn; + } + + if (OutLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, Buffer, OutLength); + memOutPtr = &memOut; + } + + WDF_REQUEST_SEND_OPTIONS_INIT( + &reqOpts, + WDF_REQUEST_SEND_OPTION_TIMEOUT | + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(BTH_HFP_SYNC_REQ_TIMEOUT_IN_SEC); + + ntStatus = WdfIoTargetSendIoctlSynchronously( + m_WdfIoTarget, + Request, + IoControlCode, + memInPtr, + memOutPtr, + &reqOpts, + NULL); // bytes returned. + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_VERBOSE, ("SendIoCtrlSynchronously: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +VOID +BthHfpDevice::EvtBthHfpDeviceNotificationStatusWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + The function processes status notification updates. + +Arguments: + + WorkItem - WDF work-item object. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceNotificationStatusWorkItem]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * This; + KIRQL oldIrql; + + if (WorkItem == NULL) + { + return; + } + + This = GetBthHfpDeviceNotificationWorkItemContext(WorkItem)->BthHfpDevice; + ASSERT(This != NULL); + + for (;;) + { + BOOL resend = TRUE; + WDFREQUEST req = NULL; + BthHfpDeviceNotificationReqContext * reqCtx; + WDF_REQUEST_COMPLETION_PARAMS params; + + // + // Retrieve a task. + // + KeAcquireSpinLock(&This->m_Lock, &oldIrql); + req = (WDFREQUEST) WdfCollectionGetFirstItem(This->m_ReqCollection); + if (req != NULL) + { + WdfCollectionRemove(This->m_ReqCollection, req); + } + KeReleaseSpinLock(&This->m_Lock, oldIrql); + + if (req == NULL) + { + break; + } + + // + // Get request parameters and context. + // + WDF_REQUEST_COMPLETION_PARAMS_INIT(¶ms); + WdfRequestGetCompletionParams(req, ¶ms); + + reqCtx = GetBthHfpDeviceNotificationReqContext(req); + ASSERT(reqCtx != NULL); + + // + // Handle this notification. + // + if (NT_SUCCESS(params.IoStatus.Status)) + { + switch(params.Parameters.Ioctl.IoControlCode) + { + case IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE: + { + InterlockedExchange(&This->m_NRECDisableStatusLong, (LONG)reqCtx->Buffer.BoolStatus); + } + break; + + case IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE: + { + LONG oldVolume; + + oldVolume = InterlockedExchange(&This->m_SpeakerVolumeLevel, reqCtx->Buffer.Volume); + if (reqCtx->Buffer.Volume != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_SpeakerVolumeCallback.Handler != NULL) + { + This->m_SpeakerVolumeCallback.Handler( + This->m_SpeakerVolumeCallback.Context); + } + } + } + break; + + case IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE: + { + LONG oldVolume; + + oldVolume = InterlockedExchange(&This->m_MicVolumeLevel, reqCtx->Buffer.Volume); + if (reqCtx->Buffer.Volume != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_MicVolumeCallback.Handler != NULL) + { + This->m_MicVolumeCallback.Handler( + This->m_MicVolumeCallback.Context); + } + } + } + break; + + case IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE: + { + BOOL oldStatus; + + oldStatus = (BOOL)InterlockedExchange(&This->m_ConnectionStatusLong, (LONG)reqCtx->Buffer.BoolStatus); + if (reqCtx->Buffer.BoolStatus != oldStatus) + { + // Notify audio miniport about this change. + if (This->m_SpeakerConnectionStatusCallback.Handler != NULL) + { + This->m_SpeakerConnectionStatusCallback.Handler( + This->m_SpeakerConnectionStatusCallback.Context); + } + + if (This->m_MicConnectionStatusCallback.Handler != NULL) + { + This->m_MicConnectionStatusCallback.Handler( + This->m_MicConnectionStatusCallback.Context); + } + } + } + break; + + default: + // This should never happen. + resend = FALSE; + DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: invalid request ctrl 0x%x", + params.Parameters.Ioctl.IoControlCode)); + break; + } + } + + if (resend) + { + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: WdfRequestReuse failed, 0x%x", ntStatus)); + break; + } + + // Resend status notification request. + reqCtx->Buffer.bImmediate = FALSE; + + ntStatus = This->SendIoCtrlAsynchronously( + req, + params.Parameters.Ioctl.IoControlCode, + reqCtx->MemIn, + reqCtx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + This); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: SendIoCtrlAsynchronously" + "(0x%x) failed, 0x%x", + params.Parameters.Ioctl.IoControlCode, ntStatus)); + break; + } + } + } +} + +//============================================================================= +#pragma code_seg() +void +BthHfpDevice::EvtBthHfpDeviceNotificationStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceNotificationStatusCompletion]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + BthHfpDevice * This = NULL; + KIRQL oldIrql; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + ctx = GetBthHfpDeviceNotificationReqContext(Request); + This = ctx->BthHfpDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (ntStatus == STATUS_CANCELLED) + { + // BTH HFP device is shutting down. Do not re-send this request. + goto Done; + } + + // + // If something is wrong with the HFP interface, do not loop forever. + // + if (!NT_SUCCESS(ntStatus)) + { + if (++ctx->Errors > BTH_HFP_NOTIFICATION_MAX_ERROR_COUNT) + { + // Too many errors. Do not re-send this request. + goto Done; + } + } + else + { + // reset the # of errors. + ctx->Errors = 0; + } + + // + // Let the work-item thread process this request. + // + KeAcquireSpinLock(&This->m_Lock, &oldIrql); + + ntStatus = WdfCollectionAdd(This->m_ReqCollection, Request); + if (NT_SUCCESS(ntStatus)) + { + WdfWorkItemEnqueue(This->m_WorkItem); + } + + KeReleaseSpinLock(&This->m_Lock, oldIrql); + +Done:; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpDescriptor +( + _Out_ PBTHHFP_DESCRIPTOR2 * Descriptor +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass descriptor. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpDescriptor]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PBTHHFP_DESCRIPTOR2 descriptor = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + + *Descriptor = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2, + NULL, + NULL, + NULL); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("GetBthHfpDescriptor: IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2 buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + descriptor = (PBTHHFP_DESCRIPTOR2) ExAllocatePoolWithTag(NonPagedPoolNx, length, MINADAPTER_POOLTAG); + if (descriptor == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the Bth HFP SCO Bypass descriptor. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2, + NULL, + length, + descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Descriptor = descriptor; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (descriptor != NULL) + { + ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpNrecDisableStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass NREC-Disable + status change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpNrecDisableStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_NRECDisableStatusReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Get the Bth HFP SCO Bypass NREC-Disable status (async). + // + ntStatus = SendIoCtrlAsynchronously( + m_NRECDisableStatusReq, + IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpNrecDisableStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpVolumePropertyValues +( + _In_ ULONG Length, + _Out_ PKSPROPERTY_VALUES * PropValues +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass volume values. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpVolumePropertyValues]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PKSPROPERTY_VALUES propValues = NULL; + + *PropValues = NULL; + + // + // Allocate memory. + // + propValues = (PKSPROPERTY_VALUES) ExAllocatePoolWithTag(NonPagedPoolNx, Length, MINADAPTER_POOLTAG); + if (propValues == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the Bth HFP SCO Bypass descriptor. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_VOLUMEPROPERTYVALUES, + 0, + Length, + propValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_VOLUMEPROPERTYVALUES) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *PropValues = propValues; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (propValues != NULL) + { + ExFreePoolWithTag(propValues, MINADAPTER_POOLTAG); + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpSpeakerVolume +( + _In_ LONG Volume +) +/*++ + +Routine Description: + + This function synchronously sets the remote Bluetooth Hands-Free Profile SCO + Bypass speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpSpeakerVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // Get the Bth HFP SCO Bypass speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_SPEAKER_SET_VOLUME, + sizeof(Volume), + 0, + &Volume); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpSpeakerVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_SPEAKER_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpSpeakerVolume +( + _Out_ LONG * Volume +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpSpeakerVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationBuffer buffer = {0}; + + *Volume = 0; + + buffer.bImmediate = TRUE; + + // + // Get the Bth HFP SCO Bypass speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.bImmediate), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpSpeakerVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpSpeakerVolumeStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass speaker + volume change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpSpeakerVolumeStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Register for speaker volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerVolumeReq, + IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpSpeakerVolumeStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpMicVolume +( + _In_ LONG Volume +) +/*++ + +Routine Description: + + This function synchronously sets the remote Bluetooth Hands-Free Profile SCO + Bypass mic volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpMicVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // Get the Bth HFP SCO Bypass mic volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_MIC_SET_VOLUME, + sizeof(Volume), + 0, + &Volume); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpMicVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_MIC_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpMicVolume +( + _Out_ LONG * Volume +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass mic volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpMicVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationBuffer buffer = {0}; + + *Volume = 0; + + buffer.bImmediate = TRUE; + + // + // Get the Bth HFP SCO Bypass mic volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.bImmediate), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpMicVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpMicVolumeStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass mic + volume change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpMicVolumeStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Register for mic volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_MicVolumeReq, + IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpMicVolumeStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpConnectionStatus +( + _Out_ BOOL * ConnectionStatus +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass connection status. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpConnectionStatus]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOL bValue = TRUE; // In: bImmediate, Out: value. + + *ConnectionStatus = 0; + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE, + sizeof(bValue), + sizeof(bValue), + &bValue); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpConnectionStatus: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *ConnectionStatus = bValue; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpConnectionStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass + connection status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpConnectionStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq); + + // + // Make sure this obj is alive while the IRP is active. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlAsynchronously( + m_ConnectionReq, + IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpConnectionStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpConnect() +/*++ + +Routine Description: + + This function synchronously requests a Bluetooth Hands-Free Profile level + connection to the paired Bluetooth device. + + This request initiates the Service Level Connection establishment procedure + and completes without waiting for the connection procedure to complete. + Connection status can be determined using IOCTL_BTHHFP_GET_CONNECTION_STATUS_UPDATE. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpConnect]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_REQUEST_CONNECT, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpConnect: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_REQUEST_CONNECT) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpDisconnect() +/*++ + +Routine Description: + + This function synchronously requests a Bluetooth Hands-Free Profile level + connection to the paired Bluetooth device. + + This request initiates disconnection of the Service Level Connection and + completes without waiting for the disconnection to complete. Connection + status can be determined using IOCTL_BTHHFP_GET_CONNECTION_STATUS_UPDATE. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpDisconnect]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_REQUEST_DISCONNECT, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpDisconnect: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_REQUEST_DISCONNECT) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +void +BthHfpDevice::EvtBthHfpDeviceStreamStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +/*++ + +Routine Description: + + Completion callback for the Bluetooth Hands-Free Profile SCO Bypass + stream status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceStreamStatusCompletion]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * reqCtx = NULL; + BthHfpDevice * This = NULL; + NTSTATUS ntResult = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + // + // Get the SCO stream status. + // + reqCtx = GetBthHfpDeviceNotificationReqContext(Request); + This = reqCtx->BthHfpDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (!NT_SUCCESS(ntStatus)) + { + ntResult = STATUS_INVALID_DEVICE_STATE; + } + else + { + ntResult = reqCtx->Buffer.NtStatus; + } + + InterlockedExchange(&This->m_StreamStatusLong, (LONG)ntResult); + + // + // Let the stop routine know we are done. Stop routine will + // re-init the request. + // + KeSetEvent(&This->m_StreamStatusEvent, IO_NO_INCREMENT, FALSE); +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpStreamStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass + stream status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpStreamStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ASSERT(m_nStreams > 0); + + ctx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + ctx->Buffer.bImmediate = FALSE; + + KeClearEvent(&m_StreamStatusEvent); + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlAsynchronously( + m_StreamReq, + IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceStreamStatusCompletion, + this); + + if (!NT_SUCCESS(ntStatus)) + { + KeSetEvent(&m_StreamStatusEvent, IO_NO_INCREMENT, FALSE); + DPF(D_ERROR, ("EnableBthHfpStreamStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE) failed, 0x%x", ntStatus)); + goto Done; + } + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StopBthHfpStreamStatusNotification() +/*++ + +Routine Description: + + This function stops the Bluetooth Hands-Free Profile SCO Bypass + connection status notification. + The function waits for the request to be done before returning. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StopBthHfpStreamStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * reqCtx = NULL; + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WdfRequestCancelSentRequest(m_StreamReq); + KeWaitForSingleObject(&m_StreamStatusEvent, Executive, KernelMode, FALSE, NULL); + + reqCtx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + ASSERT(reqCtx != NULL); + UNREFERENCED_VAR(reqCtx); + + // + // Re-init the request for later. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(m_StreamReq, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("StopBthHfpStreamStatusNotification: WdfRequestReuse failed, 0x%x", ntStatus)); + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpStreamOpen() +/*++ + +Routine Description: + + This function synchronously requests an open SCO channel to transmit audio + data over the air. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpStreamOpen]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_STREAM_OPEN, + 0, + 0, + NULL); + + if (ntStatus == STATUS_DEVICE_BUSY) + { + // The stream channel is already open. + DPF(D_VERBOSE, ("SetBthHfpStreamOpen: the stream channel is already open")); + ntStatus = STATUS_SUCCESS; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpStreamOpen: SendIoCtrlSynchronously(IOCTL_BTHHFP_STREAM_OPEN) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpStreamClose() +/*++ + +Routine Description: + + This function synchronously requests to close the SCO channel. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpStreamClose]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_STREAM_CLOSE, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpStreamClose: SendIoCtrlSynchronously(IOCTL_BTHHFP_STREAM_CLOSE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::Start() +/*++ + +Routine Description: + + Asynchronously called to start the audio device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Start]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOL connStatus = FALSE; + UCHAR codecId = 0; + UINT bthMiniportsIndex = 0; + + // CVSD is the narrow band codec for SCO. Wideband codec IDs are any number higher than 1. + // mSBC is the only required wideband codec, though the controller+headset combination may + // support other wideband codecs. + const UCHAR CODEC_CVSD = 1; + + // + // Get bth hfp descriptor + // + ntStatus = GetBthHfpDescriptor(&m_Descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpDescriptor: failed to retrieve BTHHFP_DESCRIPTOR2, 0x%x", ntStatus)), + Done); + + // + // Get valume settings. + // + if (m_Descriptor->SupportsVolume) + { + PKSPROPERTY_VALUES volumePropValues = NULL; + LONG volume = 0; + + // Volume settings. + ntStatus = GetBthHfpVolumePropertyValues( + m_Descriptor->VolumePropertyValuesSize, + &volumePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpVolumePropertyValues: failed to retrieve KSPROPERTY_VALUES, 0x%x", ntStatus)), + Done); + + m_VolumePropValues = volumePropValues; + + // Speaker volume. + ntStatus = GetBthHfpSpeakerVolume(&volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpSpeakerVolume: failed, 0x%x", ntStatus)), + Done); + + m_SpeakerVolumeLevel = volume; + + // Mic volume. + ntStatus = GetBthHfpMicVolume(&volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpMicVolume: failed, 0x%x", ntStatus)), + Done); + + m_MicVolumeLevel = volume; + } + + // + // Get connection status. + // + ntStatus = GetBthHfpConnectionStatus(&connStatus); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpConnectionStatus: failed, 0x%x", ntStatus)), + Done); + + m_ConnectionStatus = connStatus; + + // + // Get codec id (if non-zero, connection supports Wideband Speech) + // + ntStatus = GetBthHfpCodecId(&codecId); + if (ntStatus == STATUS_INVALID_DEVICE_REQUEST) + { + // GetBthHfpCodecId fails with STATUS_INVALID_DEVICE_REQUEST if the system doesn't + // support Wideband Speech (currently only Mobile supports this call) + ntStatus = STATUS_SUCCESS; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpCodecId: failed, 0x%x", ntStatus)), + Done); + + if (codecId > CODEC_CVSD) + { + // Use the miniport tables that support 16kHz + bthMiniportsIndex = 1; + } + + // + // Customize the topology/wave descriptors for this instance + // + ntStatus = CreateCustomEndpointMinipair( + g_BthHfpRenderEndpoints[bthMiniportsIndex], + &m_Descriptor->FriendlyName, + &m_Descriptor->OutputPinCategory, + &m_SpeakerMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: CreateCustomEndpointMinipair for Render: failed, 0x%x", ntStatus)), + Done); + + ntStatus = CreateCustomEndpointMinipair( + g_BthHfpCaptureEndpoints[bthMiniportsIndex], + &m_Descriptor->FriendlyName, + &m_Descriptor->InputPinCategory, + &m_MicMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: CreateCustomEndpointMinipair for Capture: failed, 0x%x", ntStatus)), + Done); + + ASSERT(m_SpeakerMiniports != NULL); + ASSERT(m_MicMiniports != NULL); + _Analysis_assume_(m_SpeakerMiniports != NULL); + _Analysis_assume_(m_MicMiniports != NULL); + + // + // Register topology and wave filters. + // + ntStatus = m_Adapter->InstallEndpointFilters( + NULL, + m_SpeakerMiniports, + PBTHHFPDEVICECOMMON(this), + &m_UnknownSpeakerTopology, + &m_UnknownSpeakerWave + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: InstallEndpointRenderFilters (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)), + Done); + + ntStatus = m_Adapter->InstallEndpointFilters( + NULL, + m_MicMiniports, + PBTHHFPDEVICECOMMON(this), + &m_UnknownMicTopology, + &m_UnknownMicWave + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: InstallEndpointCaptureFilters (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)), + Done); + + // + // Pend status notifications. + // + + // NREC disable AudioGateway (AG) status. + ntStatus = EnableBthHfpNrecDisableStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpNrecDisableStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Volume speaker status. + ntStatus = EnableBthHfpSpeakerVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpSpeakerVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Volume mic status. + ntStatus = EnableBthHfpMicVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpMicVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Connection status. + ntStatus = EnableBthHfpConnectionStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpConnectionStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done:; + if (!NT_SUCCESS(ntStatus)) + { + InterlockedExchange((PLONG)&m_State, eBthHfpStateFailed); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::CreateCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair +) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR pNewMinipair = NULL; + SYSVAD_DEVPROPERTY* pProperties = NULL; + PPCFILTER_DESCRIPTOR pNewTopoFilterDesc = NULL; + PPCPIN_DESCRIPTOR pNewTopoPins = NULL; + ULONG cProperties; + ULONG cTopoPins; + + PAGED_CODE(); + + // + // This routine will add one more property to whatever the base minipair describes for the topo filter interface properties + // It will also allocate and set up custom filter and pin descriptors to allow changing the KSNODETYPE for the hfp device + // + cTopoPins = pBaseMinipair->TopoDescriptor->PinCount; + cProperties = pBaseMinipair->TopoInterfacePropertyCount + 1; + pProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePoolWithTag(NonPagedPoolNx, cProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); + pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG); + pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG); + pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG); + + if ((pProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL)) + { + SYSVAD_DEVPROPERTY *pLastProperty; + + // Copy base minipair properties to new property list + if (pBaseMinipair->TopoInterfacePropertyCount > 0) + { + RtlCopyMemory(pProperties, pBaseMinipair->TopoInterfaceProperties, pBaseMinipair->TopoInterfacePropertyCount * sizeof(SYSVAD_DEVPROPERTY)); + } + + // Add friendly name property to the list + NT_ASSERT(FriendlyName->Length + sizeof(UNICODE_NULL) <= FriendlyName->MaximumLength); // Assuming NULL terminated string + pLastProperty = &pProperties[cProperties - 1]; + pLastProperty->PropertyKey = &DEVPKEY_DeviceInterface_FriendlyName; + pLastProperty->Type = DEVPROP_TYPE_STRING_INDIRECT; + pLastProperty->BufferSize = FriendlyName->Length + sizeof(UNICODE_NULL); + pLastProperty->Buffer = FriendlyName->Buffer; + + // Copy base minipair structure + RtlCopyMemory(pNewMinipair, pBaseMinipair, sizeof(ENDPOINT_MINIPAIR)); + + RtlCopyMemory(pNewTopoFilterDesc, pBaseMinipair->TopoDescriptor, sizeof(PCFILTER_DESCRIPTOR)); + RtlCopyMemory(pNewTopoPins, pBaseMinipair->TopoDescriptor->Pins, cTopoPins * sizeof(PCPIN_DESCRIPTOR)); + + pNewTopoFilterDesc->Pins = pNewTopoPins; + pNewMinipair->TopoDescriptor = pNewTopoFilterDesc; + + // Update it to point to new property list + pNewMinipair->TopoInterfacePropertyCount = cProperties; + pNewMinipair->TopoInterfaceProperties = pProperties; + + ntStatus = UpdateCustomEndpointCategory(pNewTopoFilterDesc, pNewTopoPins, pCategory); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("UpdateCustomEndpointCategory: failed, 0x%x", ntStatus)); + } + else + { + *ppCustomMinipair = pNewMinipair; + + pProperties = NULL; + pNewMinipair = NULL; + pNewTopoFilterDesc = NULL; + pNewTopoPins = NULL; + + ntStatus = STATUS_SUCCESS; + } + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (pProperties != NULL) + { + ExFreePoolWithTag(pProperties, SYSVAD_POOLTAG); + } + if (pNewMinipair != NULL) + { + ExFreePoolWithTag(pNewMinipair, SYSVAD_POOLTAG); + } + if (pNewTopoFilterDesc != NULL) + { + ExFreePoolWithTag(pNewTopoFilterDesc, SYSVAD_POOLTAG); + } + if (pNewTopoPins != NULL) + { + ExFreePoolWithTag(pNewTopoPins, SYSVAD_POOLTAG); + } + + return ntStatus; +} +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::UpdateCustomEndpointCategory +( + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory +) +{ + NTSTATUS ntStatus = STATUS_NOT_FOUND; + ULONG cPinCount = 0; + BOOL FoundCategoryAudio = FALSE; + BOOL FoundNodeType = FALSE; + + PAGED_CODE(); + + cPinCount = pCustomMinipairTopoFilter->PinCount; + + // Find the right pin: There should be two pins, one with Category KSCATEGORY_AUDIO, + // and one with a KSNODETYPE_* Category. We need to modify the KSNODETYPE category. + for (ULONG i = 0; i < cPinCount; ++i) + { + if (IsEqualGUID(*pCustomMinipairTopoPins[i].KsPinDescriptor.Category, KSCATEGORY_AUDIO)) + { + ASSERT(FoundCategoryAudio == FALSE); + if (FoundCategoryAudio) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("UpdateCustomEndpointCategory: KSCATEGORY_AUDIO found more than once, 0x%x", ntStatus)); + break; + } + + FoundCategoryAudio = TRUE; + continue; + } + + ASSERT(FoundNodeType == FALSE); + if (FoundNodeType) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("UpdateCustomEndpointCategory: Found more than one applicable Pin, 0x%x", ntStatus)); + break; + } + + pCustomMinipairTopoPins[i].KsPinDescriptor.Category = pCategory; + FoundNodeType = TRUE; + ntStatus = STATUS_SUCCESS; + } + + return ntStatus; +} +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::DeleteCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pCustomMinipair +) +{ + PAGED_CODE(); + + if (pCustomMinipair != NULL) + { + if (pCustomMinipair->TopoInterfaceProperties != NULL) + { + ExFreePoolWithTag(const_cast<SYSVAD_DEVPROPERTY*>(pCustomMinipair->TopoInterfaceProperties), SYSVAD_POOLTAG); + pCustomMinipair->TopoInterfaceProperties = NULL; + } + if (pCustomMinipair->TopoDescriptor != NULL) + { + if (pCustomMinipair->TopoDescriptor->Pins != NULL) + { + ExFreePoolWithTag((PVOID)pCustomMinipair->TopoDescriptor->Pins, SYSVAD_POOLTAG); + pCustomMinipair->TopoDescriptor->Pins = NULL; + } + ExFreePoolWithTag(pCustomMinipair->TopoDescriptor, SYSVAD_POOLTAG); + pCustomMinipair->TopoDescriptor = NULL; + } + ExFreePoolWithTag(pCustomMinipair, SYSVAD_POOLTAG); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::Stop() +/*++ + +Routine Description: + + Asynchronously called to stop the audio device. + After returning from this function, there are no more async notifications + pending (volume, connection, etc.). + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Stop]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + eBthHfpState state = eBthHfpStateInvalid; + + state = (eBthHfpState) InterlockedExchange((PLONG)&m_State, eBthHfpStateStopping); + ASSERT(state == eBthHfpStateRunning || state == eBthHfpStateFailed); + UNREFERENCED_VAR(state); + + // + // Stop async notifications. + // + WdfIoTargetPurge(m_WdfIoTarget, WdfIoTargetPurgeIoAndWait); + + // + // Wait for work-item. + // + WdfWorkItemFlush(m_WorkItem); + + // + // Remove the topology and wave render filters. + // + if (m_UnknownSpeakerTopology || m_UnknownSpeakerWave) + { + ntStatus = m_Adapter->RemoveEndpointFilters( + m_SpeakerMiniports, + m_UnknownSpeakerTopology, + m_UnknownSpeakerWave); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("RemoveEndpointFilters (Bth HFP SCO-Bypass Speaker): failed, 0x%x", ntStatus)); + } + } + + // + // Remove the topology and wave capture filters. + // + if (m_UnknownMicTopology || m_UnknownMicWave) + { + ntStatus = m_Adapter->RemoveEndpointFilters( + m_MicMiniports, + m_UnknownMicTopology, + m_UnknownMicWave); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("RemoveEndpointFilters (Bth HFP SCO-Bypass Capture): failed, 0x%x", ntStatus)); + } + } + + // + // Release port/miniport pointers. + // + SAFE_RELEASE(m_UnknownSpeakerTopology); + SAFE_RELEASE(m_UnknownSpeakerWave); + SAFE_RELEASE(m_UnknownMicTopology); + SAFE_RELEASE(m_UnknownMicWave); + + // + // The device is in the stopped state. + // + InterlockedExchange((PLONG)&m_State, eBthHfpStateStopped); + + DeleteCustomEndpointMinipair(m_SpeakerMiniports); + m_SpeakerMiniports = NULL; + + DeleteCustomEndpointMinipair(m_MicMiniports); + m_MicMiniports = NULL; + +} + +#endif // SYSVAD_BTH_BYPASS + + diff --git a/audio/sysvad/common.h b/audio/sysvad/common.h new file mode 100644 index 00000000..4e4e8a01 --- /dev/null +++ b/audio/sysvad/common.h @@ -0,0 +1,622 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Common.h + +Abstract: + + CAdapterCommon class declaration. + +--*/ + +#ifndef _SYSVAD_COMMON_H_ +#define _SYSVAD_COMMON_H_ + +#define HNSTIME_PER_MILLISECOND 10000 + +//============================================================================= +// Macros +//============================================================================= + +#define UNREFERENCED_VAR(status) \ + status = status + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, jump to the given label. +// +// Parameters: +// +// condition - [in] Code that fits in if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_JUMP(condition, label) \ + if (condition) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, perform the given statement +// then jump to the given label. +// +// Parameters: +// +// condition - [in] Code that fits in if statement +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_ACTION_JUMP(condition, action, label) \ + if (condition) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the ntStatus is not NT_SUCCESS, perform the given statement then jump to +// the given label. +// +// Parameters: +// +// ntStatus - [in] Value to check +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_FAILED_ACTION_JUMP(ntStatus, action, label) \ + if (!NT_SUCCESS(ntStatus)) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the ntStatus passed is not NT_SUCCESS, jump to the given label. +// +// Parameters: +// +// ntStatus - [in] Value to check +// label - [in] label to jump if condition is met +// +#define IF_FAILED_JUMP(ntStatus, label) \ + if (!NT_SUCCESS(ntStatus)) \ + { \ + goto label; \ + } + +#define SAFE_RELEASE(p) {if (p) { (p)->Release(); (p) = nullptr; } } + +// JACKDESC_RGB(r, g, b) +#define JACKDESC_RGB(r, g, b) \ + ((COLORREF)((r << 16) | (g << 8) | (b))) + +// Min/Max defines. +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +#define MINWAVERT_POOLTAG 'RWNM' +#define MINTOPORT_POOLTAG 'RTNM' +#define MINADAPTER_POOLTAG 'uAyS' + +typedef enum +{ + eSpeakerDevice = 0, + eSpeakerHpDevice, + eHdmiRenderDevice, + eMicInDevice, + eMicArrayDevice1, + eMicArrayDevice2, + eMicArrayDevice3, + eBthHfpSpeakerDevice, + eBthHfpMicDevice, + eCellularDevice, + eHandsetSpeakerDevice, + eHandsetMicDevice, + eSpeakerHsDevice, + eMicHsDevice, + eFmRxDevice, + eSpdifRenderDevice, + eMaxDeviceType + +} eDeviceType; + +// +// Signal processing modes and default formats structs. +// +typedef struct _MODE_AND_DEFAULT_FORMAT { + GUID Mode; + KSDATAFORMAT* DefaultFormat; +} MODE_AND_DEFAULT_FORMAT, *PMODE_AND_DEFAULT_FORMAT; + +// +// Enumeration of the various types of pins implemented in this driver. +// +typedef enum +{ + NoPin, + BridgePin, + SystemRenderPin, + OffloadRenderPin, + RenderLoopbackPin, + SystemCapturePin, + KeywordCapturePin, + TelephonyBidiPin, +} PINTYPE; + +// +// PIN_DEVICE_FORMATS_AND_MODES +// +// Used to specify a pin's type (e.g. system, offload, etc.), formats, and +// modes. Conceptually serves similar purpose as the PCPIN_DESCRIPTOR to +// define a pin, but is more specific to driver implementation. +// +// Arrays of these structures follow the same order as the filter's +// pin descriptor array so that KS pin IDs can serve as an index. +// +typedef struct _PIN_DEVICE_FORMATS_AND_MODES +{ + PINTYPE PinType; + + KSDATAFORMAT_WAVEFORMATEXTENSIBLE * WaveFormats; + ULONG WaveFormatsCount; + + MODE_AND_DEFAULT_FORMAT * ModeAndDefaultFormat; + ULONG ModeAndDefaultFormatCount; + +} PIN_DEVICE_FORMATS_AND_MODES, *PPIN_DEVICE_FORMATS_AND_MODES; + +// forward declaration. +typedef struct _ENDPOINT_MINIPAIR *PENDPOINT_MINIPAIR; + +// both wave & topology miniport create function prototypes have this form: +typedef HRESULT (*PFNCREATEMINIPORT)( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +); + +//============================================================================= +// +//============================================================================= +typedef struct _SYSVAD_DEVPROPERTY { + const DEVPROPKEY *PropertyKey; + DEVPROPTYPE Type; + ULONG BufferSize; + __field_bcount_opt(BufferSize) PVOID Buffer; +} SYSVAD_DEVPROPERTY, PSYSVAD_DEVPROPERTY; + +#define ENDPOINT_NO_FLAGS 0x00000000 +#define ENDPOINT_OFFLOAD_SUPPORTED 0x00000001 +#define ENDPOINT_SOUNDDETECTOR_SUPPORTED 0x00000002 +#define ENDPOINT_CELLULAR_PROVIDER1 0x00000010 +#define ENDPOINT_CELLULAR_PROVIDER2 0x00000020 + +// +// Endpoint miniport pair (wave/topology) descriptor. +// +typedef struct _ENDPOINT_MINIPAIR +{ + eDeviceType DeviceType; + + // Topology miniport. + PWSTR TopoName; // make sure this name matches with SYSVAD.<TopoName>.szPname in the inf's [Strings] section + PFNCREATEMINIPORT TopoCreateCallback; + PCFILTER_DESCRIPTOR* TopoDescriptor; + ULONG TopoInterfacePropertyCount; + const SYSVAD_DEVPROPERTY* TopoInterfaceProperties; + + // Wave RT miniport. + PWSTR WaveName; // make sure this name matches with SYSVAD.<WaveName>.szPname in the inf's [Strings] section + PFNCREATEMINIPORT WaveCreateCallback; + PCFILTER_DESCRIPTOR* WaveDescriptor; + ULONG WaveInterfacePropertyCount; + const SYSVAD_DEVPROPERTY* WaveInterfaceProperties; + + USHORT DeviceMaxChannels; + PIN_DEVICE_FORMATS_AND_MODES* PinDeviceFormatsAndModes; + ULONG PinDeviceFormatsAndModesCount; + + // Miniport physical connections. + PHYSICALCONNECTIONTABLE* PhysicalConnections; + ULONG PhysicalConnectionCount; + + // General endpoint flags (one of more ENDPOINT_<flag-type>, see above) + ULONG DeviceFlags; +} ENDPOINT_MINIPAIR; + +//============================================================================= +// Defines +//============================================================================= + +DEFINE_GUID(IID_IAdapterCommon, +0x7eda2950, 0xbf9f, 0x11d0, 0x87, 0x1f, 0x0, 0xa0, 0xc9, 0x11, 0xb5, 0x44); + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// IAdapterCommon +// +DECLARE_INTERFACE_(IAdapterCommon, IUnknown) +{ + STDMETHOD_(NTSTATUS, Init) + ( + THIS_ + _In_ PDEVICE_OBJECT DeviceObject + ) PURE; + + STDMETHOD_(PDEVICE_OBJECT, GetDeviceObject) + ( + THIS + ) PURE; + + STDMETHOD_(PDEVICE_OBJECT, GetPhysicalDeviceObject) + ( + THIS + ) PURE; + + STDMETHOD_(WDFDEVICE, GetWdfDevice) + ( + THIS + ) PURE; + + STDMETHOD_(VOID, SetWaveServiceGroup) + ( + THIS_ + _In_ PSERVICEGROUP ServiceGroup + ) PURE; + + STDMETHOD_(BOOL, bDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, bDevSpecificWrite) + ( + THIS_ + _In_ BOOL bDevSpecific + ); + + STDMETHOD_(INT, iDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, iDevSpecificWrite) + ( + THIS_ + _In_ INT iDevSpecific + ); + + STDMETHOD_(UINT, uiDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, uiDevSpecificWrite) + ( + THIS_ + _In_ UINT uiDevSpecific + ); + + STDMETHOD_(BOOL, MixerMuteRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerMuteWrite) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value + ); + + STDMETHOD_(ULONG, MixerMuxRead) + ( + THIS + ); + + STDMETHOD_(VOID, MixerMuxWrite) + ( + THIS_ + _In_ ULONG Index + ); + + STDMETHOD_(LONG, MixerVolumeRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerVolumeWrite) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value + ) PURE; + + STDMETHOD_(LONG, MixerPeakMeterRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerReset) + ( + THIS + ) PURE; + + STDMETHOD_(NTSTATUS, WriteEtwEvent) + ( + THIS_ + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 + ) PURE; + + STDMETHOD_(VOID, SetEtwHelper) + ( + THIS_ + PPORTCLSETWHELPER _pPortClsEtwHelper + ) PURE; + + STDMETHOD_(NTSTATUS, InstallSubdevice) + ( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY * pProperties, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PRESOURCELIST ResourceList, + _In_ REFGUID PortInterfaceId, + _Out_opt_ PUNKNOWN * OutPortInterface, + _Out_opt_ PUNKNOWN * OutPortUnknown, + _Out_opt_ PUNKNOWN * OutMiniportUnknown + ); + + STDMETHOD_(NTSTATUS, UnregisterSubdevice) + ( + THIS_ + _In_opt_ PUNKNOWN UnknownPort + ); + + STDMETHOD_(NTSTATUS, ConnectTopologies) + ( + THIS_ + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHOD_(NTSTATUS, DisconnectTopologies) + ( + THIS_ + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHOD_(NTSTATUS, InstallEndpointFilters) + ( + THIS_ + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave + ); + + STDMETHOD_(NTSTATUS, RemoveEndpointFilters) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave + ); + + STDMETHOD_(NTSTATUS, GetFilters) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN *UnknownTopologyPort, + _Out_opt_ PUNKNOWN *UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN *UnknownWavePort, + _Out_opt_ PUNKNOWN *UnknownWaveMiniport + ); + + STDMETHOD_(NTSTATUS, SetIdlePowerManagement) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnable + ); + +#ifdef SYSVAD_BTH_BYPASS + STDMETHOD_(NTSTATUS, InitBthScoBypass)(); + +#endif // SYSVAD_BTH_BYPASS + + STDMETHOD_(VOID, Cleanup)(); +}; + +typedef IAdapterCommon *PADAPTERCOMMON; + +//============================================================================= +// Function Prototypes +//============================================================================= +NTSTATUS +NewAdapterCommon +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType +); + + +#ifdef SYSVAD_BTH_BYPASS + +// Event callback definition. +typedef VOID (*PFNEVENTNOTIFICATION)( + _In_opt_ PVOID Context +); + +DEFINE_GUID(IID_IBthHfpDeviceCommon, +0x576b824a, 0x5248, 0x47b1, 0x82, 0xc5, 0xe4, 0x7b, 0xa7, 0xe2, 0xaf, 0x2b); + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// IAdapterCommon +// +DECLARE_INTERFACE_(IBthHfpDeviceCommon, IUnknown) +{ + STDMETHOD_(BOOL, IsVolumeSupported) + ( + THIS_ + ) PURE; + + STDMETHOD_(PKSPROPERTY_VALUES, GetVolumeSettings) + ( + THIS_ + _Out_ PULONG Size + ) PURE; + + STDMETHOD_(LONG, GetSpeakerVolume) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, SetSpeakerVolume) + ( + THIS_ + _In_ ULONG Volume + ) PURE; + + STDMETHOD_(LONG, GetMicVolume) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, SetMicVolume) + ( + THIS_ + _In_ ULONG Volume + ) PURE; + + STDMETHOD_(BOOL, GetConnectionStatus) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, Connect) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, Disconnect) + ( + THIS_ + ) PURE; + + STDMETHOD_(BOOL, GetStreamStatus) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, StreamOpen) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, StreamClose) + ( + THIS_ + ) PURE; + + STDMETHOD_(GUID, GetContainerId) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, SetSpeakerVolumeHandler) + ( + THIS_ + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ) PURE; + + STDMETHOD_(VOID, SetSpeakerConnectionStatusHandler) + ( + THIS_ + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ) PURE; + + STDMETHOD_(VOID, SetMicVolumeHandler) + ( + THIS_ + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ) PURE; + + STDMETHOD_(VOID, SetMicConnectionStatusHandler) + ( + THIS_ + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ) PURE; + + STDMETHOD_(BOOL, IsNRECSupported) + ( + THIS_ + ) PURE; + + STDMETHOD_(BOOL, GetNRECDisableStatus) + ( + THIS_ + ) PURE; +}; +typedef IBthHfpDeviceCommon *PBTHHFPDEVICECOMMON; + +#endif // SYSVAD_BTH_BYPASS + +#endif //_SYSVAD_COMMON_H_ + diff --git a/audio/sysvad/hw.cpp b/audio/sysvad/hw.cpp new file mode 100644 index 00000000..2ba0d514 --- /dev/null +++ b/audio/sysvad/hw.cpp @@ -0,0 +1,448 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hw.cpp + +Abstract: + + Implementation of SYSVAD HW class. + SYSVAD HW has an array for storing mixer and volume settings + for the topology. + + +--*/ +#include <sysvad.h> +#include "hw.h" + +//============================================================================= +// CSYSVADHW +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +CSYSVADHW::CSYSVADHW() +: m_ulMux(0), + m_bDevSpecific(FALSE), + m_iDevSpecific(0), + m_uiDevSpecific(0) +/*++ + +Routine Description: + + Constructor for SYSVADHW. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + MixerReset(); +} // CSYSVADHW +#pragma code_seg() + + +//============================================================================= +BOOL +CSYSVADHW::bGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + True or False (in this example). + +--*/ +{ + return m_bDevSpecific; +} // bGetDevSpecific + +//============================================================================= +void +CSYSVADHW::bSetDevSpecific +( + _In_ BOOL bDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + fDevSpecific - true or false for this example. + +Return Value: + + void + +--*/ +{ + m_bDevSpecific = bDevSpecific; +} // bSetDevSpecific + +//============================================================================= +INT +CSYSVADHW::iGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + int (in this example). + +--*/ +{ + return m_iDevSpecific; +} // iGetDevSpecific + +//============================================================================= +void +CSYSVADHW::iSetDevSpecific +( + _In_ INT iDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + fDevSpecific - true or false for this example. + +Return Value: + + void + +--*/ +{ + m_iDevSpecific = iDevSpecific; +} // iSetDevSpecific + +//============================================================================= +UINT +CSYSVADHW::uiGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + UINT (in this example). + +--*/ +{ + return m_uiDevSpecific; +} // uiGetDevSpecific + +//============================================================================= +void +CSYSVADHW::uiSetDevSpecific +( + _In_ UINT uiDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + uiDevSpecific - int for this example. + +Return Value: + + void + +--*/ +{ + m_uiDevSpecific = uiDevSpecific; +} // uiSetDevSpecific + + +//============================================================================= +BOOL +CSYSVADHW::GetMixerMute +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) mute levels for SYSVAD + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + mute setting + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_MuteControls[ulNode]; + } + + return 0; +} // GetMixerMute + +//============================================================================= +ULONG +CSYSVADHW::GetMixerMux() +/*++ + +Routine Description: + + Return the current mux selection + +Arguments: + +Return Value: + + ULONG + +--*/ +{ + return m_ulMux; +} // GetMixerMux + +//============================================================================= +LONG +CSYSVADHW::GetMixerVolume +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) volume for SYSVAD. + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + LONG - volume level + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_VolumeControls[ulNode]; + } + + return 0; +} // GetMixerVolume + +//============================================================================= +LONG +CSYSVADHW::GetMixerPeakMeter +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) peak meter for SYSVAD. + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + LONG - sample peak meter level + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_PeakMeterControls[ulNode]; + } + + return 0; +} // GetMixerVolume + +//============================================================================= +#pragma code_seg("PAGE") +void +CSYSVADHW::MixerReset() +/*++ + +Routine Description: + + Resets the mixer registers. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + RtlFillMemory(m_VolumeControls, sizeof(LONG) * MAX_TOPOLOGY_NODES, 0xFF); + RtlFillMemory(m_MuteControls, sizeof(BOOL) * MAX_TOPOLOGY_NODES, TRUE); + + for (ULONG i=0; i<MAX_TOPOLOGY_NODES; ++i) + { + m_PeakMeterControls[i] = PEAKMETER_SIGNED_MAXIMUM/2; + } + + // BUGBUG change this depending on the topology + m_ulMux = 2; +} // MixerReset +#pragma code_seg() + +//============================================================================= +void +CSYSVADHW::SetMixerMute +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel, + _In_ BOOL fMute +) +/*++ + +Routine Description: + + Sets the HW (!) mute levels for SYSVAD + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we setting? + + fMute - mute flag + +Return Value: + + void + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + m_MuteControls[ulNode] = fMute; + } +} // SetMixerMute + +//============================================================================= +void +CSYSVADHW::SetMixerMux +( + _In_ ULONG ulNode +) +/*++ + +Routine Description: + + Sets the HW (!) mux selection + +Arguments: + + ulNode - topology node id + +Return Value: + + void + +--*/ +{ + m_ulMux = ulNode; +} // SetMixMux + +//============================================================================= +void +CSYSVADHW::SetMixerVolume +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel, + _In_ LONG lVolume +) +/*++ + +Routine Description: + + Sets the HW (!) volume for SYSVAD. + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we setting? + + lVolume - volume level + +Return Value: + + void + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + m_VolumeControls[ulNode] = lVolume; + } +} // SetMixerVolume + diff --git a/audio/sysvad/hw.h b/audio/sysvad/hw.h new file mode 100644 index 00000000..cdfe4f75 --- /dev/null +++ b/audio/sysvad/hw.h @@ -0,0 +1,107 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hw.h + +Abstract: + + Declaration of SYSVAD HW class. + SYSVAD HW has an array for storing mixer and volume settings + for the topology. + +--*/ + +#ifndef _SYSVAD_HW_H_ +#define _SYSVAD_HW_H_ + +//============================================================================= +// Defines +//============================================================================= +// BUGBUG we should dynamically allocate this... +#define MAX_TOPOLOGY_NODES 20 + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CSYSVADHW +// This class represents virtual SYSVAD HW. An array representing volume +// registers and mute registers. + +class CSYSVADHW +{ +public: +protected: + BOOL m_MuteControls[MAX_TOPOLOGY_NODES]; + LONG m_VolumeControls[MAX_TOPOLOGY_NODES]; + LONG m_PeakMeterControls[MAX_TOPOLOGY_NODES]; + ULONG m_ulMux; // Mux selection + BOOL m_bDevSpecific; + INT m_iDevSpecific; + UINT m_uiDevSpecific; + +private: + +public: + CSYSVADHW(); + + void MixerReset(); + BOOL bGetDevSpecific(); + void bSetDevSpecific + ( + _In_ BOOL bDevSpecific + ); + INT iGetDevSpecific(); + void iSetDevSpecific + ( + _In_ INT iDevSpecific + ); + UINT uiGetDevSpecific(); + void uiSetDevSpecific + ( + _In_ UINT uiDevSpecific + ); + BOOL GetMixerMute + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel + ); + void SetMixerMute + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel, + _In_ BOOL fMute + ); + ULONG GetMixerMux(); + void SetMixerMux + ( + _In_ ULONG ulNode + ); + LONG GetMixerVolume + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel + ); + void SetMixerVolume + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel, + _In_ LONG lVolume + ); + + LONG GetMixerPeakMeter + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel + ); + +protected: +private: +}; +typedef CSYSVADHW *PCSYSVADHW; + +#endif // _SYSVAD_HW_H_ + diff --git a/audio/sysvad/ihvprivatepropertyset.h b/audio/sysvad/ihvprivatepropertyset.h new file mode 100644 index 00000000..b865a2d9 --- /dev/null +++ b/audio/sysvad/ihvprivatepropertyset.h @@ -0,0 +1,14 @@ +//=========================================================================== +// HARDWARE OFFLOAD PIN DEFINITIONS +//=========================================================================== +#define STATIC_KSPROPSETID_OffloadPin\ + 0xfb05301, 0x5a11, 0x400a, 0x99, 0x6b, 0x12,0xdf, 0x99, 0x28, 0x36, 0xde + +DEFINE_GUIDSTRUCT("0FB05301-5A11-400a-996B-12DF992836DE", KSPROPSETID_OffloadPin); + +#define KSPROPSETID_OffloadPin DEFINE_GUIDNAMED(KSPROPSETID_OffloadPin) + +typedef enum { + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER +} KSPROPERTY_OFFLOAD_PIN;
\ No newline at end of file diff --git a/audio/sysvad/kshelper.cpp b/audio/sysvad/kshelper.cpp new file mode 100644 index 00000000..d5213426 --- /dev/null +++ b/audio/sysvad/kshelper.cpp @@ -0,0 +1,960 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + kshelper.cpp + +Abstract: + + Helper functions for sysvad + +--*/ + +#include "sysvad.h" + +//4127: conditional expression is constant +#pragma warning (disable : 4127) + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +PWAVEFORMATEX +GetWaveFormatEx +( + _In_ PKSDATAFORMAT pDataFormat +) +/*++ + +Routine Description: + + Returns the waveformatex for known formats. + +Arguments: + + pDataFormat - data format. + +Return Value: + + waveformatex in DataFormat. + NULL for unknown data formats. + +--*/ +{ + PAGED_CODE(); + + PWAVEFORMATEX pWfx = NULL; + + // If this is a known dataformat extract the waveformat info. + // + if + ( + pDataFormat && + ( IsEqualGUIDAligned(pDataFormat->MajorFormat, + KSDATAFORMAT_TYPE_AUDIO) && + ( IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) || + IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND) ) ) + ) + { + pWfx = PWAVEFORMATEX(pDataFormat + 1); + + if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND)) + { + PKSDSOUND_BUFFERDESC pwfxds; + + pwfxds = PKSDSOUND_BUFFERDESC(pDataFormat + 1); + pWfx = &pwfxds->WaveFormatEx; + } + } + + return pWfx; +} // GetWaveFormatEx + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +ValidatePropertyParams +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cbValueSize, + _In_ ULONG cbInstanceSize /* = 0 */ +) +/*++ + +Routine Description: + + Validates property parameters. + +Arguments: + + PropertyRequest - + cbValueSize - + cbInstanceSize - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + + if (PropertyRequest && cbValueSize) + { + // If the caller is asking for ValueSize. + // + if (0 == PropertyRequest->ValueSize) + { + PropertyRequest->ValueSize = cbValueSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + // If the caller passed an invalid ValueSize. + // + else if (PropertyRequest->ValueSize < cbValueSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else if (PropertyRequest->InstanceSize < cbInstanceSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + // If all parameters are OK. + // + else if (PropertyRequest->ValueSize >= cbValueSize) + { + if (PropertyRequest->Value) + { + ntStatus = STATUS_SUCCESS; + // + // Caller should set ValueSize, if the property + // call is successful. + // + } + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + // Clear the ValueSize if unsuccessful. + // + if (PropertyRequest && + STATUS_SUCCESS != ntStatus && + STATUS_BUFFER_OVERFLOW != ntStatus) + { + PropertyRequest->ValueSize = 0; + } + + return ntStatus; +} // ValidatePropertyParams + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +SysvadPropertyDispatch +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + Handles and dispatches a SYSVADPROPERTY_ITEM. + + Use this as the property handler only if the property item is a + SYSVADPROPERTY_ITEM. +--*/ +{ + PAGED_CODE(); + + SYSVADPROPERTY_ITEM* item = (SYSVADPROPERTY_ITEM*)PropertyRequest->PropertyItem; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + if (item->SupportHandler != nullptr) + { + return item->SupportHandler(PropertyRequest); + } + else + { + return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL); + } + } + + // Verify instance data size + if (PropertyRequest->InstanceSize < item->MinProperty) + { + return STATUS_INVALID_PARAMETER; + } + + ULONG cbMinSize = item->MinData; + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + PropertyRequest->ValueSize = 0; + return STATUS_BUFFER_TOO_SMALL; + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + if (item->GetHandler != nullptr) + { + return item->GetHandler(PropertyRequest); + } + else + { + return STATUS_NOT_SUPPORTED; + } + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (item->SetHandler != nullptr) + { + return item->SetHandler(PropertyRequest); + } + else + { + return STATUS_NOT_SUPPORTED; + } + } + + return STATUS_INVALID_DEVICE_REQUEST; +} + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG Flags, + _In_ DWORD PropTypeSetId +) +/*++ + +Routine Description: + + Default basic support handler. Basic processing depends on the size of data. + For ULONG it only returns Flags. For KSPROPERTY_DESCRIPTION, the structure + is filled. + +Arguments: + + PropertyRequest - + + Flags - Support flags. + + PropTypeSetId - PropTypeSetId + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Flags & KSPROPERTY_TYPE_BASICSUPPORT); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = Flags; + PropDesc->DescriptionSize = sizeof(KSPROPERTY_DESCRIPTION); + if (VT_ILLEGAL != PropTypeSetId) + { + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = PropTypeSetId; + } + else + { + PropDesc->PropTypeSet.Set = GUID_NULL; + PropDesc->PropTypeSet.Id = 0; + } + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 0; + PropDesc->Reserved = 0; + + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + // + *(PULONG(PropertyRequest->Value)) = Flags; + + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandler_BasicSupport + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportVolume +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for Volume nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_I4; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; i<MaxChannels; ++i) + { + Range[i].Bounds.SignedMaximum = VOLUME_SIGNED_MAXIMUM; // 0 dB + Range[i].Bounds.SignedMinimum = VOLUME_SIGNED_MINIMUM; // -96 dB + Range[i].SteppingDelta = VOLUME_STEPPING_DELTA; // .5 dB + Range[i].Reserved = 0; + } + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportMute +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for Mute nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_BOOL; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; i<MaxChannels; ++i) + { + Range[i].Bounds.SignedMaximum = 1; // true + Range[i].Bounds.SignedMinimum = 0; // false + Range[i].SteppingDelta = 1; // false <- -> true + Range[i].Reserved = 0; + } + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportPeakMeter2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for peak meter nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_I4; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; i<MaxChannels; ++i) + { + Range[i].Bounds.SignedMaximum = PEAKMETER_SIGNED_MAXIMUM; + Range[i].Bounds.SignedMinimum = PEAKMETER_SIGNED_MINIMUM; + Range[i].SteppingDelta = PEAKMETER_STEPPING_DELTA; + Range[i].Reserved = 0; + } + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_CpuResources +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Processes KSPROPERTY_AUDIO_CPURESOURCES + +Arguments: + + PropertyRequest - property request structure. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(ULONG)); + if (NT_SUCCESS(ntStatus)) + { + *(PULONG(PropertyRequest->Value)) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU; + PropertyRequest->ValueSize = sizeof(ULONG); + } + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + VT_UI4 + ); + } + + return ntStatus; +} // PropertyHandlerCpuResources + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_Volume +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_VOLUMELEVEL + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG ulChannel; + PLONG plVolume; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportVolume( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // volume value is a LONG + sizeof(ULONG) // instance is the channel number + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + plVolume = PLONG (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *plVolume = + AdapterCommon->MixerVolumeRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + ); + PropertyRequest->ValueSize = sizeof(ULONG); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (ALL_CHANNELS_ID == ulChannel) + { + for (ULONG i=0; i<ulChannel; ++i) + { + AdapterCommon->MixerVolumeWrite + ( + PropertyRequest->Node, + i, + VOLUME_NORMALIZE_IN_RANGE(*plVolume) + ); + } + } + else + { + AdapterCommon->MixerVolumeWrite + ( + PropertyRequest->Node, + ulChannel, + VOLUME_NORMALIZE_IN_RANGE(*plVolume) + ); + } + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_Mute +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_MUTE + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus; + ULONG ulChannel; + PBOOL pfMute; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportMute( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(BOOL), + sizeof(ULONG) + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + pfMute = PBOOL (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *pfMute = + AdapterCommon->MixerMuteRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + ); + PropertyRequest->ValueSize = sizeof(BOOL); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (ALL_CHANNELS_ID == ulChannel) + { + for (ULONG i=0; i<ulChannel; ++i) + { + AdapterCommon->MixerMuteWrite + ( + PropertyRequest->Node, + i, + (*pfMute) ? TRUE : FALSE + ); + } + } + else + { + AdapterCommon->MixerMuteWrite + ( + PropertyRequest->Node, + ulChannel, + (*pfMute) ? TRUE : FALSE + ); + } + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerMute + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_PeakMeter2 +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_PEAKMETER2 + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG ulChannel; + PLONG plSample; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportPeakMeter2( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // sample value is a LONG + sizeof(ULONG) // instance is the channel number + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + plSample = PLONG (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *plSample = + PEAKMETER_NORMALIZE_IN_RANGE( + AdapterCommon->MixerPeakMeterRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + )); + + PropertyRequest->ValueSize = sizeof(ULONG); + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerVolume + + diff --git a/audio/sysvad/kshelper.h b/audio/sysvad/kshelper.h new file mode 100644 index 00000000..10c1cadc --- /dev/null +++ b/audio/sysvad/kshelper.h @@ -0,0 +1,159 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + kshelper.h + +Abstract: + + Helper functions for sysvad + +--*/ +#ifndef _SYSVAD_KSHELPER_H_ +#define _SYSVAD_KSHELPER_H_ + +#include <portcls.h> +#include <ksdebug.h> + +PWAVEFORMATEX +GetWaveFormatEx +( + _In_ PKSDATAFORMAT pDataFormat +); + +NTSTATUS +ValidatePropertyParams +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cbValueSize, + _In_ ULONG cbInstanceSize = 0 +); + +NTSTATUS +PropertyHandler_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG Flags, + _In_ DWORD PropTypeSetId +); + +NTSTATUS +PropertyHandler_BasicSupportVolume +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_BasicSupportMute +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_BasicSupportPeakMeter2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_CpuResources +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +NTSTATUS +PropertyHandler_Volume +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_Mute +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_PeakMeter2 +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +//============================================================================= +// Property helpers +//============================================================================= + +NTSTATUS +SysvadPropertyDispatch +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// Use this structure to define property items with extra data allowing easier +// definition of separate get, set, and support handlers dispatched through +// SysvadPropertyDispatch. +typedef struct +{ + PCPROPERTY_ITEM PropertyItem; // Standard PCPROPERTY_ITEM + ULONG MinProperty; // Minimum size of the property instance data + ULONG MinData; // Minimum size of the property value + PCPFNPROPERTY_HANDLER GetHandler; // Property get handler (NULL if GET not supported) + PCPFNPROPERTY_HANDLER SetHandler; // Property set handler (NULL if SET not supported) + PCPFNPROPERTY_HANDLER SupportHandler; // Property support handler (NULL for common handler) +} SYSVADPROPERTY_ITEM; + +// The following macros facilitate adding property handlers to a class, allowing +// easier declaration and definition of a "thunk" routine that directly handles +// the property request and calls into a class instance method. Note that as +// written, the thunk routine assumes PAGED_CODE. +#define DECLARE_CLASSPROPERTYHANDLER(theClass, theMethod) \ +NTSTATUS theClass##_##theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +); + +#define DECLARE_PROPERTYHANDLER(theMethod) \ +NTSTATUS theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +); + +#define DEFINE_CLASSPROPERTYHANDLER(theClass, theMethod) \ +NTSTATUS theClass##_##theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +) \ +{ \ + NTSTATUS status; \ + \ + PAGED_CODE(); \ + \ + theClass* p = reinterpret_cast<theClass*>(PropertyRequest->MajorTarget); \ + \ + p->AddRef(); \ + status = p->theMethod(PropertyRequest); \ + p->Release(); \ + \ + return status; \ +} \ +NTSTATUS theClass::theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +) + + + + +#endif // _SYSVAD_KSHELPER_H_ + diff --git a/audio/sysvad/phoneaudiosample.inf b/audio/sysvad/phoneaudiosample.inf Binary files differnew file mode 100644 index 00000000..a949197a --- /dev/null +++ b/audio/sysvad/phoneaudiosample.inf diff --git a/audio/sysvad/readme.htm b/audio/sysvad/readme.htm new file mode 100644 index 00000000..a5e3563d --- /dev/null +++ b/audio/sysvad/readme.htm @@ -0,0 +1,396 @@ +<html xmlns:o="urn:schemas-microsoft-com:office:office" +xmlns:w="urn:schemas-microsoft-com:office:word" +xmlns="http://www.w3.org/TR/REC-html40"> + +<head> +<meta http-equiv=Content-Type content="text/html; charset=windows-1252"> +<meta name=ProgId content=Word.Document> +<meta name=Generator content="Microsoft Word 11"> +<meta name=Originator content="Microsoft Word 11"> +<link rel=File-List href="readme151_files/filelist.xml"> +<title>AEVAD Driver</title> +<!--[if gte mso 9]><xml> + <w:WordDocument> + <w:SpellingState>Clean</w:SpellingState> + <w:GrammarState>Clean</w:GrammarState> + <w:ValidateAgainstSchemas/> + <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> + <w:IgnoreMixedContent>false</w:IgnoreMixedContent> + <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> + <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> + </w:WordDocument> +</xml><![endif]--><!--[if gte mso 9]><xml> + <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> + </w:LatentStyles> +</xml><![endif]--> +<style> +<!-- + /* Font Definitions */ + @font-face + {font-family:Verdana; + panose-1:2 11 6 4 3 5 4 4 2 4; + mso-font-charset:0; + mso-generic-font-family:swiss; + mso-font-pitch:variable; + mso-font-signature:536871559 0 0 0 415 0;} +@font-face + {font-family:"MS Sans Serif"; + panose-1:0 0 0 0 0 0 0 0 0 0; + mso-font-charset:0; + mso-generic-font-family:swiss; + mso-font-format:other; + mso-font-pitch:variable; + mso-font-signature:3 0 0 0 1 0;} + /* Style Definitions */ + p.MsoNormal, li.MsoNormal, div.MsoNormal + {mso-style-parent:""; + margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman"; + mso-fareast-font-family:"Times New Roman";} +h2 + {mso-margin-top-alt:auto; + margin-right:0in; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + mso-outline-level:2; + font-size:18.0pt; + font-family:"Times New Roman"; + font-weight:bold;} +h3 + {mso-margin-top-alt:auto; + margin-right:0in; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + mso-outline-level:3; + font-size:13.5pt; + font-family:"Times New Roman"; + font-weight:bold;} +h4 + {mso-margin-top-alt:auto; + margin-right:0in; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + mso-outline-level:4; + font-size:12.0pt; + font-family:"Times New Roman"; + font-weight:bold;} +p.MsoBodyText, li.MsoBodyText, div.MsoBodyText + {margin-top:0in; + margin-right:0in; + margin-bottom:6.0pt; + margin-left:0in; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman"; + mso-fareast-font-family:"Times New Roman";} +a:link, span.MsoHyperlink + {color:blue; + text-decoration:underline; + text-underline:single;} +a:visited, span.MsoHyperlinkFollowed + {color:purple; + text-decoration:underline; + text-underline:single;} +p + {mso-margin-top-alt:auto; + margin-right:0in; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman"; + mso-fareast-font-family:"Times New Roman";} +pre + {margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt; + font-size:10.0pt; + font-family:"Courier New"; + mso-fareast-font-family:"Courier New";} +p.Code, li.Code, div.Code + {mso-style-name:Code; + mso-style-update:auto; + mso-style-parent:"Body Text"; + margin-top:3.0pt; + margin-right:0in; + margin-bottom:6.0pt; + margin-left:.5in; + line-height:13.0pt; + mso-pagination:widow-orphan; + font-size:11.0pt; + mso-bidi-font-size:10.0pt; + font-family:"Courier New"; + mso-fareast-font-family:"Times New Roman"; + mso-bidi-font-family:"Times New Roman";} +span.SpellE + {mso-style-name:""; + mso-spl-e:yes;} +span.GramE + {mso-style-name:""; + mso-gram-e:yes;} +@page Section1 + {size:8.5in 11.0in; + margin:1.0in 1.25in 1.0in 1.25in; + mso-header-margin:.5in; + mso-footer-margin:.5in; + mso-paper-source:0;} +div.Section1 + {page:Section1;} + /* List Definitions */ + @list l0 + {mso-list-id:1010329625; + mso-list-type:hybrid; + mso-list-template-ids:-2011428930 67698689 67698691 67698693 67698689 67698691 67698693 67698689 67698691 67698693;} +@list l0:level1 + {mso-level-number-format:bullet; + mso-level-text:\F0B7; + mso-level-tab-stop:.5in; + mso-level-number-position:left; + text-indent:-.25in; + font-family:Symbol;} +@list l0:level2 + {mso-level-tab-stop:1.0in; + mso-level-number-position:left; + text-indent:-.25in;} +@list l0:level3 + {mso-level-tab-stop:1.5in; + mso-level-number-position:left; + text-indent:-.25in;} +@list l0:level4 + {mso-level-tab-stop:2.0in; + mso-level-number-position:left; + text-indent:-.25in;} +@list l0:level5 + {mso-level-tab-stop:2.5in; + mso-level-number-position:left; + text-indent:-.25in;} +@list l0:level6 + {mso-level-tab-stop:3.0in; + mso-level-number-position:left; + text-indent:-.25in;} +@list l0:level7 + {mso-level-tab-stop:3.5in; + mso-level-number-position:left; + text-indent:-.25in;} +@list l0:level8 + {mso-level-tab-stop:4.0in; + mso-level-number-position:left; + text-indent:-.25in;} +@list l0:level9 + {mso-level-tab-stop:4.5in; + mso-level-number-position:left; + text-indent:-.25in;} +ol + {margin-bottom:0in;} +ul + {margin-bottom:0in;} +--> +</style> +<!--[if gte mso 10]> +<style> + /* Style Definitions */ + table.MsoNormalTable + {mso-style-name:"Table Normal"; + mso-tstyle-rowband-size:0; + mso-tstyle-colband-size:0; + mso-style-noshow:yes; + mso-style-parent:""; + mso-padding-alt:0in 5.4pt 0in 5.4pt; + mso-para-margin:0in; + mso-para-margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:10.0pt; + font-family:"Times New Roman"; + mso-ansi-language:#0400; + mso-fareast-language:#0400; + mso-bidi-language:#0400;} +table.MsoTableGrid + {mso-style-name:"Table Grid"; + mso-tstyle-rowband-size:0; + mso-tstyle-colband-size:0; + border:solid windowtext 1.0pt; + mso-border-alt:solid windowtext .5pt; + mso-padding-alt:0in 5.4pt 0in 5.4pt; + mso-border-insideh:.5pt solid windowtext; + mso-border-insidev:.5pt solid windowtext; + mso-para-margin-top:0in; + mso-para-margin-right:0in; + mso-para-margin-bottom:6.0pt; + mso-para-margin-left:0in; + mso-pagination:widow-orphan; + font-size:9.0pt; + mso-bidi-font-size:10.0pt; + font-family:Arial; + mso-bidi-font-family:"Times New Roman"; + mso-ansi-language:#0400; + mso-fareast-language:#0400; + mso-bidi-language:#0400;} +</style> +<![endif]--> +</head> + +<body lang=EN-US link=blue vlink=purple style='tab-interval:.5in'> + +<div class=Section1> + +<h2><a name="_top"></a><span style='font-family:Verdana'>Microsoft Audio Engine Virtual +Audio Device (AEVAD) Driver</span></h2> + +<h3><span style='font-family:Verdana'>SUMMARY</span></h3> + +<p><span style='font-size:10.0pt;font-family:Verdana'>This is a sample driver that demonstrates + how to develop a WDM audio driver that exposes support for audio offloading. The + driver uses a "virtual audio device" instead of an +actual hardware-based adapter and highlights the different +aspects of the audio offloading WDM audio driver architecture.</span></p> + +<p><span style='font-size:10.0pt;font-family:Verdana'>Driver +developers can use the framework in this sample to implement offload support without concern +for hardware dependencies. The framework includes implementations of the +following interfaces:</span></p> + +<p style='margin-left:.5in;text-indent:-.25in;mso-list:l0 level1 lfo2; +tab-stops:list .5in'><![if !supportLists]><span style='font-family:Symbol; +mso-fareast-font-family:Symbol;mso-bidi-font-family:Symbol'><span +style='mso-list:Ignore'>�<span style='font:7.0pt "Times New Roman"'> +</span></span></span><![endif]><span style='font-size:10.0pt;font-family:Verdana'>The +<span class=SpellE><i>CAdapterCommon</i></span><span style='mso-bidi-font-style: +italic'> interface </span>gives the <span class=SpellE>miniports</span> access +to virtual mixer hardware. It also implements the <span class=SpellE><b +style='mso-bidi-font-weight:normal'>IAdapterPowerManagement</b></span> +interface.</span></p> + +<p style='margin-left:.5in;text-indent:-.25in;mso-list:l0 level1 lfo2; +tab-stops:list .5in'><![if !supportLists]><span style='font-family:Symbol; +mso-fareast-font-family:Symbol;mso-bidi-font-family:Symbol'><span +style='mso-list:Ignore'>�<span style='font:7.0pt "Times New Roman"'> +</span></span></span><![endif]><span style='font-size:10.0pt;font-family:Verdana'>The +<span class=SpellE><i>CMiniportTopologySYSVAD</i></span><span style='mso-bidi-font-style: +italic'> interface</span> is the base class for all sample topologies. It has +very basic common functions. In addition, this class contains common topology +property handlers.</span></p> + +<p><span style='font-size:10.0pt;font-family:Verdana'>The following table shows +the features that are implemented in the various subdirectories of this sample.</span></p> + +<table class=MsoNormalTable border=1 cellspacing=0 cellpadding=0 + style='border-collapse:collapse;border:none;mso-border-alt:solid windowtext .5pt; + mso-padding-alt:0in 5.75pt 0in 5.75pt'> + <tr style='mso-yfti-irow:0;mso-yfti-firstrow:yes'> + <td width=120 valign=top style='width:89.7pt;border:solid windowtext 1.0pt; + mso-border-alt:solid windowtext .5pt;padding:0in 5.75pt 0in 5.75pt'> + <p><b><span style='font-size:10.0pt;font-family:Verdana'>Subdirectory</span></b></p> + </td> + <td width=360 valign=top style='width:3.75in;border:solid windowtext 1.0pt; + border-left:none;mso-border-left-alt:solid windowtext .5pt;mso-border-alt: + solid windowtext .5pt;padding:0in 5.75pt 0in 5.75pt'> + <p><b style='mso-bidi-font-weight:normal'><span style='font-size:10.0pt; + font-family:Verdana'>Description</span></b></p> + </td> + </tr> + <tr style='mso-yfti-irow:1'> + <td width=120 valign=top style='width:89.7pt;border:solid windowtext 1.0pt; + border-top:none;mso-border-top-alt:solid windowtext .5pt;mso-border-alt:solid windowtext .5pt; + padding:0in 5.75pt 0in 5.75pt'> + <p><em>aesamplewavert</em></p> + </td> + <td width=360 valign=top style='width:3.75in;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + mso-border-top-alt:solid windowtext .5pt;mso-border-left-alt:solid windowtext .5pt; + mso-border-alt:solid windowtext .5pt;padding:0in 5.75pt 0in 5.75pt'> + Basic audio offloading driver with independent microphone support.</td> + </tr> +</table> + +<p><span style='font-size:10.0pt;font-family:Verdana'><o:p> </o:p></span></p> + +<h3><span style='font-family:Verdana'>BUILDING THE SAMPLE</span></h3> + +<p><span style='font-size:10.0pt;font-family:Verdana'>To build this sample, open the +VC++ project file (AESample.VcxProj) from the WDK sample content in Visual Studio Dev11.</span></p>WDK + +<p><span style='font-size:10.0pt;font-family:"Verdana","sans-serif"'>The sample +must be manually installed.<span style='mso-spacerun:yes'>� </span>It can be +installed via the <b style='mso-bidi-font-weight:normal'>devcon.exe tool</b>, +which is available as a free download from <a +href="http://support.microsoft.com/kb/311272">http://support.microsoft.com/kb/311272</a>, +or it can be installed programmatically via a third-party setup application.</span></p> + +<p><span class=SpellE><b style='mso-bidi-font-weight:normal'><span +style='font-size:10.0pt;font-family:"Verdana","sans-serif"'>Devcon</span></b></span><span +style='font-size:10.0pt;font-family:"Verdana","sans-serif"'> source code is also +included elsewhere in the WDK as a sample.<o:p></o:p></span></p> + +<h3><span style='font-family:Verdana'>DRIVER ISSUES</span></h3> + +<p><span style='font-size:10.0pt;font-family:Verdana'>No known issues</span></p> + +<h3><span style='font-family:Verdana'>CODE TOUR</span></h3> + +<h4><span style='font-family:Verdana'>File Manifest</span></h4> + +<pre><u>File<span style='mso-tab-count:2'>���������� </span>Description</u></pre><pre><o:p> </o:p></pre><pre><span class=SpellE><span +class=GramE>adapter.cpp<span style='mso-tab-count: +1'>��� </span>Connects the driver to the system</span></span></pre> + <pre><span class=SpellE><span +class=GramE>aevad.inf</span></span><span style='mso-tab-count:1'>����� </span>Installation INF file for this sample</pre><pre><span +class=SpellE><span class=GramE>aevad.rc</span></span><span style='mso-tab-count: +1'>������ </span>Common resource file</pre> +<pre><span +class=SpellE><span class=GramE>aevadtest.h</span></span><span style='mso-tab-count: +1'>� </span>Common header file for test cases</pre> + <pre><span class=SpellE><span +class=GramE>basetopo.cpp</span></span><span style='mso-tab-count:1'>�� </span>Base topology class implementation</pre><pre><span +class=SpellE><span class=GramE>basetopo.h</span></span><span style='mso-tab-count: +1'>���� </span>Base topology class definition</pre><span +class=SpellE><span class=GramE>common.cpp</span></span><span style='mso-tab-count: +1'>���� </span>Common object used by all miniport drivers</pre><pre><span +class=SpellE><span class=GramE>common.h</span></span> <span style='mso-tab-count: +1'>����� </span>Header file for the common object</pre><pre><span class=SpellE><span +class=GramE>hw.cpp</span></span><span style='mso-tab-count:2'>�������� </span>Hardware abstraction of SYSVAD</pre><pre><span +class=SpellE><span class=GramE>hw.h</span></span><span style='mso-tab-count: +2'>���������� </span>Hardware class definition</pre> + <pre>ihvprivatepropertyset.h Header for private KS property set example</pre><pre><span class=SpellE><span +class=GramE>kshelper.cpp</span></span><span style='mso-tab-count:1'>�� </span>Kernel streaming utility function implementations</pre><pre><span +class=SpellE><span class=GramE>kshelper.h</span></span><span style='mso-tab-count: +1'>���� </span>Kernel streaming utility function definitions</pre><pre><span +class=SpellE><span class=GramE>sysvad.h</span></span><span style='mso-tab-count: +2'>������� </span>Common definitions</pre> + <pre><span class=GramE>readme.htm</span><span +style='mso-tab-count:1'>���� </span>The documentation for this driver (this file)</pre><pre><span +class=SpellE><span class=GramE>sources.inc</span></span><span style='mso-tab-count: +1'>��� </span>Common sources file included by all sample drivers</pre> + <pre>unittestdata.h Definitions that can be used for unit testing</pre><pre><o:p> </o:p></pre> + +<p align=center style='text-align:center;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><a +href="#_top"><span style='font-size:10.0pt;font-family:Verdana'>Top of page</span></a><span +style='font-size:10.0pt;font-family:Verdana'> </span></p> + +<pre><o:p> </o:p></pre> + +<table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0 width=624 + style='width:6.5in;mso-cellspacing:0in;mso-padding-alt:0in 5.4pt 0in 5.4pt'> + <tr style='mso-yfti-irow:0;mso-yfti-firstrow:yes;mso-yfti-lastrow:yes; + height:1.5pt'> + <td style='background:aqua;padding:.75pt .75pt .75pt .75pt;height:1.5pt'> + <p class=MsoNormal><span style='font-size:2.0pt;mso-bidi-font-size:12.0pt'><o:p> </o:p></span></p> + </td> + </tr> +</table> + +<p style='tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span +style='font-size:7.5pt;font-family:"MS Sans Serif"'>� 2004</span> <span +style='font-size:7.5pt;font-family:"MS Sans Serif"'>Microsoft Corporation </span></p> + +</div> + +</body> + +</html> diff --git a/audio/sysvad/savedata.cpp b/audio/sysvad/savedata.cpp new file mode 100644 index 00000000..d621a21a --- /dev/null +++ b/audio/sysvad/savedata.cpp @@ -0,0 +1,1015 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + savedata.cpp + +Abstract: + + Implementation of SYSVAD data saving class. + + To save the playback data to disk, this class maintains a circular data + buffer, associated frame structures and worker items to save frames to + disk. + Each frame structure represents a portion of buffer. When that portion + of frame is full, a workitem is scheduled to save it to disk. + + + +--*/ +#pragma warning (disable : 4127) +#pragma warning (disable : 26165) + +#include <sysvad.h> +#include "savedata.h" +#include <ntstrsafe.h> // This is for using RtlStringcbPrintf + +#define SAVEDATA_POOLTAG 'TDVS' +#define SAVEDATA_POOLTAG1 '1DVS' +#define SAVEDATA_POOLTAG2 '2DVS' +#define SAVEDATA_POOLTAG3 '3DVS' +#define SAVEDATA_POOLTAG4 '4DVS' +#define SAVEDATA_POOLTAG5 '5DVS' +#define SAVEDATA_POOLTAG6 '6DVS' +#define SAVEDATA_POOLTAG7 '7DVS' + +//============================================================================= +// Defines +//============================================================================= +#define RIFF_TAG 0x46464952; +#define WAVE_TAG 0x45564157; +#define FMT__TAG 0x20746D66; +#define DATA_TAG 0x61746164; + +#define DEFAULT_FRAME_COUNT 2 +#define DEFAULT_FRAME_SIZE PAGE_SIZE * 4 +#define DEFAULT_BUFFER_SIZE DEFAULT_FRAME_SIZE * DEFAULT_FRAME_COUNT + +#define DEFAULT_FILE_NAME L"\\DosDevices\\C:\\STREAM" +#define OFFLOAD_FILE_NAME L"OFFLOAD" +#define HOST_FILE_NAME L"HOST" + +#define MAX_WORKER_ITEM_COUNT 15 + +//============================================================================= +// Statics +//============================================================================= +ULONG CSaveData::m_ulStreamId = 0; +ULONG CSaveData::m_ulOffloadStreamId = 0; + +#pragma code_seg("PAGE") +//============================================================================= +// CSaveData +//============================================================================= + +//============================================================================= +CSaveData::CSaveData() +: m_pDataBuffer(NULL), + m_FileHandle(NULL), + m_ulFrameCount(DEFAULT_FRAME_COUNT), + m_ulBufferSize(DEFAULT_BUFFER_SIZE), + m_ulFrameSize(DEFAULT_FRAME_SIZE), + m_ulBufferOffset(0), + m_ulFrameIndex(0), + m_fFrameUsed(NULL), + m_waveFormat(NULL), + m_pFilePtr(NULL), + m_fWriteDisabled(FALSE), + m_bInitialized(FALSE) +{ + PAGED_CODE(); + + m_FileHeader.dwRiff = RIFF_TAG; + m_FileHeader.dwFileSize = 0; + m_FileHeader.dwWave = WAVE_TAG; + m_FileHeader.dwFormat = FMT__TAG; + m_FileHeader.dwFormatLength = sizeof(WAVEFORMATEX); + + m_DataHeader.dwData = DATA_TAG; + m_DataHeader.dwDataLength = 0; + + RtlZeroMemory(&m_objectAttributes, sizeof(m_objectAttributes)); +} // CSaveData + +//============================================================================= +CSaveData::~CSaveData() +{ + PAGED_CODE(); + + DPF_ENTER(("[CSaveData::~CSaveData]")); + + // Update the wave header in data file with real file size. + // + if(m_pFilePtr) + { + m_FileHeader.dwFileSize = + (DWORD) m_pFilePtr->QuadPart - 2 * sizeof(DWORD); + m_DataHeader.dwDataLength = (DWORD) m_pFilePtr->QuadPart - + sizeof(m_FileHeader) - + m_FileHeader.dwFormatLength - + sizeof(m_DataHeader); + + if (STATUS_SUCCESS == KeWaitForSingleObject + ( + &m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + )) + { + if (NT_SUCCESS(FileOpen(FALSE))) + { + FileWriteHeader(); + + FileClose(); + } + + KeReleaseMutex(&m_FileSync, FALSE); + } + } + + if (m_waveFormat) + { + ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1); + m_waveFormat = NULL; + } + + if (m_fFrameUsed) + { + ExFreePoolWithTag(m_fFrameUsed, SAVEDATA_POOLTAG2); + m_fFrameUsed = NULL; + // NOTE : Do not release m_pFilePtr. + } + + if (m_FileName.Buffer) + { + ExFreePoolWithTag(m_FileName.Buffer, SAVEDATA_POOLTAG3); + m_FileName.Buffer = NULL; + } + + if (m_pDataBuffer) + { + ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4); + m_pDataBuffer = NULL; + } +} // CSaveData + +//============================================================================= +void +CSaveData::DestroyWorkItems +( + void +) +{ + PAGED_CODE(); + + if (m_pWorkItems) + { + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + if (m_pWorkItems[i].WorkItem!=NULL) + { + IoFreeWorkItem(m_pWorkItems[i].WorkItem); + m_pWorkItems[i].WorkItem = NULL; + } + } + ExFreePoolWithTag(m_pWorkItems, SAVEDATA_POOLTAG); + m_pWorkItems = NULL; + } + +} // DestroyWorkItems + +//============================================================================= +void +CSaveData::Disable +( + _In_ BOOL fDisable +) +{ + PAGED_CODE(); + + m_fWriteDisabled = fDisable; +} // Disable + +//============================================================================= +NTSTATUS +CSaveData::FileClose(void) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (m_FileHandle) + { + ntStatus = ZwClose(m_FileHandle); + m_FileHandle = NULL; + } + + return ntStatus; +} // FileClose + +//============================================================================= +NTSTATUS +CSaveData::FileOpen +( + _In_ BOOL fOverWrite +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + IO_STATUS_BLOCK ioStatusBlock; + + if( FALSE == m_bInitialized ) + { + return STATUS_UNSUCCESSFUL; + } + + if(!m_FileHandle) + { + ntStatus = + ZwCreateFile + ( + &m_FileHandle, + GENERIC_WRITE | SYNCHRONIZE, + &m_objectAttributes, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + fOverWrite ? FILE_OVERWRITE_IF : FILE_OPEN_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileOpen : Error opening data file]")); + } + } + + return ntStatus; +} // FileOpen + +//============================================================================= +NTSTATUS +CSaveData::FileWrite +( + _In_reads_bytes_(ulDataSize) PBYTE pData, + _In_ ULONG ulDataSize +) +{ + PAGED_CODE(); + + ASSERT(pData); + ASSERT(m_pFilePtr); + + NTSTATUS ntStatus; + + if (m_FileHandle) + { + IO_STATUS_BLOCK ioStatusBlock; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + pData, + ulDataSize, + m_pFilePtr, + NULL); + + if (NT_SUCCESS(ntStatus)) + { + ASSERT(ioStatusBlock.Information == ulDataSize); + + m_pFilePtr->QuadPart += ulDataSize; + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWrite : WriteFileError]")); + } + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWrite : File not open]")); + ntStatus = STATUS_INVALID_HANDLE; + } + + return ntStatus; +} // FileWrite + +//============================================================================= +NTSTATUS +CSaveData::FileWriteHeader(void) +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + + if (m_FileHandle && m_waveFormat) + { + IO_STATUS_BLOCK ioStatusBlock; + + m_pFilePtr->QuadPart = 0; + + m_FileHeader.dwFormatLength = (m_waveFormat->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + m_waveFormat->cbSize; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + &m_FileHeader, + sizeof(m_FileHeader), + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write File Header Error]")); + } + + m_pFilePtr->QuadPart += sizeof(m_FileHeader); + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + m_waveFormat, + m_FileHeader.dwFormatLength, + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Format Error]")); + } + + m_pFilePtr->QuadPart += m_FileHeader.dwFormatLength; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + &m_DataHeader, + sizeof(m_DataHeader), + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Data Header Error]")); + } + + m_pFilePtr->QuadPart += sizeof(m_DataHeader); + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : File not open]")); + ntStatus = STATUS_INVALID_HANDLE; + } + + return ntStatus; +} // FileWriteHeader +NTSTATUS +CSaveData::SetDeviceObject +( + _In_ PDEVICE_OBJECT DeviceObject +) +{ + PAGED_CODE(); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + m_pDeviceObject = DeviceObject; + return ntStatus; +} + +PDEVICE_OBJECT +CSaveData::GetDeviceObject +( + void +) +{ + PAGED_CODE(); + + return m_pDeviceObject; +} + +#pragma code_seg() +//============================================================================= +PSAVEWORKER_PARAM +CSaveData::GetNewWorkItem +( + void +) +{ + LARGE_INTEGER timeOut = { 0 }; + NTSTATUS ntStatus; + + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + ntStatus = + KeWaitForSingleObject + ( + &m_pWorkItems[i].EventDone, + Executive, + KernelMode, + FALSE, + &timeOut + ); + if (STATUS_SUCCESS == ntStatus) + { + if (m_pWorkItems[i].WorkItem) + return &(m_pWorkItems[i]); + else + return NULL; + } + } + + return NULL; +} // GetNewWorkItem +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CSaveData::Initialize +( + _In_ BOOL _bOffloaded +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WCHAR szTemp[MAX_PATH]; + size_t cLen; + + DPF_ENTER(("[CSaveData::Initialize]")); + + if (_bOffloaded) + { + m_ulOffloadStreamId++; + } + else + { + m_ulStreamId++; + } + + // Allocate data file name. + // + RtlStringCchPrintfW(szTemp, MAX_PATH, L"%s_%s_%d.wav", DEFAULT_FILE_NAME, _bOffloaded ? OFFLOAD_FILE_NAME : HOST_FILE_NAME, _bOffloaded ? m_ulOffloadStreamId : m_ulStreamId); + m_FileName.Length = 0; + ntStatus = RtlStringCchLengthW (szTemp, sizeof(szTemp)/sizeof(szTemp[0]), &cLen); + if (NT_SUCCESS(ntStatus)) + { + m_FileName.MaximumLength = (USHORT)((cLen * sizeof(WCHAR)) + sizeof(WCHAR));//convert to wchar and add room for NULL + m_FileName.Buffer = (PWSTR) + ExAllocatePoolWithTag + ( + PagedPool, + m_FileName.MaximumLength, + SAVEDATA_POOLTAG3 + ); + if (!m_FileName.Buffer) + { + DPF(D_TERSE, ("[Could not allocate memory for FileName]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Allocate memory for data buffer. + // + if (NT_SUCCESS(ntStatus)) + { + RtlStringCbCopyW(m_FileName.Buffer, m_FileName.MaximumLength, szTemp); + m_FileName.Length = (USHORT)wcslen(m_FileName.Buffer) * sizeof(WCHAR); + DPF(D_BLAB, ("[New DataFile -- %S", m_FileName.Buffer)); + + m_pDataBuffer = (PBYTE) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + m_ulBufferSize, + SAVEDATA_POOLTAG4 + ); + if (!m_pDataBuffer) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + RtlZeroMemory(m_pDataBuffer, m_ulBufferSize); + } + } + + // Allocate memory for frame usage flags and m_pFilePtr. + // + if (NT_SUCCESS(ntStatus)) + { + m_fFrameUsed = (PBOOL) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + m_ulFrameCount * sizeof(BOOL) + + sizeof(LARGE_INTEGER), + SAVEDATA_POOLTAG2 + ); + if (!m_fFrameUsed) + { + DPF(D_TERSE, ("[Could not allocate memory for frame flags]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Initialize the spinlock to synchronize access to the frames + // + KeInitializeSpinLock ( &m_FrameInUseSpinLock ) ; + + // Initialize the file mutex + // + KeInitializeMutex( &m_FileSync, 1 ) ; + + // Open the data file. + // + if (NT_SUCCESS(ntStatus)) + { + // m_fFrameUsed has additional memory to hold m_pFilePtr + // + m_pFilePtr = (PLARGE_INTEGER) + (((PBYTE) m_fFrameUsed) + m_ulFrameCount * sizeof(BOOL)); + RtlZeroMemory(m_fFrameUsed, m_ulFrameCount * sizeof(BOOL) + sizeof(LARGE_INTEGER)); + + // Create data file. + InitializeObjectAttributes + ( + &m_objectAttributes, + &m_FileName, + OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + m_bInitialized = TRUE; + + // Write wave header information to data file. + ntStatus = KeWaitForSingleObject + ( + &m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + ); + + if (STATUS_SUCCESS == ntStatus) + { + ntStatus = FileOpen(TRUE); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = FileWriteHeader(); + + FileClose(); + } + + KeReleaseMutex( &m_FileSync, FALSE ); + } + } + + return ntStatus; +} // Initialize + +//============================================================================= +NTSTATUS +CSaveData::InitializeWorkItems +( + _In_ PDEVICE_OBJECT DeviceObject +) +{ + PAGED_CODE(); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + DPF_ENTER(("[CSaveData::InitializeWorkItems]")); + + if (m_pWorkItems != NULL) + { + return ntStatus; + } + + m_pWorkItems = (PSAVEWORKER_PARAM) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + sizeof(SAVEWORKER_PARAM) * MAX_WORKER_ITEM_COUNT, + SAVEDATA_POOLTAG + ); + if (m_pWorkItems) + { + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + + m_pWorkItems[i].WorkItem = IoAllocateWorkItem(DeviceObject); + if(m_pWorkItems[i].WorkItem == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + KeInitializeEvent + ( + &m_pWorkItems[i].EventDone, + NotificationEvent, + TRUE + ); + } + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + return ntStatus; +} // InitializeWorkItems + +//============================================================================= + +IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; + +VOID +SaveFrameWorkerCallback +( + _In_ PDEVICE_OBJECT pDeviceObject, + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(pDeviceObject); + + PAGED_CODE(); + + ASSERT(Context); + + PSAVEWORKER_PARAM pParam = (PSAVEWORKER_PARAM) Context; + PCSaveData pSaveData; + + if (NULL == pParam) + { + // This is completely unexpected, assert here. + // + ASSERT(pParam); + return; + } + + DPF(D_VERBOSE, ("[SaveFrameWorkerCallback], %d", pParam->ulFrameNo)); + + ASSERT(pParam->pSaveData); + ASSERT(pParam->pSaveData->m_fFrameUsed); + + if (pParam->WorkItem) + { + pSaveData = pParam->pSaveData; + + if (STATUS_SUCCESS == KeWaitForSingleObject + ( + &pSaveData->m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + )) + { + if (NT_SUCCESS(pSaveData->FileOpen(FALSE))) + { + pSaveData->FileWrite(pParam->pData, pParam->ulDataSize); + pSaveData->FileClose(); + } + InterlockedExchange( (LONG *)&(pSaveData->m_fFrameUsed[pParam->ulFrameNo]), FALSE ); + + KeReleaseMutex( &pSaveData->m_FileSync, FALSE ); + } + } + + KeSetEvent(&pParam->EventDone, 0, FALSE); +} // SaveFrameWorkerCallback + +//============================================================================= +NTSTATUS +CSaveData::SetDataFormat +( + _In_ PKSDATAFORMAT pDataFormat +) +{ + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; + + DPF_ENTER(("[CSaveData::SetDataFormat]")); + + ASSERT(pDataFormat); + + PWAVEFORMATEX pwfx = NULL; + + if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND)) + { + pwfx = + &(((PKSDATAFORMAT_DSOUND) pDataFormat)->BufferDesc.WaveFormatEx); + } + else if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) + { + pwfx = &((PKSDATAFORMAT_WAVEFORMATEX) pDataFormat)->WaveFormatEx; + } + + if (pwfx) + { + // Free the previously allocated waveformat + if (m_waveFormat) + { + ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1); + } + + m_waveFormat = (PWAVEFORMATEX) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + pwfx->cbSize, + SAVEDATA_POOLTAG1 + ); + + if(m_waveFormat) + { + RtlCopyMemory( m_waveFormat, + pwfx, + (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + pwfx->cbSize); + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + return ntStatus; +} // SetDataFormat + +//============================================================================= +NTSTATUS +CSaveData::SetMaxWriteSize +( + _In_ ULONG ulMaxWriteSize +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG bufferSize = 0; + PBYTE buffer = NULL; + + DPF_ENTER(("[CSaveData::SetMaxWriteSize]")); + + // + // Compute new buffer size. + // + ntStatus = RtlULongMult(ulMaxWriteSize, DEFAULT_FRAME_COUNT, &bufferSize); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data, MaxWriteSize %u is too big]", ulMaxWriteSize)); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + // + // Alloc memory for buffer. + // + buffer = (PBYTE) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + bufferSize, + SAVEDATA_POOLTAG4 + ); + if (!buffer) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + RtlZeroMemory(buffer, bufferSize); + + // + // Free old one. + // + if (m_pDataBuffer) + { + ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4); + m_pDataBuffer = NULL; + } + + // + // Init new buffer settings. + // + m_pDataBuffer = buffer; + m_ulBufferSize = bufferSize; + m_ulFrameSize = ulMaxWriteSize; + + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} // SetDataFormat + +//============================================================================= +void +CSaveData::ReadData +( + _Inout_updates_bytes_all_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount +) +{ + UNREFERENCED_PARAMETER(pBuffer); + UNREFERENCED_PARAMETER(ulByteCount); + + PAGED_CODE(); + + // Not implemented yet. +} // ReadData + +//============================================================================= +#pragma code_seg() +void +CSaveData::SaveFrame +( + _In_ ULONG ulFrameNo, + _In_ ULONG ulDataSize +) +{ + PSAVEWORKER_PARAM pParam = NULL; + + DPF_ENTER(("[CSaveData::SaveFrame]")); + + pParam = GetNewWorkItem(); + if (pParam) + { + pParam->pSaveData = this; + pParam->ulFrameNo = ulFrameNo; + pParam->ulDataSize = ulDataSize; + pParam->pData = m_pDataBuffer + ulFrameNo * m_ulFrameSize; + KeResetEvent(&pParam->EventDone); + IoQueueWorkItem(pParam->WorkItem, SaveFrameWorkerCallback, + CriticalWorkQueue, (PVOID)pParam); + } +} // SaveFrame +#pragma code_seg("PAGE") +//============================================================================= +void +CSaveData::WaitAllWorkItems +( + void +) +{ + PAGED_CODE(); + + DPF_ENTER(("[CSaveData::WaitAllWorkItems]")); + + // Save the last partially-filled frame + if (m_ulBufferOffset > m_ulFrameIndex * m_ulFrameSize) + { + ULONG size; + + size = m_ulBufferOffset - m_ulFrameIndex * m_ulFrameSize; + SaveFrame(m_ulFrameIndex, size); + } + + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + DPF(D_VERBOSE, ("[Waiting for WorkItem] %d", i)); + KeWaitForSingleObject + ( + &(m_pWorkItems[i].EventDone), + Executive, + KernelMode, + FALSE, + NULL + ); + } +} // WaitAllWorkItems + +#pragma code_seg() +//============================================================================= +void +CSaveData::WriteData +( + _In_reads_bytes_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount +) +{ + ASSERT(pBuffer); + + BOOL fSaveFrame = FALSE; + ULONG ulSaveFrameIndex = 0; + KIRQL oldIrql; + + // If stream writing is disabled, then exit. + // + if (m_fWriteDisabled) + { + return; + } + + DPF_ENTER(("[CSaveData::WriteData ulByteCount=%lu]", ulByteCount)); + + if( 0 == ulByteCount ) + { + return; + } + + // The logic below assumes that write size is <= than frame size. + if (ulByteCount > m_ulFrameSize) + { + ulByteCount = m_ulFrameSize; + } + + // Check to see if this frame is available. + KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql); + if (!m_fFrameUsed[m_ulFrameIndex]) + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + + ULONG ulWriteBytes = ulByteCount; + + if( (m_ulBufferSize - m_ulBufferOffset) < ulWriteBytes ) + { + ulWriteBytes = m_ulBufferSize - m_ulBufferOffset; + } + + RtlCopyMemory(m_pDataBuffer + m_ulBufferOffset, pBuffer, ulWriteBytes); + m_ulBufferOffset += ulWriteBytes; + + // Check to see if we need to save this frame + if (m_ulBufferOffset >= ((m_ulFrameIndex + 1) * m_ulFrameSize)) + { + fSaveFrame = TRUE; + } + + // Loop the buffer, if we reached the end. + if (m_ulBufferOffset == m_ulBufferSize) + { + fSaveFrame = TRUE; + m_ulBufferOffset = 0; + } + + if (fSaveFrame) + { + InterlockedExchange( (LONG *)&(m_fFrameUsed[m_ulFrameIndex]), TRUE ); + ulSaveFrameIndex = m_ulFrameIndex; + m_ulFrameIndex = (m_ulFrameIndex + 1) % m_ulFrameCount; + } + + // Write the left over if the next frame is available. + if (ulWriteBytes != ulByteCount) + { + KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql ); + if (!m_fFrameUsed[m_ulFrameIndex]) + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + RtlCopyMemory + ( + m_pDataBuffer + m_ulBufferOffset, + pBuffer + ulWriteBytes, + ulByteCount - ulWriteBytes + ); + + m_ulBufferOffset += ulByteCount - ulWriteBytes; + } + else + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql); + DPF(D_BLAB, ("[Frame overflow, next frame is in use]")); + } + } + + if (fSaveFrame) + { + SaveFrame(ulSaveFrameIndex, m_ulFrameSize); + } + } + else + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + DPF(D_BLAB, ("[Frame %d is in use]", m_ulFrameIndex)); + } + +} // WriteData + + diff --git a/audio/sysvad/savedata.h b/audio/sysvad/savedata.h new file mode 100644 index 00000000..a1525bda --- /dev/null +++ b/audio/sysvad/savedata.h @@ -0,0 +1,193 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + savedata.h + +Abstract: + + Declaration of SYSVAD data saving class. This class supplies services +to save data to disk. + + +--*/ + +#ifndef _SYSVAD_SAVEDATA_H +#define _SYSVAD_SAVEDATA_H + +//----------------------------------------------------------------------------- +// Forward declaration +//----------------------------------------------------------------------------- +class CSaveData; +typedef CSaveData *PCSaveData; + + +//----------------------------------------------------------------------------- +// Structs +//----------------------------------------------------------------------------- + +// Parameter to workitem. +#include <pshpack1.h> +typedef struct _SAVEWORKER_PARAM { + PIO_WORKITEM WorkItem; + ULONG ulFrameNo; + ULONG ulDataSize; + PBYTE pData; + PCSaveData pSaveData; + KEVENT EventDone; +} SAVEWORKER_PARAM; +typedef SAVEWORKER_PARAM *PSAVEWORKER_PARAM; +#include <poppack.h> + +// wave file header. +#include <pshpack1.h> +typedef struct _OUTPUT_FILE_HEADER +{ + DWORD dwRiff; + DWORD dwFileSize; + DWORD dwWave; + DWORD dwFormat; + DWORD dwFormatLength; +} OUTPUT_FILE_HEADER; +typedef OUTPUT_FILE_HEADER *POUTPUT_FILE_HEADER; + +typedef struct _OUTPUT_DATA_HEADER +{ + DWORD dwData; + DWORD dwDataLength; +} OUTPUT_DATA_HEADER; +typedef OUTPUT_DATA_HEADER *POUTPUT_DATA_HEADER; + +#include <poppack.h> + +//----------------------------------------------------------------------------- +// Classes +//----------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////////// +// CSaveData +// Saves the wave data to disk. +// +IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; + +class CSaveData +{ +protected: + UNICODE_STRING m_FileName; // DataFile name. + HANDLE m_FileHandle; // DataFile handle. + PBYTE m_pDataBuffer; // Data buffer. + ULONG m_ulBufferSize; // Total buffer size. + + ULONG m_ulFrameIndex; // Current Frame. + ULONG m_ulFrameCount; // Frame count. + ULONG m_ulFrameSize; + ULONG m_ulBufferOffset; // index in buffer. + PBOOL m_fFrameUsed; // Frame usage table. + KSPIN_LOCK m_FrameInUseSpinLock; // Spinlock for synch. + KMUTEX m_FileSync; // Synchronizes file access + + OBJECT_ATTRIBUTES m_objectAttributes; // Used for opening file. + + OUTPUT_FILE_HEADER m_FileHeader; + PWAVEFORMATEX m_waveFormat; + OUTPUT_DATA_HEADER m_DataHeader; + PLARGE_INTEGER m_pFilePtr; + + static PDEVICE_OBJECT m_pDeviceObject; + static ULONG m_ulStreamId; + static ULONG m_ulOffloadStreamId; + static PSAVEWORKER_PARAM m_pWorkItems; + + BOOL m_fWriteDisabled; + + BOOL m_bInitialized; + +public: + CSaveData(); + ~CSaveData(); + + static NTSTATUS InitializeWorkItems + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + static void DestroyWorkItems + ( + void + ); + void Disable + ( + _In_ BOOL fDisable + ); + static PSAVEWORKER_PARAM GetNewWorkItem + ( + void + ); + NTSTATUS Initialize + ( + _In_ BOOL _bOffloaded + ); + static NTSTATUS SetDeviceObject + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + static PDEVICE_OBJECT GetDeviceObject + ( + void + ); + void ReadData + ( + _Inout_updates_bytes_all_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount + ); + NTSTATUS SetDataFormat + ( + _In_ PKSDATAFORMAT pDataFormat + ); + NTSTATUS SetMaxWriteSize + ( + _In_ ULONG ulMaxWriteSize + ); + void WaitAllWorkItems + ( + void + ); + void WriteData + ( + _In_reads_bytes_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount + ); + +private: + NTSTATUS FileClose + ( + void + ); + NTSTATUS FileOpen + ( + _In_ BOOL fOverWrite + ); + NTSTATUS FileWrite + ( + _In_reads_bytes_(ulDataSize) PBYTE pData, + _In_ ULONG ulDataSize + ); + NTSTATUS FileWriteHeader + ( + void + ); + + void SaveFrame + ( + _In_ ULONG ulFrameNo, + _In_ ULONG ulDataSize + ); + + friend + IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; +}; +typedef CSaveData *PCSaveData; + +#endif + diff --git a/audio/sysvad/sources.inc b/audio/sysvad/sources.inc new file mode 100644 index 00000000..7396cf46 --- /dev/null +++ b/audio/sysvad/sources.inc @@ -0,0 +1,34 @@ +TARGETTYPE=DRIVER + +TARGETLIBS= \ + $(DDK_LIB_PATH)\portcls.lib\ + $(DDK_LIB_PATH)\stdunk.lib \ + $(SDK_LIB_PATH)\libcntpr.lib + +INCLUDES= \ + $(DDK_INC_PATH); \ + .. + +MSC_WARNING_LEVEL=-W4 -WX + +C_DEFINES= $(C_DEFINES) -D_WIN32 -DUNICODE -D_UNICODE -DPC_IMPLEMENTATION + +# +# Different levels of debug printage. First is nothing but +# catastrophic errors, last is everything under the sun. +# +#C_DEFINES= $(C_DEFINES) -DDEBUG_LEVEL=DEBUGLVL_ERROR +C_DEFINES= $(C_DEFINES) -DDEBUG_LEVEL=DEBUGLVL_TERSE +#C_DEFINES= $(C_DEFINES) -DDEBUG_LEVEL=DEBUGLVL_VERBOSE +#C_DEFINES= $(C_DEFINES) -DDEBUG_LEVEL=DEBUGLVL_BLAB + +SOURCES=\ + ..\adapter.cpp \ + ..\basetopo.cpp \ + ..\common.cpp \ + ..\hw.cpp \ + ..\kshelper.cpp \ + ..\savedata.cpp \ + ..\tonegenerator.cpp \ + ..\sysvad.rc + diff --git a/audio/sysvad/sysvad.h b/audio/sysvad/sysvad.h new file mode 100644 index 00000000..0e35aebd --- /dev/null +++ b/audio/sysvad/sysvad.h @@ -0,0 +1,265 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Sysvad.h + +Abstract: + + Header file for common stuff. + +--*/ + +#ifndef _SYSVAD_H_ +#define _SYSVAD_H_ + +#include <portcls.h> +#include <stdunk.h> +#include <ksdebug.h> +#include <ntintsafe.h> +#include <wdf.h> +#include <wdfminiport.h> +#include <MsApoFxProxy.h> +#include <Ntstrsafe.h> + +//============================================================================= +// Defines +//============================================================================= + +// Version number. Revision numbers are specified for each sample. +#define SYSVAD_VERSION 1 + +// Revision number. +#define SYSVAD_REVISION 0 + +// Product Id +// {5B722BF8-F0AB-47ee-B9C8-8D61D31375A1} +#define STATIC_PID_SYSVAD\ + 0x5b722bf8, 0xf0ab, 0x47ee, 0xb9, 0xc8, 0x8d, 0x61, 0xd3, 0x13, 0x75, 0xa1 +DEFINE_GUIDSTRUCT("5B722BF8-F0AB-47ee-B9C8-8D61D31375A1", PID_SYSVAD); +#define PID_SYSVAD DEFINE_GUIDNAMED(PID_SYSVAD) + +// Pool tag used for SYSVAD allocations +#define SYSVAD_POOLTAG 'DVSM' + +// Debug module name +#define STR_MODULENAME "SYSVAD: " + +// Debug utility macros +#define D_FUNC 4 +#define D_BLAB DEBUGLVL_BLAB +#define D_VERBOSE DEBUGLVL_VERBOSE +#define D_TERSE DEBUGLVL_TERSE +#define D_ERROR DEBUGLVL_ERROR +#define DPF _DbgPrintF +#define DPF_ENTER(x) DPF(D_FUNC, x) + +// Channel orientation +#define CHAN_LEFT 0 +#define CHAN_RIGHT 1 +#define CHAN_MASTER (-1) + +// Dma Settings. +#define DMA_BUFFER_SIZE 0x16000 + +#define KSPROPERTY_TYPE_ALL KSPROPERTY_TYPE_BASICSUPPORT | \ + KSPROPERTY_TYPE_GET | \ + KSPROPERTY_TYPE_SET + +// Specific node numbers +#define DEV_SPECIFIC_VT_BOOL 9 +#define DEV_SPECIFIC_VT_I4 10 +#define DEV_SPECIFIC_VT_UI4 11 + +#define _100NS_PER_MILLISECOND 10000 // number of 100ns units per millisecond + +// Default volume settings. +#define VOLUME_STEPPING_DELTA 0x8000 +#define VOLUME_SIGNED_MAXIMUM 0x00000000 +#define VOLUME_SIGNED_MINIMUM (-96 * 0x10000) + +// Default peak meter settings +#define PEAKMETER_STEPPING_DELTA 0x1000 +#define PEAKMETER_SIGNED_MAXIMUM LONG_MAX +#define PEAKMETER_SIGNED_MINIMUM LONG_MIN + +#define VALUE_NORMALIZE_P(v, step) \ + ((((v) + (step)/2) / (step)) * (step)) + +#define VALUE_NORMALIZE(v, step) \ + ((v) > 0 ? VALUE_NORMALIZE_P((v), (step)) : -(VALUE_NORMALIZE_P(-(v), (step)))) + +#define VALUE_NORMALIZE_IN_RANGE_EX(v, min, max, step) \ + ((v) > (max) ? (max) : \ + (v) < (min) ? (min) : \ + VALUE_NORMALIZE((v), (step))) + +// to normalize volume values. +#define VOLUME_NORMALIZE_IN_RANGE(v) \ + VALUE_NORMALIZE_IN_RANGE_EX((v), VOLUME_SIGNED_MINIMUM, VOLUME_SIGNED_MAXIMUM, VOLUME_STEPPING_DELTA) + +// to normalize sample peak meter. +#define PEAKMETER_NORMALIZE_IN_RANGE(v) \ + VALUE_NORMALIZE_IN_RANGE_EX((v), PEAKMETER_SIGNED_MINIMUM, PEAKMETER_SIGNED_MAXIMUM, PEAKMETER_STEPPING_DELTA) + +#define ALL_CHANNELS_ID UINT32_MAX + +// Flags to identify stream processing mode +typedef enum { + SIGNALPROCESSINGMODE_NONE = 0x00, + SIGNALPROCESSINGMODE_DEFAULT = 0x01, + SIGNALPROCESSINGMODE_RAW = 0x02, + SIGNALPROCESSINGMODE_COMMUNICATIONS = 0x04, + SIGNALPROCESSINGMODE_SPEECH = 0x08, + SIGNALPROCESSINGMODE_NOTIFICATION = 0x10, + SIGNALPROCESSINGMODE_MEDIA = 0x20, + SIGNALPROCESSINGMODE_MOVIE = 0x40 +} SIGNALPROCESSINGMODE; + +#define MAP_GUID_TO_MODE(guid, mode) \ + if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_DEFAULT)) \ + { \ + mode = SIGNALPROCESSINGMODE_DEFAULT; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_RAW)) \ + { \ + mode = SIGNALPROCESSINGMODE_RAW; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS)) \ + { \ + mode = SIGNALPROCESSINGMODE_COMMUNICATIONS; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_SPEECH)) \ + { \ + mode = SIGNALPROCESSINGMODE_SPEECH; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION)) \ + { \ + mode = SIGNALPROCESSINGMODE_NOTIFICATION; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_MEDIA)) \ + { \ + mode = SIGNALPROCESSINGMODE_MEDIA; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_MOVIE)) \ + { \ + mode = SIGNALPROCESSINGMODE_MOVIE; \ + } \ + else \ + { \ + ASSERT(FALSE && "Unknown Signal Processing Mode"); \ + mode = SIGNALPROCESSINGMODE_NONE; \ + } + +#define VERIFY_MODE_RESOURCES_AVAILABLE(modes, guid, status) \ + { \ + SIGNALPROCESSINGMODE mode = SIGNALPROCESSINGMODE_NONE; \ + MAP_GUID_TO_MODE(guid, mode); \ + if (SIGNALPROCESSINGMODE_NONE != mode) \ + { \ + status = (modes & mode) ? STATUS_INSUFFICIENT_RESOURCES : STATUS_SUCCESS; \ + } \ + else \ + { \ + status = STATUS_INVALID_PARAMETER; \ + } \ + } + +#define ALLOCATE_MODE_RESOURCES(modes, guid) \ + { \ + SIGNALPROCESSINGMODE mode = SIGNALPROCESSINGMODE_NONE; \ + MAP_GUID_TO_MODE(guid, mode); \ + modes |= mode; \ + } + +#define FREE_MODE_RESOURCES(modes, guid) \ + { \ + SIGNALPROCESSINGMODE mode = SIGNALPROCESSINGMODE_NONE; \ + MAP_GUID_TO_MODE(guid, mode); \ + modes &= (~mode); \ + } + +// Define the value data type for supported sound detector patterns. Only +// one pattern type is supported in this sample. +typedef struct { + KSMULTIPLE_ITEM MultipleItem; + GUID PatternType[1]; +} CONTOSO_SUPPORTEDPATTERNSVALUE; + +//============================================================================= +// Typedefs +//============================================================================= + +// Flags to identify stream processing mode +typedef enum { + CONNECTIONTYPE_TOPOLOGY_OUTPUT = 0, + CONNECTIONTYPE_WAVE_OUTPUT = 1 +} CONNECTIONTYPE; + +// Connection table for registering topology/wave bridge connection +typedef struct _PHYSICALCONNECTIONTABLE +{ + ULONG ulTopology; + ULONG ulWave; + CONNECTIONTYPE eType; +} PHYSICALCONNECTIONTABLE, *PPHYSICALCONNECTIONTABLE; + +// +// This is the structure of the portclass FDO device extension Nt has created +// for us. We keep the adapter common object here. +// +struct IAdapterCommon; +typedef struct _PortClassDeviceContext // 32 64 Byte offsets for 32 and 64 bit architectures +{ + ULONG_PTR m_pulReserved1[2]; // 0-7 0-15 First two pointers are reserved. + PDEVICE_OBJECT m_DoNotUsePhysicalDeviceObject; // 8-11 16-23 Reserved pointer to our Physical Device Object (PDO). + PVOID m_pvReserved2; // 12-15 24-31 Reserved pointer to our Start Device function. + PVOID m_pvReserved3; // 16-19 32-39 "Out Memory" according to DDK. + IAdapterCommon* m_pCommon; // 20-23 40-47 Pointer to our adapter common object. +#ifdef _USE_SingleComponentMultiFxStates + POHANDLE m_poHandle; // 24-27 48-55 PoFxDevice handle. +#else + PVOID m_pvUnused1; // 24-27 48-55 Unused space. +#endif // _USE_SingleComponentMultiFxStates + PVOID m_pvUnused2; // 28-31 56-63 Unused space. + + // Anything after above line should not be used. + // This actually goes on for (64*sizeof(ULONG_PTR)) but it is all opaque. +} PortClassDeviceContext; + +// +// Global settings. +// +extern DWORD g_DoNotCreateDataFiles; +extern DWORD g_DisableBthScoBypass; + +//============================================================================= +// Function prototypes +//============================================================================= + +// Generic topology handler +NTSTATUS PropertyHandler_Topology +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// Default WaveFilter automation table. +// Handles the GeneralComponentId request. +NTSTATUS PropertyHandler_WaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +NTSTATUS PropertyHandler_OffloadPin +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// common.h uses some of the above definitions. +#include "common.h" +#include "kshelper.h" + +#endif + diff --git a/audio/sysvad/sysvad.rc b/audio/sysvad/sysvad.rc new file mode 100644 index 00000000..cd8f2faa --- /dev/null +++ b/audio/sysvad/sysvad.rc @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + sysvad.rc + +Abstract: + + +--*/ + +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SOUND +#define VER_FILEDESCRIPTION_STR "Microsoft Virtual Audio Device Slate Sample Driver" +#define VER_INTERNALNAME_STR "slateaudiosample.sys" +#define VER_ORIGINALFILENAME_STR "slateaudiosample.sys" + +#define VER_LEGALCOPYRIGHT_YEARS "2013" +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) Microsoft Corp." VER_LEGALCOPYRIGHT_YEARS + +#include "common.ver" + + + diff --git a/audio/sysvad/sysvad.sln b/audio/sysvad/sysvad.sln new file mode 100644 index 00000000..a177531a --- /dev/null +++ b/audio/sysvad/sysvad.sln @@ -0,0 +1,121 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{C3727A52-F4BA-46A5-87DD-422C5A6E6487}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "KeywordDetectorAdapter", "KeywordDetectorAdapter", "{CAA240C8-9145-42EC-850F-72D0A9D611D8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PropPageExtensions", "PropPageExtensions", "{4894017C-6595-4FE8-8E11-D89D220C30A6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwapAPO", "SwapAPO", "{AC42EA6C-395E-4886-8BEF-7B348AD02B2B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "APO", "APO", "{2CED13CF-D957-488D-981D-0D68A8964814}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EndpointsCommon", "EndpointsCommon", "{0ADDFDA3-E93A-49F4-8506-4F0F77C94BBC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PhoneAudioSample", "PhoneAudioSample", "{90897FF0-653E-44A3-A37D-AE5B59FDAE64}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TabletAudioSample", "TabletAudioSample", "{E93D14DB-304C-457B-AF3F-70F74A9B9D8E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{5F7004D7-03BD-44AA-9FDD-6677B2C93469}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeywordDetectorContosoAdapter", "KeywordDetectorAdapter\KeywordDetectorContosoAdapter.vcxproj", "{92E28772-ADE3-4367-B679-6DCF6FFA75F1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PropPageExt", "SwapAPO\PropPageExtensions\PropPageExt.vcxproj", "{76DBA957-38F6-47ED-A5C0-AD587D08FF12}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SwapAPO", "SwapAPO\APO\SwapAPO.vcxproj", "{2A1B7375-D9CB-4067-AEDE-180D75B5934D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EndpointsCommon", "EndpointsCommon\EndpointsCommon.vcxproj", "{E3BA10BE-08FF-4244-86C6-C788072CEA25}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PhoneAudioSample", "PhoneAudioSample\PhoneAudioSample.vcxproj", "{43FF11E5-B4C4-409A-AE4B-13342918B6F8}" + ProjectSection(ProjectDependencies) = postProject + {E3BA10BE-08FF-4244-86C6-C788072CEA25} = {E3BA10BE-08FF-4244-86C6-C788072CEA25} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TabletAudioSample", "TabletAudioSample\TabletAudioSample.vcxproj", "{29A5DD25-AB56-4DEE-96AB-21EC2926A300}" + ProjectSection(ProjectDependencies) = postProject + {E3BA10BE-08FF-4244-86C6-C788072CEA25} = {E3BA10BE-08FF-4244-86C6-C788072CEA25} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Debug|Win32.ActiveCfg = Debug|Win32 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Debug|Win32.Build.0 = Debug|Win32 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Release|Win32.ActiveCfg = Release|Win32 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Release|Win32.Build.0 = Release|Win32 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Debug|x64.ActiveCfg = Debug|x64 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Debug|x64.Build.0 = Debug|x64 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Release|x64.ActiveCfg = Release|x64 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Release|x64.Build.0 = Release|x64 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Debug|Win32.ActiveCfg = Debug|Win32 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Debug|Win32.Build.0 = Debug|Win32 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Release|Win32.ActiveCfg = Release|Win32 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Release|Win32.Build.0 = Release|Win32 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Debug|x64.ActiveCfg = Debug|x64 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Debug|x64.Build.0 = Debug|x64 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Release|x64.ActiveCfg = Release|x64 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Release|x64.Build.0 = Release|x64 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Debug|Win32.ActiveCfg = Debug|Win32 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Debug|Win32.Build.0 = Debug|Win32 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Release|Win32.ActiveCfg = Release|Win32 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Release|Win32.Build.0 = Release|Win32 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Debug|x64.ActiveCfg = Debug|x64 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Debug|x64.Build.0 = Debug|x64 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Release|x64.ActiveCfg = Release|x64 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Release|x64.Build.0 = Release|x64 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Debug|Win32.ActiveCfg = Debug|Win32 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Debug|Win32.Build.0 = Debug|Win32 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Release|Win32.ActiveCfg = Release|Win32 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Release|Win32.Build.0 = Release|Win32 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Debug|x64.ActiveCfg = Debug|x64 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Debug|x64.Build.0 = Debug|x64 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Release|x64.ActiveCfg = Release|x64 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Release|x64.Build.0 = Release|x64 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Debug|Win32.ActiveCfg = Debug|Win32 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Debug|Win32.Build.0 = Debug|Win32 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Release|Win32.ActiveCfg = Release|Win32 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Release|Win32.Build.0 = Release|Win32 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Debug|x64.ActiveCfg = Debug|x64 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Debug|x64.Build.0 = Debug|x64 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Release|x64.ActiveCfg = Release|x64 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Release|x64.Build.0 = Release|x64 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Debug|Win32.ActiveCfg = Debug|Win32 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Debug|Win32.Build.0 = Debug|Win32 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Release|Win32.ActiveCfg = Release|Win32 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Release|Win32.Build.0 = Release|Win32 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Debug|x64.ActiveCfg = Debug|x64 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Debug|x64.Build.0 = Debug|x64 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Release|x64.ActiveCfg = Release|x64 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Release|x64.Build.0 = Release|x64 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Debug|Win32.ActiveCfg = Debug|Win32 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Debug|Win32.Build.0 = Debug|Win32 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Release|Win32.ActiveCfg = Release|Win32 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Release|Win32.Build.0 = Release|Win32 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Debug|x64.ActiveCfg = Debug|x64 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Debug|x64.Build.0 = Debug|x64 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Release|x64.ActiveCfg = Release|x64 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5F7004D7-03BD-44AA-9FDD-6677B2C93469} = {C3727A52-F4BA-46A5-87DD-422C5A6E6487} + {92E28772-ADE3-4367-B679-6DCF6FFA75F1} = {CAA240C8-9145-42EC-850F-72D0A9D611D8} + {76DBA957-38F6-47ED-A5C0-AD587D08FF12} = {4894017C-6595-4FE8-8E11-D89D220C30A6} + {2A1B7375-D9CB-4067-AEDE-180D75B5934D} = {2CED13CF-D957-488D-981D-0D68A8964814} + {E3BA10BE-08FF-4244-86C6-C788072CEA25} = {0ADDFDA3-E93A-49F4-8506-4F0F77C94BBC} + {43FF11E5-B4C4-409A-AE4B-13342918B6F8} = {90897FF0-653E-44A3-A37D-AE5B59FDAE64} + {29A5DD25-AB56-4DEE-96AB-21EC2926A300} = {E93D14DB-304C-457B-AF3F-70F74A9B9D8E} + {4894017C-6595-4FE8-8E11-D89D220C30A6} = {AC42EA6C-395E-4886-8BEF-7B348AD02B2B} + {2CED13CF-D957-488D-981D-0D68A8964814} = {AC42EA6C-395E-4886-8BEF-7B348AD02B2B} + EndGlobalSection +EndGlobal diff --git a/audio/sysvad/tabletaudiosample.inf b/audio/sysvad/tabletaudiosample.inf Binary files differnew file mode 100644 index 00000000..ab5596e8 --- /dev/null +++ b/audio/sysvad/tabletaudiosample.inf |
