diff options
| author | judzmura <[email protected]> | 2021-03-01 13:12:34 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-03-01 13:12:34 -0800 |
| commit | 9a2e9aecb9135e782c434e617f57bfbdf9d8f2fd (patch) | |
| tree | c67dd7a72a15a8d72c7bb6c6e19c1086d5e15f08 /audio/simpleaudiosample/Source/Main | |
| parent | 6c41d837b87a0b70fe5dcee78baeba4ef28c45ad (diff) | |
Add SimpleAudioSample driver. (#577)
* Add SimpleAudioSample driver.
* Fixed outdated copyrights and updated readme.
* Added Filters as a dependency for Main and defined FileDigestAlgorithm as SHA256.
Diffstat (limited to 'audio/simpleaudiosample/Source/Main')
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/Main.vcxproj | 478 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/Main.vcxproj.Filters | 44 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/NewDelete.cpp | 141 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/SimpleAudioSample.inx | bin | 0 -> 15164 bytes | |||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/SimpleAudioSample.rc | 28 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/adapter.cpp | 844 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/basetopo.cpp | 688 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/common.cpp | 2772 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/mintopo.cpp | 557 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/minwavert.cpp | 1683 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/minwavert.h | 302 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/minwavertstream.cpp | 1644 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/minwavertstream.h | 248 |
13 files changed, 9429 insertions, 0 deletions
diff --git a/audio/simpleaudiosample/Source/Main/Main.vcxproj b/audio/simpleaudiosample/Source/Main/Main.vcxproj new file mode 100644 index 00000000..fabab156 --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/Main.vcxproj @@ -0,0 +1,478 @@ +<?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|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</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>{E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}</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>{2A9A946E-F017-4E0D-BF75-AD81E7E97D64}</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 Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <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 Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <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 Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets"> + <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 Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets"> + <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> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <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> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> + </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> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <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> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> + </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> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> + </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> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>SimpleAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <TargetName>SimpleAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>SimpleAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <TargetName>SimpleAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>SimpleAudioSample</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>SimpleAudioSample</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Utilities\$(IntDir)\Utilities.lib</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Filters\$(IntDir)\Filters.lib</AdditionalDependencies> + </Link> + <ClCompile> + <!-- + Use one of the following pre-processor defines to select the right power management: + - _USE_IPortClsRuntimePower + - _USE_SingleComponentMultiFxStates + Note that the single-component/multi-state feature requires also a registry + value set via the INF. The name of the registry value is SingleComponentMultiFxStates. + see INF file for more info. + --> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Utilities\$(IntDir)\Utilities.lib</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Filters\$(IntDir)\Filters.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Utilities\$(IntDir)\Utilities.lib</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Filters\$(IntDir)\Filters.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Utilities\$(IntDir)\Utilities.lib</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Filters\$(IntDir)\Filters.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Utilities\$(IntDir)\Utilities.lib</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Filters\$(IntDir)\Filters.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Utilities\$(IntDir)\Utilities.lib</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);.\..\Filters\$(IntDir)\Filters.lib</AdditionalDependencies> + </Link> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_USE_IPortClsRuntimePower</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Utilities</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Filters</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="adapter.cpp" /> + <ClCompile Include="basetopo.cpp" /> + <ClCompile Include="common.cpp" /> + <ClCompile Include="mintopo.cpp" /> + <ClCompile Include="minwavert.cpp" /> + <ClCompile Include="minwavertstream.cpp" /> + <ClCompile Include="newdelete.cpp" /> + <ResourceCompile Include="SimpleAudioSample.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inx)" Include="*.inx" /> + <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> diff --git a/audio/simpleaudiosample/Source/Main/Main.vcxproj.Filters b/audio/simpleaudiosample/Source/Main/Main.vcxproj.Filters new file mode 100644 index 00000000..0669f019 --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/Main.vcxproj.Filters @@ -0,0 +1,44 @@ +<?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>{7FE326AE-6214-4FE0-A27B-F229A098BDBC}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{64C16D2B-849C-4264-A615-18A8E8AC9505}</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>{46FAF4E8-F18C-4797-9CFA-2F3473BCB316}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{EE8F0A00-C1F0-4CB4-80E3-5FCC378E0BF4}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\*.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="*.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="SimpleAudioSample.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Inf Include="*.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> +</Project> diff --git a/audio/simpleaudiosample/Source/Main/NewDelete.cpp b/audio/simpleaudiosample/Source/Main/NewDelete.cpp new file mode 100644 index 00000000..8ef3797d --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/NewDelete.cpp @@ -0,0 +1,141 @@ +/***************************************************************************** +* NewDelete.cpp - CPP placement new and delete operators implementation +***************************************************************************** +* Copyright (c) Microsoft Corporation All Rights Reserved +* +* Module Name: +* +* NewDelete.cpp +* +* Abstract: +* +* Definition of placement new and delete operators. +* +*/ + +#ifdef _NEW_DELETE_OPERATORS_ +#ifdef __cplusplus +extern "C" { +#include <wdm.h> +} +#else +#include <wdm.h> +#endif + +#include "newDelete.h" +#include "definitions.h" + +#pragma code_seg() +/***************************************************************************** +* Functions +*/ + +/***************************************************************************** +* ::new() +***************************************************************************** +* New function for creating objects with a specified allocation tag. +*/ +PVOID operator new +( + size_t iSize, + POOL_FLAGS poolFlags, + ULONG tag +) +{ + PVOID result = ExAllocatePool2(poolFlags, iSize, tag); + + return result; +} + + +/***************************************************************************** +* ::new() +***************************************************************************** +* New function for creating objects with a specified allocation tag. +*/ +PVOID operator new +( + size_t iSize, + POOL_FLAGS poolFlags +) +{ + PVOID result = ExAllocatePool2(poolFlags, iSize, SIMPLEAUDIOSAMPLE_POOLTAG); + + return result; +} + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Delete with tag function. +*/ +void __cdecl operator delete +( + PVOID pVoid, + ULONG tag +) +{ + if (pVoid) + { + ExFreePoolWithTag(pVoid, tag); + } +} + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Sized Delete function. +*/ +void __cdecl operator delete +( + _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid, + _In_ size_t cbSize +) +{ + UNREFERENCED_PARAMETER(cbSize); + + if (pVoid) + { + ExFreePoolWithTag(pVoid, SIMPLEAUDIOSAMPLE_POOLTAG); + } +} + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Sized Array Delete function. +*/ +void __cdecl operator delete[] +( + _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid, + _In_ size_t cbSize +) +{ + UNREFERENCED_PARAMETER(cbSize); + + if (pVoid) + { + ExFreePoolWithTag(pVoid, SIMPLEAUDIOSAMPLE_POOLTAG); + } +} + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Array Delete function. +*/ +void __cdecl operator delete[] +( + _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid +) +{ + if (pVoid) + { + ExFreePoolWithTag(pVoid, SIMPLEAUDIOSAMPLE_POOLTAG); + } +} +#endif//_NEW_DELETE_OPERATORS_ diff --git a/audio/simpleaudiosample/Source/Main/SimpleAudioSample.inx b/audio/simpleaudiosample/Source/Main/SimpleAudioSample.inx Binary files differnew file mode 100644 index 00000000..a63bb0ba --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/SimpleAudioSample.inx diff --git a/audio/simpleaudiosample/Source/Main/SimpleAudioSample.rc b/audio/simpleaudiosample/Source/Main/SimpleAudioSample.rc new file mode 100644 index 00000000..7fbb4893 --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/SimpleAudioSample.rc @@ -0,0 +1,28 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + SimpleAudioSample.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 Simple Audio Sample Driver" +#define VER_INTERNALNAME_STR "SimpleAudioSample.sys" +#define VER_ORIGINALFILENAME_STR "SimpleAudioSample.sys" + +#define VER_LEGALCOPYRIGHT_YEARS "2013" +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) Microsoft Corp." VER_LEGALCOPYRIGHT_YEARS + +#include "common.ver" + + diff --git a/audio/simpleaudiosample/Source/Main/adapter.cpp b/audio/simpleaudiosample/Source/Main/adapter.cpp new file mode 100644 index 00000000..105f7fde --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/adapter.cpp @@ -0,0 +1,844 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + adapter.cpp + +Abstract: + + Setup and miniport installation. No resources are used by simple audio sample. + 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 "definitions.h" +#include "endpoints.h" +#include "minipairs.h" + +typedef void (*fnPcDriverUnload) (PDRIVER_OBJECT); +fnPcDriverUnload gPCDriverUnloadRoutine = NULL; +extern "C" DRIVER_UNLOAD DriverUnload; + +//----------------------------------------------------------------------------- +// 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; // default is off. +DWORD g_DisableToneGenerator = 0; // default is to generate tones. +UNICODE_STRING g_RegistryPath; // This is used to store the registry settings path for the driver + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +#pragma code_seg("PAGE") +void ReleaseRegistryStringBuffer() +{ + if (g_RegistryPath.Buffer != NULL) + { + ExFreePool(g_RegistryPath.Buffer); + g_RegistryPath.Buffer = NULL; + g_RegistryPath.Length = 0; + g_RegistryPath.MaximumLength = 0; + } +} + +//============================================================================= +#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]")); + + ReleaseRegistryStringBuffer(); + + 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 +CopyRegistrySettingsPath( + _In_ PUNICODE_STRING RegistryPath +) +/*++ + +Routine Description: + +Copies the following registry path to a global variable. + +\REGISTRY\MACHINE\SYSTEM\ControlSetxxx\Services\<driver>\Parameters + +Arguments: + +RegistryPath - Registry path passed to DriverEntry + +Returns: + +NTSTATUS - SUCCESS if able to configure the framework + +--*/ + +{ + // Initializing the unicode string, so that if it is not allocated it will not be deallocated too. + RtlInitUnicodeString(&g_RegistryPath, NULL); + + g_RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR); + + g_RegistryPath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, g_RegistryPath.MaximumLength, MINADAPTER_POOLTAG); + + if (g_RegistryPath.Buffer == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlAppendUnicodeToString(&g_RegistryPath, RegistryPath->Buffer); + + return STATUS_SUCCESS; +} + +//============================================================================= +#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[] = { + // QueryRoutine Flags Name EntryContext DefaultType DefaultData DefaultLength + { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"DoNotCreateDataFiles", &g_DoNotCreateDataFiles, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &g_DoNotCreateDataFiles, sizeof(ULONG)}, + { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"DisableToneGenerator", &g_DisableToneGenerator, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &g_DisableToneGenerator, sizeof(ULONG)}, + { NULL, 0, NULL, NULL, 0, NULL, 0} + }; + + DPF(D_TERSE, ("[GetRegistrySettings]")); + + PAGED_CODE(); + + RtlInitUnicodeString(¶metersPath, NULL); + + parametersPath.MaximumLength = + RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(WCHAR); + + parametersPath.Buffer = (PWCH) ExAllocatePool2(POOL_FLAG_PAGED, parametersPath.MaximumLength, MINADAPTER_POOLTAG); + if (parametersPath.Buffer == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlAppendUnicodeToString(¶metersPath, RegistryPath->Buffer); + RtlAppendUnicodeToString(¶metersPath, L"\\Parameters"); + + 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)); + DPF(D_VERBOSE, ("DisableToneGenerator: %u", g_DisableToneGenerator)); + + // + // 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]")); + + // Copy registry Path name in a global variable to be used by modules inside driver. + // !! NOTE !! Inside this function we are initializing the registrypath, so we MUST NOT add any failing calls + // before the following call. + ntStatus = CopyRegistrySettingsPath(RegistryPathName); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Registry path copy error 0x%x", ntStatus)), + Done); + + // + // 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()); + } + + ReleaseRegistryStringBuffer(); + } + + 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; + + // 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(InBuffer); + UNREFERENCED_PARAMETER(InBufferSize); + UNREFERENCED_PARAMETER(OutBuffer); + UNREFERENCED_PARAMETER(OutBufferSize); + UNREFERENCED_PARAMETER(BytesReturned); + 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; + PPORTCLSStreamResourceManager2 pPortClsResMgr2 = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_pDeviceObject); + + ntStatus = _pAdapterCommon->InstallEndpointFilters( + _pIrp, + _pAeMiniports, + NULL, + &unknownTopology, + &unknownWave, + NULL, NULL); + + 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). + // + // testing IPortClsStreamResourceManager: + 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; + } + + // testing IPortClsStreamResourceManager2: + ntStatus = unknownWave->QueryInterface(IID_IPortClsStreamResourceManager2, (PVOID *)&pPortClsResMgr2); + 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 = pPortClsResMgr2->AddStreamResource2(pdo, NULL, &res, &hRes); + if (NT_SUCCESS(ntStatusTest)) + { + pPortClsResMgr2->RemoveStreamResource(hRes); + hRes = NULL; + } + + pPortClsResMgr2->Release(); + pPortClsResMgr2 = 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, + 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; +} + +//============================================================================= +#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, + POOL_FLAG_NON_PAGED + ); + 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); + +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; + + // Documented https://msdn.microsoft.com/en-us/library/windows/hardware/ff544039(v=vs.85).aspx + // This method will be called in IRQL PASSIVE_LEVEL +#pragma warning(suppress: 28118) + 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); + + switch (stack->MinorFunction) + { + case IRP_MN_REMOVE_DEVICE: + case IRP_MN_SURPRISE_REMOVAL: + case IRP_MN_STOP_DEVICE: + ext = static_cast<PortClassDeviceContext*>(_DeviceObject->DeviceExtension); + + if (ext->m_pCommon != NULL) + { + ext->m_pCommon->Cleanup(); + + ext->m_pCommon->Release(); + ext->m_pCommon = NULL; + } + break; + + default: + break; + } + + ntStatus = PcDispatchIrp(_DeviceObject, _Irp); + + return ntStatus; +} + +#pragma code_seg() + diff --git a/audio/simpleaudiosample/Source/Main/basetopo.cpp b/audio/simpleaudiosample/Source/Main/basetopo.cpp new file mode 100644 index 00000000..b29a9bb1 --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/basetopo.cpp @@ -0,0 +1,688 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + basetopo.cpp + +Abstract: + + Implementation of topology miniport. This the base class for + all simple audio sample drivers +--*/ + +//4127: conditional expression is constant +#pragma warning (disable : 4127) + +#include "definitions.h" +#include "basetopo.h" + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportTopologySimpleAudioSample::CMiniportTopologySimpleAudioSample +( + _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; +} // CMiniportTopologySimpleAudioSample + +CMiniportTopologySimpleAudioSample::~CMiniportTopologySimpleAudioSample +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + SAFE_RELEASE(m_AdapterCommon); + SAFE_RELEASE(m_PortEvents); +} // ~CMiniportTopologySimpleAudioSample + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySimpleAudioSample::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 +CMiniportTopologySimpleAudioSample::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 +CMiniportTopologySimpleAudioSample::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(("[CMiniportTopologySimpleAudioSample::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 +CMiniportTopologySimpleAudioSample::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 +CMiniportTopologySimpleAudioSample::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 +CMiniportTopologySimpleAudioSample::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 +CMiniportTopologySimpleAudioSample::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 +CMiniportTopologySimpleAudioSample::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(("[CMiniportTopologySimpleAudioSample::GenerateEventList]")); + + ASSERT(m_PortEvents != NULL); + + m_PortEvents->GenerateEventList( + Set, + EventId, + PinEvent, + PinId, + NodeEvent, + NodeId); +} + +#pragma code_seg() diff --git a/audio/simpleaudiosample/Source/Main/common.cpp b/audio/simpleaudiosample/Source/Main/common.cpp new file mode 100644 index 00000000..8843d3a4 --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/common.cpp @@ -0,0 +1,2772 @@ +/*++ + +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 "definitions.h" +#include "hw.h" +#include "savedata.h" +#include "endpoints.h" + +//----------------------------------------------------------------------------- +// CSaveData statics +//----------------------------------------------------------------------------- + +PSAVEWORKER_PARAM CSaveData::m_pWorkItems = NULL; +PDEVICE_OBJECT CSaveData::m_pDeviceObject = NULL; +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// 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; + + PCSimpleAudioSampleHW m_pHW; // Virtual Simple Audio Sample 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_opt_ PWSTR TemplateName, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_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, + _Out_opt_ PUNKNOWN * UnknownMiniportTopology, + _Out_opt_ PUNKNOWN * UnknownMiniportWave + ); + + 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 + ); + + STDMETHODIMP_(VOID) Cleanup(); + + //===================================================================== + // friends + friend NTSTATUS NewAdapterCommon + ( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _In_ POOL_FLAGS PoolFlags + ); + + 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_opt_ PCWSTR TemplateReferenceString, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_DEVPROPERTY *pProperties, + _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName + ); + + NTSTATUS MigrateDeviceInterfaceTemplateParameters + ( + _In_ PUNICODE_STRING SymbolicLinkName, + _In_opt_ PCWSTR TemplateReferenceString + ); +}; + +typedef struct _MINIPAIR_UNKNOWN +{ + LIST_ENTRY ListEntry; + WCHAR Name[MAX_PATH]; + PUNKNOWN PortInterface; + PUNKNOWN MiniportInterface; + PADAPTERPOWERMANAGEMENT PowerInterface; +} MINIPAIR_UNKNOWN; + +#define MAX_DEVICE_REG_KEY_LENGTH 0x100 + +// +// Used to implement the singleton pattern. +// +LONG CAdapterCommon::m_AdapterInstances = 0; + + + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS SimpleAudioSampleIoSetDeviceInterfacePropertyDataMultiple +( + _In_ PUNICODE_STRING SymbolicLinkName, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_DEVPROPERTY *pProperties +) +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + if (pProperties) + { + 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, + _In_ POOL_FLAGS PoolFlags +) +/*++ + +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 (InterlockedCompareExchange(&CAdapterCommon::m_AdapterInstances, 1, 0) != 0) + { + ntStatus = STATUS_DEVICE_BUSY; + DPF(D_ERROR, ("NewAdapterCommon failed, only one instance is allowed")); + goto Done; + } + + // + // Allocate an adapter object. + // + CAdapterCommon *p = new(PoolFlags, 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; + } + + InterlockedDecrement(&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; + + 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 (POOL_FLAG_NON_PAGED, SIMPLEAUDIOSAMPLE_POOLTAG) CSimpleAudioSampleHW; + if (!m_pHW) + { + DPF(D_TERSE, ("Insufficient memory for Simple Audio Sample 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_opt_ PCWSTR TemplateReferenceString, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_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); + + // + // Migrate optional device interface parameters from the template if it exists + // This is done first, so that any additional parameters in pProperties will override the defaults. + // + if (NULL != TemplateReferenceString) + { + ntStatus = MigrateDeviceInterfaceTemplateParameters(AudioSymbolicLinkName, TemplateReferenceString); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("MigrateDeviceInterfaceTempalteParameters: MigrateDeviceInterfaceTemplateParameters(...): failed, 0x%x", ntStatus)), + Done); + } + + // + // Set properties on the interface + // + ntStatus = SimpleAudioSampleIoSetDeviceInterfacePropertyDataMultiple(AudioSymbolicLinkName, cPropertyCount, pProperties); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: SimpleAudioSampleIoSetDeviceInterfacePropertyDataMultiple(...): 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_opt_ PWSTR TemplateName, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_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, TemplateName, 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, + POOL_FLAG_NON_PAGED, + 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(POOL_FLAG_NON_PAGED, 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]")); + 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, + _Out_opt_ PUNKNOWN * UnknownMiniportTopology, + _Out_opt_ PUNKNOWN * UnknownMiniportWave +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::InstallEndpointFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopology = NULL; + PUNKNOWN unknownWave = NULL; + BOOL bTopologyCreated = FALSE; + BOOL bWaveCreated = FALSE; + PUNKNOWN unknownMiniTopo = NULL; + PUNKNOWN unknownMiniWave = NULL; + + // Initialize output optional parameters if needed + if (UnknownTopology) + { + *UnknownTopology = NULL; + } + + if (UnknownWave) + { + *UnknownWave = NULL; + } + + if (UnknownMiniportTopology) + { + *UnknownMiniportTopology = NULL; + } + + if (UnknownMiniportWave) + { + *UnknownMiniportWave = NULL; + } + + ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopology, &unknownMiniTopo); + if (!NT_SUCCESS(ntStatus) || NULL == unknownTopology || NULL == unknownMiniTopo) + { + bTopologyCreated = TRUE; + + // Install Simple Audio Sample topology miniport for the render endpoint. + // + ntStatus = InstallSubdevice(Irp, + MiniportPair->TopoName, // make sure this name matches with SIMPLEAUDIOSAMPLE.<TopoName>.szPname in the inf's [Strings] section + MiniportPair->TemplateTopoName, + CLSID_PortTopology, + CLSID_PortTopology, + MiniportPair->TopoCreateCallback, + MiniportPair->TopoInterfacePropertyCount, + MiniportPair->TopoInterfaceProperties, + DeviceContext, + MiniportPair, + NULL, + IID_IPortTopology, + NULL, + &unknownTopology, + &unknownMiniTopo + ); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = CacheSubdevice(MiniportPair->TopoName, unknownTopology, unknownMiniTopo); + } + } + + ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWave, &unknownMiniWave); + if (!NT_SUCCESS(ntStatus) || NULL == unknownWave || NULL == unknownMiniWave) + { + bWaveCreated = TRUE; + + // Install Simple Audio Sample wave miniport for the render endpoint. + // + ntStatus = InstallSubdevice(Irp, + MiniportPair->WaveName, // make sure this name matches with SIMPLEAUDIOSAMPLE.<WaveName>.szPname in the inf's [Strings] section + MiniportPair->TemplateWaveName, + CLSID_PortWaveRT, + CLSID_PortWaveRT, + MiniportPair->WaveCreateCallback, + MiniportPair->WaveInterfacePropertyCount, + MiniportPair->WaveInterfaceProperties, + DeviceContext, + MiniportPair, + NULL, + IID_IPortWaveRT, + NULL, + &unknownWave, + &unknownMiniWave + ); + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = CacheSubdevice(MiniportPair->WaveName, unknownWave, unknownMiniWave); + } + } + + 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; + } + if (UnknownMiniportTopology != NULL && unknownMiniTopo != NULL) + { + unknownMiniTopo->AddRef(); + *UnknownMiniportTopology = unknownMiniTopo; + } + + if (UnknownMiniportWave != NULL && unknownMiniWave != NULL) + { + unknownMiniWave->AddRef(); + *UnknownMiniportWave = unknownMiniWave; + } + + } + else + { + if (bTopologyCreated && unknownTopology != NULL) + { + UnregisterSubdevice(unknownTopology); + RemoveCachedSubdevice(MiniportPair->TopoName); + } + + if (bWaveCreated && unknownWave != NULL) + { + UnregisterSubdevice(unknownWave); + RemoveCachedSubdevice(MiniportPair->WaveName); + } + } + + SAFE_RELEASE(unknownMiniTopo); + SAFE_RELEASE(unknownTopology); + SAFE_RELEASE(unknownMiniWave); + 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; +} + +#pragma code_seg("PAGE") +NTSTATUS +CopyRegistryValues(HANDLE _hSourceKey, HANDLE _hDestinationKey) +/*++ + +Routine Description: + + This method copies the registry values in _hSourceKey to _hDestinationKey. + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PKEY_VALUE_FULL_INFORMATION kvFullInfo = NULL; + ULONG ulFullInfoLength = 0; + ULONG ulFullInfoResultLength = 0; + PWSTR pwstrKeyValueName = NULL; + UNICODE_STRING strKeyValueName; + PAGED_CODE(); + // Allocate the KEY_VALUE_FULL_INFORMATION structure + ulFullInfoLength = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_DEVICE_REG_KEY_LENGTH; + kvFullInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulFullInfoLength, MINADAPTER_POOLTAG); + IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, Exit); + + // Iterate over each value and copy it to the destination + for (UINT i = 0; NT_SUCCESS(ntStatus); i++) + { + // Enumerate the next value + ntStatus = ZwEnumerateValueKey(_hSourceKey, i, KeyValueFullInformation, kvFullInfo, ulFullInfoLength, &ulFullInfoResultLength); + + // Jump out of this loop if there are no more values + IF_TRUE_ACTION_JUMP(ntStatus == STATUS_NO_MORE_ENTRIES, ntStatus = STATUS_SUCCESS, Exit); + + // Handle incorrect buffer size + if (ntStatus == STATUS_BUFFER_TOO_SMALL || ntStatus == STATUS_BUFFER_OVERFLOW) + { + // Free and re-allocate the KEY_VALUE_FULL_INFORMATION structure with the correct size + ExFreePoolWithTag(kvFullInfo, MINADAPTER_POOLTAG); + + ulFullInfoLength = ulFullInfoResultLength; + + kvFullInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulFullInfoLength, MINADAPTER_POOLTAG); + IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit); + + // Try to enumerate the current value again + ntStatus = ZwEnumerateValueKey(_hSourceKey, i, KeyValueFullInformation, kvFullInfo, ulFullInfoLength, &ulFullInfoResultLength); + + // Jump out of this loop if there are no more values + IF_TRUE_ACTION_JUMP(ntStatus == STATUS_NO_MORE_ENTRIES, ntStatus = STATUS_SUCCESS, Exit); + IF_FAILED_JUMP(ntStatus, loop_exit); + } + else + { + IF_FAILED_JUMP(ntStatus, loop_exit); + } + + // Allocate the key value name string + pwstrKeyValueName = (PWSTR)ExAllocatePool2(POOL_FLAG_NON_PAGED, kvFullInfo->NameLength + sizeof(WCHAR)*2, MINADAPTER_POOLTAG); + IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit); + + // Copy the key value name from the full information struct + RtlStringCbCopyNW(pwstrKeyValueName, kvFullInfo->NameLength + sizeof(WCHAR)*2, kvFullInfo->Name, kvFullInfo->NameLength); + + // Make sure the string is null terminated + pwstrKeyValueName[(kvFullInfo->NameLength) / sizeof(WCHAR)] = 0; + + // Copy the key value name string to a UNICODE string + RtlInitUnicodeString(&strKeyValueName, pwstrKeyValueName); + + // Write the key value from the source into the destination + ntStatus = ZwSetValueKey(_hDestinationKey, &strKeyValueName, 0, kvFullInfo->Type, (PVOID)((PUCHAR)kvFullInfo + kvFullInfo->DataOffset), kvFullInfo->DataLength); + IF_FAILED_JUMP(ntStatus, loop_exit); + + loop_exit: + // Free the key value name string + if (pwstrKeyValueName) + { + ExFreePoolWithTag(pwstrKeyValueName, MINADAPTER_POOLTAG); + } + + // Bail if anything failed + IF_FAILED_JUMP(ntStatus, Exit); + } + +Exit: + // Free the KEY_VALUE_FULL_INFORMATION structure + if (kvFullInfo) + { + ExFreePoolWithTag(kvFullInfo, MINADAPTER_POOLTAG); + } + + return ntStatus; +} + +NTSTATUS +CopyRegistryKey(HANDLE _hSourceKey, HANDLE _hDestinationKey, BOOL _bOverwrite = FALSE) +/*++ + +Routine Description: + + This method recursively copies the registry values in _hSourceKey to _hDestinationKey. + Set _bOverwrite to indicate whether the first level values are copied or not. + Normal use is to set false for the initial call, and then all sub paths will be copied. + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + PKEY_BASIC_INFORMATION kBasicInfo = NULL; + ULONG ulBasicInfoLength = 0; + ULONG ulBasicInfoResultLength = 0; + ULONG ulDisposition = 0; + PWSTR pwstrKeyName = NULL; + UNICODE_STRING strKeyName; + OBJECT_ATTRIBUTES hCurrentSourceKeyAttributes; + OBJECT_ATTRIBUTES hNewDestinationKeyAttributes; + HANDLE hCurrentSourceKey = NULL; + HANDLE hNewDestinationKey = NULL; + PAGED_CODE(); + // Validate parameters + IF_TRUE_ACTION_JUMP(_hSourceKey == nullptr, ntStatus = STATUS_INVALID_PARAMETER, Exit); + IF_TRUE_ACTION_JUMP(_hDestinationKey == nullptr, ntStatus = STATUS_INVALID_PARAMETER, Exit); + + // Allocate the KEY_BASIC_INFORMATION structure + ulBasicInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_DEVICE_REG_KEY_LENGTH; + kBasicInfo = (PKEY_BASIC_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulBasicInfoLength, MINADAPTER_POOLTAG); + IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, Exit); + + ntStatus = STATUS_SUCCESS; + // Iterate over each key and copy it + for (UINT i = 0; NT_SUCCESS(ntStatus); i++) + { + // Enumerate the next key + ntStatus = ZwEnumerateKey(_hSourceKey, i, KeyBasicInformation, kBasicInfo, ulBasicInfoLength, &ulBasicInfoResultLength); + + // Jump out of this loop if there are no more keys + IF_TRUE_ACTION_JUMP(ntStatus == STATUS_NO_MORE_ENTRIES, ntStatus = STATUS_SUCCESS, copy_values); + + // Handle incorrect buffer size + if (ntStatus == STATUS_BUFFER_TOO_SMALL || ntStatus == STATUS_BUFFER_OVERFLOW) + { + // Free and re-allocate the KEY_BASIC_INFORMATION structure with the correct size. + ExFreePoolWithTag(kBasicInfo, MINADAPTER_POOLTAG); + ulBasicInfoLength = ulBasicInfoResultLength; + kBasicInfo = (PKEY_BASIC_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulBasicInfoLength, MINADAPTER_POOLTAG); + IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit); + + // Try to enumerate the current key again. + ntStatus = ZwEnumerateKey(_hSourceKey, i, KeyBasicInformation, kBasicInfo, ulBasicInfoLength, &ulBasicInfoResultLength); + + // Jump out of this loop if there are no more keys + IF_TRUE_ACTION_JUMP(ntStatus == STATUS_NO_MORE_ENTRIES, ntStatus = STATUS_SUCCESS, copy_values); + IF_FAILED_JUMP(ntStatus, loop_exit); + } + else + { + IF_FAILED_JUMP(ntStatus, loop_exit); + } + + // Allocate the key name string + pwstrKeyName = (PWSTR)ExAllocatePool2(POOL_FLAG_NON_PAGED, kBasicInfo->NameLength + sizeof(WCHAR), MINADAPTER_POOLTAG); + IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit); + + // Copy the key name from the basic information struct + RtlStringCbCopyNW(pwstrKeyName, kBasicInfo->NameLength + sizeof(WCHAR), kBasicInfo->Name, kBasicInfo->NameLength); + + // Make sure the string is null terminated + pwstrKeyName[(kBasicInfo->NameLength) / sizeof(WCHAR)] = 0; + + // Copy the key name string to a UNICODE string + RtlInitUnicodeString(&strKeyName, pwstrKeyName); + + // Initialize attributes to open the currently enumerated source key + InitializeObjectAttributes(&hCurrentSourceKeyAttributes, &strKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, _hSourceKey, NULL); + + // Open the currently enumerated source key + ntStatus = ZwOpenKey(&hCurrentSourceKey, KEY_READ, &hCurrentSourceKeyAttributes); + IF_FAILED_ACTION_JUMP(ntStatus, ZwClose(hCurrentSourceKey), loop_exit); + + // Initialize attributes to create the new destination key + InitializeObjectAttributes(&hNewDestinationKeyAttributes, &strKeyName, OBJ_KERNEL_HANDLE, _hDestinationKey, NULL); + + // Create the key at the destination + ntStatus = ZwCreateKey(&hNewDestinationKey, KEY_WRITE, &hNewDestinationKeyAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &ulDisposition); + IF_FAILED_ACTION_JUMP(ntStatus, ZwClose(hCurrentSourceKey), loop_exit); + + // Now copy the contents of the currently enumerated key to the destination + ntStatus = CopyRegistryKey(hCurrentSourceKey, hNewDestinationKey, TRUE); + IF_FAILED_JUMP(ntStatus, loop_exit); + + loop_exit: + // Free the key name string + if (pwstrKeyName) + { + ExFreePoolWithTag(pwstrKeyName, MINADAPTER_POOLTAG); + } + + // Close the current source key + if (hCurrentSourceKey) + { + ZwClose(hCurrentSourceKey); + } + + // Close the new destination key + if (hNewDestinationKey) + { + ZwClose(hNewDestinationKey); + } + + // Bail if anything failed + IF_FAILED_JUMP(ntStatus, Exit); + } + +copy_values: + // Copy the values + if (_bOverwrite) + { + ntStatus = CopyRegistryValues(_hSourceKey, _hDestinationKey); + IF_FAILED_JUMP(ntStatus, Exit); + } + +Exit: + // Free the basic information structure + if (kBasicInfo) + { + ExFreePoolWithTag(kBasicInfo, MINADAPTER_POOLTAG); + } + return ntStatus; +} + + +NTSTATUS CAdapterCommon::MigrateDeviceInterfaceTemplateParameters +( + _In_ PUNICODE_STRING SymbolicLinkName, + _In_opt_ PCWSTR TemplateReferenceString +) +/*++ + +Routine Description: + + This method copies all of the properties from the template interface, + which is specified in the inf, to the actual interface being used which + may be dynamically generated at run time. This allows for a driver + to reuse a single inf entry for multiple audio endpoints. The primary + purpose for this is to allow for sideband audio endpoints to dynamically + generate the reference string at run time, tied to the peripheral connected, + while still having a simple static inf entry for setting up apo's or other + parameters. + + For example, if you have an interface in your inf defined with reference string + "SpeakerWave". At runtime you could generate "SpeakerWave-1234ABCDE", and specify + "SpeakerWave" as the template name. When "SpeakerWave-1234ABCDE" is installed + we will copy all of the parameters that were specified in the inf for "SpeakerWave" + over to "SpeakerWave-1234ABCDE". You simply need to specify "SpeakerWave" as the + "TemplateName" in the ENDPOINT_MINIPAIRS. + + By default, the first level of registry keys are not copied. Only the 2nd level and + deeper are copied. This way the friendly name and other PNP properties will not + be modified, but the EP and FX properties will be copied. + +Return Value: + + NT status code. + +--*/ + +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + HANDLE hDeviceInterfaceParametersKey(NULL); + HANDLE hTemplateDeviceInterfaceParametersKey(NULL); + UNICODE_STRING TemplateSymbolicLinkName; + UNICODE_STRING referenceString; + + RtlInitUnicodeString(&TemplateSymbolicLinkName, NULL); + RtlInitUnicodeString(&referenceString, TemplateReferenceString); + + // + // Register an audio interface if not already present for the template interface, so we can access + // the registry path. If it's already registered, this simply returns the symbolic link name. + // No need to unregister it (there is no mechanism to), and we'll never make it active. + // + ntStatus = IoRegisterDeviceInterface( + GetPhysicalDeviceObject(), + &KSCATEGORY_AUDIO, + &referenceString, + &TemplateSymbolicLinkName); + + // Open the template device interface's registry key path + ntStatus = IoOpenDeviceInterfaceRegistryKey(&TemplateSymbolicLinkName, GENERIC_READ, &hTemplateDeviceInterfaceParametersKey); + IF_FAILED_JUMP(ntStatus, Exit); + + // Open the new device interface's registry key path that we plan to activate + ntStatus = IoOpenDeviceInterfaceRegistryKey(SymbolicLinkName, GENERIC_WRITE, &hDeviceInterfaceParametersKey); + IF_FAILED_JUMP(ntStatus, Exit); + + // Copy the template device parameters key to the device interface key + ntStatus = CopyRegistryKey(hTemplateDeviceInterfaceParametersKey, hDeviceInterfaceParametersKey); + IF_FAILED_JUMP(ntStatus, Exit); + +Exit: + RtlFreeUnicodeString(&TemplateSymbolicLinkName); + + if (hTemplateDeviceInterfaceParametersKey) + { + ZwClose(hTemplateDeviceInterfaceParametersKey); + } + + if (hDeviceInterfaceParametersKey) + { + ZwClose(hDeviceInterfaceParametersKey); + } + + return ntStatus; +} diff --git a/audio/simpleaudiosample/Source/Main/mintopo.cpp b/audio/simpleaudiosample/Source/Main/mintopo.cpp new file mode 100644 index 00000000..6ab1624f --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/mintopo.cpp @@ -0,0 +1,557 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + mintopo.cpp + +Abstract: + + Implementation of topology miniport. +--*/ + +#pragma warning (disable : 4127) + +#include "definitions.h" +#include "endpoints.h" +#include "minwavert.h" +#include "mintopo.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateMiniportTopologySimpleAudioSample +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _In_ POOL_FLAGS PoolFlags, + _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 (PoolFlags, 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; +} // CreateMiniportTopologySimpleAudioSample + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportTopology::~CMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportTopology::~CMiniportTopology]")); +} // ~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 + CMiniportTopologySimpleAudioSample::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 CMiniportTopologySimpleAudioSample::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 = + CMiniportTopologySimpleAudioSample::Init + ( + UnknownAdapter, + Port_ + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: CMiniportTopologySimpleAudioSample::Init failed, 0x%x", ntStatus)), + Done); + +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; +} + +//============================================================================= +#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 diff --git a/audio/simpleaudiosample/Source/Main/minwavert.cpp b/audio/simpleaudiosample/Source/Main/minwavert.cpp new file mode 100644 index 00000000..1d92073d --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/minwavert.cpp @@ -0,0 +1,1683 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minwavert.cpp + +Abstract: + + Implementation of wavert miniport. +--*/ + +#pragma warning (disable : 4127) + +#include "definitions.h" +#include <limits.h> +#include "endpoints.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "micarraywavtable.h" + +#define EFFECTS_LIST_COUNT 2 + +//============================================================================= +// CMiniportWaveRT +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateMiniportWaveRTSimpleAudioSample +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _In_ POOL_FLAGS PoolFlags, + _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 (PoolFlags, 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_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; + } + +} // ~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 sets the ResultantFormat to be the only supported + format for the MicArray endpoint. + +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. + +--*/ +{ + UNREFERENCED_PARAMETER(PinId); + + ULONG requiredSize; + + PAGED_CODE(); + + if (!IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) + { + return STATUS_NOT_IMPLEMENTED; + } + + //If called for the mic array pin, set ResultantFormat to be the endpoint's only supported format. + //Otherwise, allow the class handler to set ResultantFormat. + if ((this->m_DeviceType) == eMicArrayDevice1) + { + requiredSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + + // + // 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; + } + + //Set ResultantFormat to be the only supported format for the MicArray endpoint. + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE resultantFormat; + resultantFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ResultantFormat; + *resultantFormat = *MicArrayPinSupportedDeviceFormats; + *ResultantFormatLength = requiredSize; + + return STATUS_SUCCESS; + } + else + { + 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_ulSystemAllocated = 0; + m_SystemStreams = NULL; + m_bGfxEnabled = FALSE; + m_pMixFormat = NULL; + m_pDeviceFormat = NULL; + m_ulMixDrmContentId = 0; + m_pbMuted = NULL; + m_plVolumeLevel = NULL; + RtlZeroMemory(&m_MixDrmRights, sizeof(m_MixDrmRights)); + + // + // Init the audio-engine used by the render devices. + // + if (IsRenderDevice()) + { + if (m_ulMaxSystemStreams == 0 ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // System streams. + size = sizeof(PCMiniportWaveRTStream) * m_ulMaxSystemStreams; + m_SystemStreams = (PCMiniportWaveRTStream *)ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG); + if (m_SystemStreams == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // 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); + } + + // 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 (POOL_FLAG_NON_PAGED, 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)); + } + 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 +) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::ValidateStreamCreate]")); + + NTSTATUS ntStatus = STATUS_NOT_SUPPORTED; + + if (_Capture) + { + + if (IsSystemCapturePin(_Pin)) + { + VERIFY_PIN_INSTANCE_RESOURCES_AVAILABLE(ntStatus, m_ulSystemAllocated, m_ulMaxSystemStreams); + } + + } + else + { + + if (IsSystemRenderPin(_Pin)) + { + VERIFY_PIN_INSTANCE_RESOURCES_AVAILABLE(ntStatus, m_ulSystemAllocated, m_ulMaxSystemStreams); + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +_Use_decl_annotations_ +VOID CMiniportWaveRT::AcquireFormatsAndModesLock() +{ + KeAcquireSpinLock(&m_DeviceFormatsAndModesLock, &m_DeviceFormatsAndModesIrql); +} + +#pragma code_seg() +_Use_decl_annotations_ +VOID CMiniportWaveRT::ReleaseFormatsAndModesLock() +{ + KeReleaseSpinLock(&m_DeviceFormatsAndModesLock, m_DeviceFormatsAndModesIrql); +} + +//--------------------------------------------------------------------------- +// 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. +// +#pragma code_seg() +_Use_decl_annotations_ +ULONG CMiniportWaveRT::GetPinSupportedDeviceFormats(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) KSDATAFORMAT_WAVEFORMATEXTENSIBLE **ppFormats) +{ + PPIN_DEVICE_FORMATS_AND_MODES pDeviceFormatsAndModes = NULL; + + AcquireFormatsAndModesLock(); + + pDeviceFormatsAndModes = m_DeviceFormatsAndModes; + ASSERT(m_DeviceFormatsAndModesCount > PinId); + ASSERT(pDeviceFormatsAndModes[PinId].WaveFormats != NULL); + ASSERT(pDeviceFormatsAndModes[PinId].WaveFormatsCount > 0); + + if (ppFormats != NULL) + { + *ppFormats = pDeviceFormatsAndModes[PinId].WaveFormats; + } + + ReleaseFormatsAndModesLock(); + + return pDeviceFormatsAndModes[PinId].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. +// +#pragma code_seg() +_Use_decl_annotations_ +ULONG CMiniportWaveRT::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; + + AcquireFormatsAndModesLock(); + + 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; + + 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; + } + } + + ReleaseFormatsAndModesLock(); + return numModes; +} + +#pragma code_seg() +BOOL CMiniportWaveRT::IsSystemCapturePin(ULONG nPinId) +{ + AcquireFormatsAndModesLock(); + + PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType; + + ReleaseFormatsAndModesLock(); + return (pinType == SystemCapturePin); +} + +#pragma code_seg() +BOOL CMiniportWaveRT::IsSystemRenderPin(ULONG nPinId) +{ + AcquireFormatsAndModesLock(); + + PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType; + + ReleaseFormatsAndModesLock(); + return (pinType == SystemRenderPin); +} + +#pragma code_seg() +BOOL CMiniportWaveRT::IsBridgePin(ULONG nPinId) +{ + AcquireFormatsAndModesLock(); + + PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType; + + ReleaseFormatsAndModesLock(); + return (pinType == BridgePin); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::StreamCreated +( + _In_ ULONG _Pin, + _In_ PCMiniportWaveRTStream _Stream +) +{ + PAGED_CODE(); + + PCMiniportWaveRTStream * streams = NULL; + ULONG count = 0; + + DPF_ENTER(("[CMiniportWaveRT::StreamCreated]")); + + if (IsSystemCapturePin(_Pin)) + { + ALLOCATE_PIN_INSTANCE_RESOURCES(m_ulSystemAllocated); + return STATUS_SUCCESS; + } + else if (IsSystemRenderPin(_Pin)) + { + + ALLOCATE_PIN_INSTANCE_RESOURCES(m_ulSystemAllocated); + streams = m_SystemStreams; + count = m_ulMaxSystemStreams; + + } + + // + // 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_PIN_INSTANCE_RESOURCES(m_ulSystemAllocated); + return STATUS_SUCCESS; + } + else if (IsSystemRenderPin(_Pin)) + { + + FREE_PIN_INSTANCE_RESOURCES(m_ulSystemAllocated); + streams = m_SystemStreams; + count = m_ulMaxSystemStreams; + 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) || + IsSystemCapturePin(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; + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + pKsFormat = (PKSDATAFORMAT)PropertyRequest->Value; + ntStatus = IsFormatSupported(kspPin->PinId, IsSystemCapturePin(kspPin->PinId) || FALSE, pKsFormat); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + + return ntStatus; +} // PropertyHandlerProposedFormat + +//============================================================================= +#pragma code_seg() +NTSTATUS +CMiniportWaveRT_EventHandler_PinCapsChange +( +_In_ PPCEVENT_REQUEST EventRequest +) +{ + CMiniportWaveRT* miniport = reinterpret_cast<CMiniportWaveRT*>(EventRequest->MajorTarget); + return miniport->EventHandler_PinCapsChange(EventRequest); +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +CMiniportWaveRT::EventHandler_PinCapsChange +( +_In_ PPCEVENT_REQUEST EventRequest +) +{ + if (*EventRequest->EventItem->Set != KSEVENTSETID_PinCapsChange) + { + return STATUS_INVALID_PARAMETER; + } + + switch (EventRequest->Verb) + { + // Do we support event handling?!? + case PCEVENT_VERB_SUPPORT: + break; + // We should add the event now! + case PCEVENT_VERB_ADD: + // If we have the interface and EventEntry is defined ... + if (EventRequest->EventEntry) + { + switch (EventRequest->EventItem->Id) + { + // Add pincaps format change event to support the force sample rate feature + case KSEVENT_PINCAPS_FORMATCHANGE: + m_pPortEvents->AddEventToEventList(EventRequest->EventEntry); + break; + default: + return STATUS_INVALID_PARAMETER; + break; + } + } + 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 ... + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#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::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 (POOL_FLAG_NON_PAGED, 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++; + } + } + + // + // 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. + // + } + + // + // 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") +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_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]")); + } + } + + pWaveHelper->Release(); + + return ntStatus; +} // PropertyHandler_WaveFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_GenericPin +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CMiniportWaveRT* pWave = NULL; + CMiniportWaveRTStream * pStream = NULL; + + PAGED_CODE(); + + if (PropertyRequest->MajorTarget == NULL || + PropertyRequest->MinorTarget == NULL) + { + ntStatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + // + // Get a ref to the miniport. + // + pWave = MajorTarget_to_Obj(PropertyRequest->MajorTarget); + pWave->AddRef(); + + // + // Get a ref to the stream. + // + pStream = MinorTarget_to_Obj(PropertyRequest->MinorTarget); + pStream->AddRef(); + +exit: + + SAFE_RELEASE(pStream); + SAFE_RELEASE(pWave); + + return ntStatus; +} + +#pragma code_seg() + diff --git a/audio/simpleaudiosample/Source/Main/minwavert.h b/audio/simpleaudiosample/Source/Main/minwavert.h new file mode 100644 index 00000000..4c0ccd83 --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/minwavert.h @@ -0,0 +1,302 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minwavert.h + +Abstract: + + Definition of wavert miniport class. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_MINWAVERT_H_ +#define _SIMPLEAUDIOSAMPLE_MINWAVERT_H_ + +//============================================================================= +// Referenced Forward +//============================================================================= +class CMiniportWaveRTStream; +typedef CMiniportWaveRTStream *PCMiniportWaveRTStream; + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CMiniportWaveRT +// +class CMiniportWaveRT : + public IMiniportWaveRT, + public IMiniportAudioSignalProcessing, + public CUnknown +{ +private: + ULONG m_ulSystemAllocated; + + ULONG m_ulMaxSystemStreams; + ULONG m_ulMaxOffloadStreams; + ULONG m_ulMaxLoopbackStreams; + + // weak ref of running streams. + PCMiniportWaveRTStream * m_SystemStreams; + + BOOL m_bGfxEnabled; + PBOOL m_pbMuted; + PLONG m_plVolumeLevel; + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE m_pMixFormat; + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE m_pDeviceFormat; + PCFILTER_DESCRIPTOR m_FilterDesc; + PIN_DEVICE_FORMATS_AND_MODES * m_DeviceFormatsAndModes; + KSPIN_LOCK m_DeviceFormatsAndModesLock; // To serialize access. + KIRQL m_DeviceFormatsAndModesIrql; + ULONG m_DeviceFormatsAndModesCount; + USHORT m_DeviceMaxChannels; + PDRMPORT m_pDrmPort; + DRMRIGHTS m_MixDrmRights; + ULONG m_ulMixDrmContentId; + + union { + PVOID m_DeviceContext; + }; + +protected: + PADAPTERCOMMON m_pAdapterCommon; + ULONG m_DeviceFlags; + eDeviceType m_DeviceType; + PPORTEVENTS m_pPortEvents; + PENDPOINT_MINIPAIR m_pMiniportPair; + +public: + NTSTATUS EventHandler_PinCapsChange + ( + _In_ PPCEVENT_REQUEST EventRequest + ); + + NTSTATUS ValidateStreamCreate + ( + _In_ ULONG _Pin, + _In_ BOOLEAN _Capture + ); + + 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 + ); + +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_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 (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; + } + else if(m_FilterDesc.PinCount > KSPIN_WAVE_RENDER3_SOURCE) + { + m_ulMaxSystemStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER3_SINK_SYSTEM].MaxFilterInstanceCount; + } + } + else + { + // + // capture bridge pin comes first in the enumeration + // + if (m_FilterDesc.PinCount > KSPIN_WAVEIN_HOST) + { + m_ulMaxSystemStreams = m_FilterDesc.Pins[KSPIN_WAVEIN_HOST].MaxFilterInstanceCount; + } + } + } + + KeInitializeSpinLock(&m_DeviceFormatsAndModesLock); + m_DeviceFormatsAndModesIrql = PASSIVE_LEVEL; + } + +#pragma code_seg() + + ~CMiniportWaveRT(); + + IMP_IMiniportWaveRT; + IMP_IMiniportAudioSignalProcessing; + + // Friends + friend class CMiniportWaveRTStream; + friend class CMiniportTopologySimpleAudioSample; + + friend NTSTATUS PropertyHandler_WaveFilter + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +public: +public: + NTSTATUS PropertyHandlerProposedFormat + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerProposedFormat2 + + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + PADAPTERCOMMON GetAdapterCommObj() + { + return m_pAdapterCommon; + }; +#pragma code_seg() + + //--------------------------------------------------------------------------------------------------------- + // 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 + ); + + //----------------------------------------------------------------------------- + // 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: + _IRQL_raises_(DISPATCH_LEVEL) + _Acquires_lock_(m_DeviceFormatsAndModesLock) + _Requires_lock_not_held_(m_DeviceFormatsAndModesLock) + _IRQL_saves_global_(SpinLock, m_DeviceFormatsAndModesIrql) + VOID AcquireFormatsAndModesLock(); + + _Releases_lock_(m_DeviceFormatsAndModesLock) + _Requires_lock_held_(m_DeviceFormatsAndModesLock) + _IRQL_restores_global_(SpinLock, m_DeviceFormatsAndModesIrql) + VOID ReleaseFormatsAndModesLock(); + + _Post_satisfies_(return > 0) + ULONG GetPinSupportedDeviceFormats(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) KSDATAFORMAT_WAVEFORMATEXTENSIBLE **ppFormats); + + _Success_(return != 0) + ULONG GetPinSupportedDeviceModes(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) _On_failure_(_Deref_post_null_) MODE_AND_DEFAULT_FORMAT **ppModes); + +#pragma code_seg() + +protected: +#pragma code_seg() + BOOL IsRenderDevice() + { + return m_DeviceType == eSpeakerDevice ? TRUE : FALSE; + } + + BOOL IsSystemRenderPin(ULONG nPinId); + + BOOL IsSystemCapturePin(ULONG nPinId); + + BOOL IsBridgePin(ULONG nPinId); + + // These three pins are the pins used by the audio engine for host, loopback, and offload. + ULONG GetSystemPinId() + { + ASSERT(IsRenderDevice()); + return KSPIN_WAVE_RENDER2_SINK_SYSTEM; + } +}; + +typedef CMiniportWaveRT *PCMiniportWaveRT; + +#endif // _SIMPLEAUDIOSAMPLE_MINWAVERT_H_ diff --git a/audio/simpleaudiosample/Source/Main/minwavertstream.cpp b/audio/simpleaudiosample/Source/Main/minwavertstream.cpp new file mode 100644 index 00000000..27289a0e --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/minwavertstream.cpp @@ -0,0 +1,1644 @@ +#include "definitions.h" +#include <limits.h> +#include <ks.h> +#include "endpoints.h" +#include "minwavert.h" +#include "minwavertstream.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) + { + ExDeleteTimer + ( + m_pNotificationTimer, + TRUE, // Cancel the timer if it is currently set. + TRUE, // Wait for the timer to finish expiring and for any callback to a ExTimerCallback routine to finish. + NULL + ); + } + + // Since we just cancelled the notification timer, wait for all queued + // DPCs to complete before we free the notification DPC. + // + KeFlushQueuedDpcs(); + + DPF_ENTER(("[CMiniportWaveRTStream::~CMiniportWaveRTStream]")); +} // ~CMiniportWaveRTStream + +//============================================================================= +#pragma code_seg("PAGE") + +NTSTATUS CMiniportWaveRTStream::ReadRegistrySettings() +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + UNICODE_STRING parametersPath; + + RTL_QUERY_REGISTRY_TABLE paramTable[] = { + // QueryRoutine Flags Name EntryContext DefaultType DefaultData DefaultLength + { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureToneFrequency", &m_ulHostCaptureToneFrequency, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_ulHostCaptureToneFrequency, sizeof(DWORD) }, + { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureToneAmplitude", &m_dwHostCaptureToneAmplitude, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_dwHostCaptureToneAmplitude, sizeof(DWORD) }, + { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureToneDCOffset", &m_dwHostCaptureToneDCOffset, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_dwHostCaptureToneDCOffset, sizeof(DWORD) }, + { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureToneInitialPhase", &m_dwHostCaptureToneInitialPhase, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_dwHostCaptureToneInitialPhase, sizeof(DWORD) }, + { NULL, 0, NULL, NULL, 0, NULL, 0 } + }; + + RtlInitUnicodeString(¶metersPath, NULL); + + // The sizeof(WCHAR) is added to the maximum length, for allowing a space for null termination of the string. + parametersPath.MaximumLength = + g_RegistryPath.Length + sizeof(L"\\Parameters") + sizeof(WCHAR); + + parametersPath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, parametersPath.MaximumLength, MINWAVERT_POOLTAG); + if (parametersPath.Buffer == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlAppendUnicodeToString(¶metersPath, g_RegistryPath.Buffer); + RtlAppendUnicodeToString(¶metersPath, L"\\Parameters"); + + ntStatus = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + parametersPath.Buffer, + ¶mTable[0], + NULL, + NULL + ); + + ExFreePool(parametersPath.Buffer); + + return ntStatus; +} + +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_ - + + 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_ullLastDPCTimeStamp = 0; + m_hnsDPCTimeCarryForward = 0; + m_ulDmaMovementRate = 0; + m_byteDisplacementCarryForward = 0; + m_bLfxEnabled = FALSE; + m_pbMuted = NULL; + m_plVolumeLevel = NULL; + m_plPeakMeter = NULL; + m_pWfExt = NULL; + m_ullLinearPosition = 0; + m_ullPresentationPosition = 0; + m_ulContentId = 0; + m_ulCurrentWritePosition = 0; + m_ulLastOsReadPacket = ULONG_MAX; + m_ulLastOsWritePacket = ULONG_MAX; + m_IsCurrentWritePositionUpdated = 0; + m_SignalProcessingMode = SignalProcessingMode; + m_bEoSReceived = FALSE; + m_bLastBufferRendered = FALSE; + + m_ulHostCaptureToneFrequency = IsEqualGUID(SignalProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) ? 1000 : 2000; + m_dwHostCaptureToneAmplitude = 50; + m_dwHostCaptureToneDCOffset = 0; + m_dwHostCaptureToneInitialPhase = 0; + + m_pPortStream = PortStream_; + InitializeListHead(&m_NotificationList); + m_ulNotificationIntervalMs = 0; + + // Initialize the spinlock to synchronize position updates + KeInitializeSpinLock(&m_PositionSpinLock); + + m_pNotificationTimer = ExAllocateTimer( + TimerNotifyRT, + this, + EX_TIMER_HIGH_RESOLUTION + ); + if (!m_pNotificationTimer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + 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)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KDPC), MINWAVERTSTREAM_POOLTAG); + if (!m_pDpc) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + m_pWfExt = (PWAVEFORMATEXTENSIBLE)ExAllocatePool2(POOL_FLAG_NON_PAGED, 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)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(BOOL), MINWAVERTSTREAM_POOLTAG); + if (m_pbMuted == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + m_plVolumeLevel = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); + if (m_plVolumeLevel == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + m_plPeakMeter = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); + if (m_plPeakMeter == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (m_bCapture) + { + ReadRegistrySettings(); + DWORD toneFrequency = 0; + DWORD toneAmplitude = 0; + DWORD toneDCOffset = 0; + DWORD toneInitialPhase = 0; + + double toneAmplitudeDouble = 0; + double toneDCOffsetDouble = 0; + double toneInitialPhaseDouble = 0; + + toneFrequency = m_ulHostCaptureToneFrequency; + toneAmplitude = m_dwHostCaptureToneAmplitude; + toneDCOffset = m_dwHostCaptureToneDCOffset; + toneInitialPhase = m_dwHostCaptureToneInitialPhase; + + if (labs(toneAmplitude) > 100) + { + toneAmplitude = toneAmplitude > 0 ? 100 : -100; + } + + if (labs(toneDCOffset) > 100) + { + toneDCOffset = toneDCOffset > 0 ? 100 : -100; + } + + DWORD abssum = labs(toneAmplitude) + labs(toneDCOffset); + + if (abssum > 100) + { + toneAmplitudeDouble = ((double)toneAmplitude) / abssum; + toneDCOffsetDouble = ((double)toneDCOffset) / abssum; + } + else + { + toneAmplitudeDouble = ((double)toneAmplitude) / 100.0; + toneDCOffsetDouble = ((double)toneDCOffset) / 100.0; + } + + if (labs(toneInitialPhase) > 31416) + { + toneInitialPhase = toneInitialPhase > 0 ? 31416 : -31416; + } + + toneInitialPhaseDouble = (double)toneInitialPhase / 10000; + + ntStatus = m_ToneGenerator.Init(toneFrequency, toneAmplitudeDouble, toneDCOffsetDouble, toneInitialPhaseDouble, 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(); + } + + 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 interface is supported only on host render streams + *Object = PVOID(PMINIPORTWAVERTOUTPUTSTREAM(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; + + // Simple Audio Sample uses following buffer to hold data before writing to a file. + // Allocating larger buffer will reduce File I/O operations. + ntStatus = m_SaveData.SetMaxWriteSize(RequestedSize_ * 4); + 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*)ExAllocatePool2( + POOL_FLAG_NON_PAGED, + sizeof(NotificationListEntry), + MINWAVERTSTREAM_POOLTAG); + if (NULL == nleNew) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + nleNew->NotificationEvent = NotificationEvent_; + + // Fail if the notification event already exists in our list. + 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_) + { + 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; + + KIRQL oldIrql; + KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql); + + 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; + + KeReleaseSpinLock(&m_PositionSpinLock, oldIrql); + + ntStatus = STATUS_SUCCESS; + + return ntStatus; +} + +//============================================================================= +// CMiniportWaveRTStream::GetReadPacket +// +// Returns information about the next packet for the OS to read. +// +// Return value +// +// Returns STATUS_DEVICE_NOT_READY if no new packets are available. +// +// 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 +) +{ + 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; + } + + KIRQL oldIrql; + KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql); + + LONGLONG packetCounter = m_llPacketCounter; + ULONGLONG ullLinearPosition = m_ullLinearPosition; + ULONGLONG hnsElapsedTimeCarryForward = m_hnsElapsedTimeCarryForward; + ULONGLONG ullDmaTimeStamp = m_ullDmaTimeStamp; + + KeReleaseSpinLock(&m_PositionSpinLock, oldIrql); + + // 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(packetCounter - 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 the end 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 correlation + // [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 = packetCounter * (m_ulDmaBufferSize / m_ulNotificationsPerBuffer); + // Need to divide by (1000 * 10000 because m_ulDmaMovementRate is average bytes per sec + ULONGLONG carryForwardBytes = (hnsElapsedTimeCarryForward * m_ulDmaMovementRate) / 10000000; + ULONGLONG deltaLinearPosition = ullLinearPosition + carryForwardBytes - linearPositionOfAvailablePacket; + ULONGLONG deltaTimeInHns = deltaLinearPosition * 10000000 / m_ulDmaMovementRate; + ULONGLONG timeOfAvailablePacketInHns = 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; + + return STATUS_SUCCESS; +} + +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::SetWritePacket +( + _In_ ULONG PacketNumber, + _In_ DWORD Flags, + _In_ ULONG EosPacketLength +) +{ + UNREFERENCED_PARAMETER(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_bEoSReceived) + { + return STATUS_INVALID_DEVICE_STATE; + } + + KIRQL oldIrql; + KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql); + // 1-based count of completed packets, 0-based packet number of current packet + LONGLONG currentPacket = m_llPacketCounter; + KeReleaseSpinLock(&m_PositionSpinLock, oldIrql); + + // 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) + { + return STATUS_INVALID_PARAMETER; + } + 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 + KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql); + ntStatus = SetCurrentWritePositionInternal(ulCurrentWritePosition); + KeReleaseSpinLock(&m_PositionSpinLock, oldIrql); + } + + if (!NT_SUCCESS(ntStatus)) + { + 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; + } + + KIRQL oldIrql; + KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql); + + if (m_KsState == KSSTATE_RUN) + { + // Get the current time and update simulated position. + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + UpdatePosition(ilQPC); + } + + *pPacketCount = LODWORD(m_llPacketCounter); + KeReleaseSpinLock(&m_PositionSpinLock, oldIrql); + + return STATUS_SUCCESS; +} + +//linear and presentation positions +#pragma code_seg() +NTSTATUS CMiniportWaveRTStream::GetPositions( + _Out_opt_ ULONGLONG* _pullLinearBufferPosition, + _Out_opt_ ULONGLONG* _pullPresentationPosition, + _Out_opt_ LARGE_INTEGER* _pliQPCTime +) +{ + DPF_ENTER(("[CMiniportWaveRTStream::GetPositions]")); + + NTSTATUS ntStatus; + LARGE_INTEGER ilQPC; + KIRQL oldIrql; + + // 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. + // + KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql); + ilQPC = KeQueryPerformanceCounter(NULL); + if (m_KsState == KSSTATE_RUN) + { + UpdatePosition(ilQPC); + } + if (_pullLinearBufferPosition) + { + *_pullLinearBufferPosition = m_ullLinearPosition; + } + if (_pullPresentationPosition) + { + *_pullPresentationPosition = m_ullPresentationPosition; + } + KeReleaseSpinLock(&m_PositionSpinLock, oldIrql); + if (_pliQPCTime) + { + *_pliQPCTime = ilQPC; + } + + ntStatus = STATUS_SUCCESS; + + return ntStatus; +} + +NTSTATUS CMiniportWaveRTStream::GetPresentationPosition(_Out_ KSAUDIO_PRESENTATION_POSITION* _pPresentationPosition) +{ + ASSERT(_pPresentationPosition); + LARGE_INTEGER timeStamp; + + DPF_ENTER(("[CMiniportWaveRTStream::GetPresentationPosition]")); + + ULONGLONG ullLinearPosition = { 0 }; + ULONGLONG ullPresentationPosition = { 0 }; + NTSTATUS status = STATUS_SUCCESS; + + status = GetPositions(&ullLinearPosition, &ullPresentationPosition, &timeStamp); + if (!NT_SUCCESS(status)) + { + return status; + } + + _pPresentationPosition->u64PositionInBlocks = ullPresentationPosition * m_pWfExt->Format.nSamplesPerSec / m_pWfExt->Format.nAvgBytesPerSec; + _pPresentationPosition->u64QPCPosition = (UINT64)timeStamp.QuadPart; + + return STATUS_SUCCESS; +} + +#pragma code_seg() +NTSTATUS CMiniportWaveRTStream::SetCurrentWritePositionInternal(_In_ ULONG _ulCurrentWritePosition) +{ + DPF_ENTER(("[CMiniportWaveRTStream::SetCurrentWritePositionInternal]")); + + ASSERT(m_bEoSReceived == FALSE); + + if (m_bEoSReceived) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + 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: Previous WaveRtBufferWritePosition that the driver received + //Parameter 3: Target WaveRtBufferWritePosition received from portcls + //Parameter 4: 0 + pAdapterComm->WriteEtwEvent(eMINIPORT_SET_WAVERT_BUFFER_WRITE_POSITION, + m_ullLinearPosition, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, + _ulCurrentWritePosition, // this is new write position + 0); // always zero + +// +// Check for eMINIPORT_GLITCH_REPORT - Same WaveRT buffer write during event driven mode. +// + if (m_ulNotificationIntervalMs > 0) + { + if (m_ulCurrentWritePosition == _ulCurrentWritePosition) + { + //Event type: eMINIPORT_GLITCH_REPORT + //Parameter 1: Current linear buffer position + //Parameter 2: Previous WaveRtBufferWritePosition that the driver received + //Parameter 3: Major glitch code: 3: Received same WaveRT buffer twice in a row during event driven mode + //Parameter 4: Minor code for the glitch cause + pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT, + m_ullLinearPosition, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, + 3, // received same WaveRT buffer twice in a row during event driven mode + _ulCurrentWritePosition); + } + } + + m_ulCurrentWritePosition = _ulCurrentWritePosition; + InterlockedExchange(&m_IsCurrentWritePositionUpdated, 1); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS CMiniportWaveRTStream::SetState +( + _In_ KSSTATE State_ +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + KIRQL oldIrql; + + // Spew an event for a pin state change request from portcls + //Event type: eMINIPORT_PIN_STATE + switch (State_) + { + case KSSTATE_STOP: + if (m_KsState == KSSTATE_ACQUIRE) + { + // Acquire stream resources + } + KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql); + // Reset DMA + m_llPacketCounter = 0; + m_ullPlayPosition = 0; + m_ullWritePosition = 0; + m_ullLinearPosition = 0; + m_ullPresentationPosition = 0; + + // Reset OS read/write positions + m_ulLastOsReadPacket = ULONG_MAX; + m_ulCurrentWritePosition = 0; + m_ulLastOsWritePacket = ULONG_MAX; + m_bEoSReceived = FALSE; + m_bLastBufferRendered = FALSE; + + KeReleaseSpinLock(&m_PositionSpinLock, oldIrql); + + // Wait until all work items are completed. + if (!m_bCapture && !g_DoNotCreateDataFiles) + { + m_SaveData.WaitAllWorkItems(); + } + break; + + case KSSTATE_ACQUIRE: + if (m_KsState == KSSTATE_STOP) + { + // Acquire stream resources + } + break; + + case KSSTATE_PAUSE: + + if (m_KsState > KSSTATE_PAUSE) + { + // + // Run -> Pause + // + + // Pause DMA + if (m_ulNotificationIntervalMs > 0) + { + ExCancelTimer(m_pNotificationTimer, NULL); + KeFlushQueuedDpcs(); + + // If pin is transitioning from RUN, save the time since last buffer completion event was sent + // so if the pin goes to RUN state again we can send the buffer completion event at correct time. + if (m_ullLastDPCTimeStamp > 0) + { + LARGE_INTEGER qpc; + LARGE_INTEGER qpcFrequency; + LONGLONG hnsCurrentTime; + + qpc = KeQueryPerformanceCounter(&qpcFrequency); + + // Convert ticks to 100ns units. + hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, qpc); + m_hnsDPCTimeCarryForward = hnsCurrentTime - m_ullLastDPCTimeStamp + m_hnsDPCTimeCarryForward; + } + } + } + // This call updates the linear buffer and presentation positions. + GetPositions(NULL, NULL, NULL); + break; + + case KSSTATE_RUN: + // Start DMA + LARGE_INTEGER ullPerfCounterTemp; + ullPerfCounterTemp = KeQueryPerformanceCounter(&m_ullPerformanceCounterFrequency); + m_ullLastDPCTimeStamp = m_ullDmaTimeStamp = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ullPerfCounterTemp); + + if (m_ulNotificationIntervalMs > 0) + { + // Set timer for 1 ms. This will cause DPC to run every 1 ms but driver will send out + // notification events only after notification interval. This timer is used by Simple Audio Sample to + // emulate hardware and send out notification event. Real hardware should not use this + // timer to fire notification event as it will drain power if the timer is running at 1 msec. + ExSetTimer + ( + m_pNotificationTimer, + (-1) * HNSTIME_PER_MILLISECOND, + HNSTIME_PER_MILLISECOND, // 1 ms + NULL + ); + + } + + break; + } + + m_KsState = State_; + + 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) + m_byteDisplacementCarryForward) / 1000 ; + m_byteDisplacementCarryForward = ((m_ulDmaMovementRate * TimeElapsedInMS) + m_byteDisplacementCarryForward) % 1000; + + // Increment presentation position even after last buffer is rendered. + m_ullPresentationPosition += ByteDisplacement; + + if (m_bCapture) + { + // Write sine wave to buffer. + WriteBytes(ByteDisplacement); + } + else + { + + if (m_bEoSReceived) + { + // since EoS flag is set, we'll need to make sure not to read data beyond EOS position. + // If driver's current position is less than EoS position, then make sure not to read data beyond EoS. + if (m_ullWritePosition <= m_ulCurrentWritePosition) + { + ByteDisplacement = min(ByteDisplacement, m_ulCurrentWritePosition - (ULONG)m_ullWritePosition); + } + // If our current position is ahead of EoS position and we'll wrap around after new position then adjust + // new position if it crosses EoS. + else if ((m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize < m_ullWritePosition) + { + if ((m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize > m_ulCurrentWritePosition) + { + ByteDisplacement = ByteDisplacement - (((ULONG)m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize - m_ulCurrentWritePosition); + } + } + } + + // If the last packet was rendered(read in the sample driver's case), send out an etw event. + if (m_bEoSReceived && !m_bLastBufferRendered + && (m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize == m_ulCurrentWritePosition) + { + m_bLastBufferRendered = TRUE; + } + + if (!g_DoNotCreateDataFiles) + { + // Read from buffer and write to a file. + ReadBytes(ByteDisplacement); + } + } + + // 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; + } + + // + // Simple Audio Sample 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 + +//============================================================================= +#pragma code_seg() +void +TimerNotifyRT +( + _In_ PEX_TIMER Timer, + _In_opt_ PVOID DeferredContext +) +{ + LARGE_INTEGER qpc; + LARGE_INTEGER qpcFrequency; + BOOL bufferCompleted = FALSE; + + UNREFERENCED_PARAMETER(Timer); + + _IRQL_limited_to_(DISPATCH_LEVEL); + + CMiniportWaveRTStream* _this = (CMiniportWaveRTStream*)DeferredContext; + + if (NULL == _this) + { + return; + } + + KIRQL oldIrql; + KeAcquireSpinLock(&_this->m_PositionSpinLock, &oldIrql); + + qpc = KeQueryPerformanceCounter(&qpcFrequency); + + // Convert ticks to 100ns units. + LONGLONG hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(_this->m_ullPerformanceCounterFrequency.QuadPart, qpc); + + // Calculate the time elapsed since the last we ran DPC that matched Notification interval. 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. + + ULONG TimeElapsedInMS = (ULONG)(hnsCurrentTime - _this->m_ullLastDPCTimeStamp + _this->m_hnsDPCTimeCarryForward)/10000; + + if (TimeElapsedInMS >= _this->m_ulNotificationIntervalMs) + { + // Carry forward the time greater than notification interval to adjust time to signal next buffer completion event accordingly. + _this->m_hnsDPCTimeCarryForward = hnsCurrentTime - _this->m_ullLastDPCTimeStamp + _this->m_hnsDPCTimeCarryForward - (_this->m_ulNotificationIntervalMs * 10000); + // Save the last time DPC ran at notification interval + _this->m_ullLastDPCTimeStamp = hnsCurrentTime; + bufferCompleted = TRUE; + } + + if (!bufferCompleted && !_this->m_bEoSReceived) + { + goto End; + } + + _this->UpdatePosition(qpc); + + if (!_this->m_bEoSReceived) + { + _this->m_llPacketCounter++; + } + + if (_this->m_KsState != KSSTATE_RUN) + { + goto End; + } + + PADAPTERCOMMON pAdapterComm = _this->m_pMiniport->GetAdapterCommObj(); + + // Simple buffer underrun detection. + if (!_this->IsCurrentWaveRTWritePositionUpdated() && !_this->m_bEoSReceived) + { + //Event type: eMINIPORT_GLITCH_REPORT + //Parameter 1: Current linear buffer position + //Parameter 2: Previous WaveRtBufferWritePosition that the driver received + //Parameter 3: Major glitch code: 1:WaveRT buffer is underrun + //Parameter 4: Minor code for the glitch cause + pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT, + _this->m_ullLinearPosition, + _this->GetCurrentWaveRTWritePosition(), + 1, // WaveRT buffer is underrun + 0); + } + + // Send buffer completion event if either of the following is true + // 1. Driver consumed a complete buffer for this stream + // 2. Driver consumed a partial buffer containing EoS for this stream + + if (!IsListEmpty(&_this->m_NotificationList) && + (bufferCompleted || _this->m_bLastBufferRendered)) + { + PLIST_ENTRY leCurrent = _this->m_NotificationList.Flink; + while (leCurrent != &_this->m_NotificationList) + { + NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry); + KeSetEvent(nleCurrent->NotificationEvent, 0, 0); + + leCurrent = leCurrent->Flink; + } + } + + if (_this->m_bLastBufferRendered) + { + ExCancelTimer(_this->m_pNotificationTimer, NULL); + } + +End: + KeReleaseSpinLock(&_this->m_PositionSpinLock, oldIrql); + return; +} +//============================================================================= + diff --git a/audio/simpleaudiosample/Source/Main/minwavertstream.h b/audio/simpleaudiosample/Source/Main/minwavertstream.h new file mode 100644 index 00000000..5545e0d8 --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/minwavertstream.h @@ -0,0 +1,248 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minwavertstream.h + +Abstract: + + Definition of wavert miniport class. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_MINWAVERTSTREAM_H_ +#define _SIMPLEAUDIOSAMPLE_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; + +EXT_CALLBACK TimerNotifyRT; + +//============================================================================= +// Referenced Forward +//============================================================================= +class CMiniportWaveRT; +typedef CMiniportWaveRT *PCMiniportWaveRT; + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CMiniportWaveRTStream +// +class CMiniportWaveRTStream : + public IDrmAudioStream, + public IMiniportWaveRTStreamNotification, + public IMiniportWaveRTInputStream, + public IMiniportWaveRTOutputStream, + public CUnknown +{ +protected: + PPORTWAVERTSTREAM m_pPortStream; + LIST_ENTRY m_NotificationList; + PEX_TIMER m_pNotificationTimer; + 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_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 EXT_CALLBACK 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; + ULONGLONG m_ullPresentationPosition; + ULONG m_ulLastOsReadPacket; + ULONG m_ulLastOsWritePacket; + LONGLONG m_llPacketCounter; + ULONGLONG m_ullDmaTimeStamp; + LARGE_INTEGER m_ullPerformanceCounterFrequency; + ULONGLONG m_hnsElapsedTimeCarryForward; + ULONGLONG m_ullLastDPCTimeStamp; + ULONGLONG m_hnsDPCTimeCarryForward; + ULONG m_byteDisplacementCarryForward; + 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; + BOOLEAN m_bEoSReceived; + BOOLEAN m_bLastBufferRendered; + KSPIN_LOCK m_PositionSpinLock; + // Member variable as config params for tone generator + ULONG m_ulHostCaptureToneFrequency; + // If abs(m_dwHostCaptureToneAmplitude) + abs(m_dwHostCaptureToneDCValue) > 100 + // m_dwHostCaptureToneDCValue will be compensated to make the sum equal to 100 + DWORD m_dwHostCaptureToneAmplitude; // must be between -100 to 100 + DWORD m_dwLoopbackCaptureToneAmplitude; // must be between -100 to 100 + DWORD m_dwHostCaptureToneDCOffset; // must be between -100 to 100 + DWORD m_dwLoopbackCaptureToneDCOffset; // must be between -100 to 100 + DWORD m_dwHostCaptureToneInitialPhase; // must be between -31416 to 31416 + DWORD m_dwLoopbackCaptureToneInitialPhase; // must be between -31416 to 31416 + // Member variable as config params for tone generator + +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 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 + ); + + + 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. + // + +#pragma code_seg() + + VOID WriteBytes + ( + _In_ ULONG ByteDisplacement + ); + + VOID ReadBytes + ( + _In_ ULONG ByteDisplacement + ); + + VOID UpdatePosition + ( + _In_ LARGE_INTEGER ilQPC + ); + + NTSTATUS SetCurrentWritePositionInternal + ( + _In_ ULONG ulCurrentWritePosition + ); + + NTSTATUS GetPositions + ( + _Out_opt_ ULONGLONG * _pullLinearBufferPosition, + _Out_opt_ ULONGLONG * _pullPresentationPosition, + _Out_opt_ LARGE_INTEGER * _pliQPCTime + ); + + NTSTATUS ReadRegistrySettings(); + +}; +typedef CMiniportWaveRTStream *PCMiniportWaveRTStream; +#endif // _SIMPLEAUDIOSAMPLE_MINWAVERTSTREAM_H_ + |
