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 | |
| 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.
46 files changed, 17187 insertions, 0 deletions
diff --git a/audio/simpleaudiosample/Package/package.VcxProj b/audio/simpleaudiosample/Package/package.VcxProj new file mode 100644 index 00000000..7d791164 --- /dev/null +++ b/audio/simpleaudiosample/Package/package.VcxProj @@ -0,0 +1,103 @@ +<?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> + <ItemGroup> + <ProjectReference Include="..\Source\Main\Main.vcxproj"> + <Project>{E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}</Project> + </ProjectReference> + </ItemGroup> + <PropertyGroup Label="PropertySheets"> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Utility</ConfigurationType> + <DriverType>Package</DriverType> + <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck> + <Configuration>Debug</Configuration> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Globals"> + <ProjectGuid>{830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}</ProjectGuid> + <SampleGuid>{E9D167E6-E633-4284-8F02-FF86DF3B6D7F}</SampleGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup /> + <PropertyGroup> + <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor> + <ImportToStore>False</ImportToStore> + <InstallMode>None</InstallMode> + <HardwareIdString /> + <CommandLine /> + <ScriptPath /> + <DeployFiles /> + <ScriptName /> + <ScriptDeviceQuery>%PathToInf%</ScriptDeviceQuery> + <EnableVerifier>False</EnableVerifier> + <AllDrivers>False</AllDrivers> + <VerifyProjectOutput>True</VerifyProjectOutput> + <VerifyDrivers /> + <VerifyFlags>133563</VerifyFlags> + </PropertyGroup> + <ItemDefinitionGroup> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/audio/simpleaudiosample/Package/package.VcxProj.Filters b/audio/simpleaudiosample/Package/package.VcxProj.Filters new file mode 100644 index 00000000..63b75548 --- /dev/null +++ b/audio/simpleaudiosample/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{0924612D-FBA7-477E-BBB9-B3B6EA7C6B03}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{97311738-0AD8-4F7E-AC88-9C9BEAF53AEE}</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>{8DC1C4F0-C285-400E-A983-77A339891354}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{9F086F34-79D2-4FAA-87FD-C2DBEFCED0BA}</UniqueIdentifier> + </Filter> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/simpleaudiosample/README.md b/audio/simpleaudiosample/README.md new file mode 100644 index 00000000..5309626a --- /dev/null +++ b/audio/simpleaudiosample/README.md @@ -0,0 +1,133 @@ +--- +page_type: sample +description: "The Microsoft Simple Audio Sample Device Driver shows how to develop a simple WDM audio driver that exposes support for two basic audio devices (a speaker and microphone array).” +languages: +- cpp +products: +- windows +- windows-wdk +--- + +# Simple Audio Sample Device Driver + +## Introduction + +The Microsoft Simple Audio Sample Device Driver shows how to develop a simple WDM audio driver that exposes support for two basic audio devices (a speaker and microphone array). These audio devices are embedded in the system (not pluggable) and the driver uses WaveRT for rendering these devices. The driver also uses a "virtual audio device" instead of an actual hardware-based adapter. + +Driver developers can use the framework in this sample to provide support for various audio devices without concern for hardware dependencies. The framework includes implementations of the following interfaces: + +- The CAdapterCommon interface gives the miniports access to virtual mixer hardware. It also implements the **IAdapterPowerManagement** interface. + +- The CMiniportTopologySimpleAudioSample interface is the base class for all sample topologies. It has very basic common functions. In addition, this class contains common topology property handlers. + +The following table shows the contents of the various subdirectories of this sample. + +| Directory | Description | +| --- | --- | +| Source | Contains all of the source code for the Simple Audio Sample driver. | +| Filters | Contains the definitions of the wave and topology filters of the endpoints that are present in the Simple Audio Sample driver (speaker and microphone array). | +| Main | Contains the core functionality of the driver. | +| Utilities | Contains helper files that provide additional functionality, such as a test tone generator and an audio file reader. | + +## Build the sample + +If you simply want to build this sample driver and don't intend to run or test it, then you do not need a target computer (also called a test computer). If, however, you would like to deploy, run and test this sample driver, then you need a second computer that will serve as your target computer. Instructions are provided in the **Run the sample** section to show you how to set up the target computer - also referred to as *provisioning* a target computer. + +Perform the following steps to build this sample driver. + +### 1. Open the driver solution in Visual Studio + +In Microsoft Visual Studio, Click **File** \> **Open** \> **Project/Solution...** and navigate to the folder that contains the sample files (for example, *C:\Windows-driver-samples\audio\SimpleAudioSample*). Double-click the *SimpleAudioSample* solution file. + +In Visual Studio locate the Solution Explorer. (If this is not already open, choose **Solution Explorer** from the **View** menu.) In Solution Explorer, you can see one solution that has five projects. + +### 2. Set the sample's configuration and platform + +In Solution Explorer, right-click **Solution ‘SimpleAudioSample’ (5 of 5 projects)**, and choose **Configuration Manager**. Make sure that the configuration and platform settings are the same for the five projects. By default, the configuration is set to **Debug**, and the platform is set to **Win32** for all the projects. If you make any configuration and/or platform changes for one project, you must make the same changes for all the remaining projects. + +### 3. Build the sample using Visual Studio + +In Visual Studio, click **Build** \> **Build Solution**. + +### 4. Locate the built driver package + +In File Explorer, navigate to the folder that contains the sample files. For example, you would navigate to *C:\\Windows-driver-samples\\audio\\SimpleAudioSample*, if that's the folder you specified in the preceding Step 1. + +In the folder, the location of the driver package varies depending on the configuration and platform settings that you selected in the **Configuration Manager**. For example, if you left the default settings unchanged, then the built driver package will be saved to a folder named *Debug* inside the same folder as the sample files. Double-click the folder for the built driver package, and then double-click the folder named *package*. + +The package should contain these files: + +| File | Description | +| --- | --- | +| SimpleAudioSample.sys | The driver file. | +| SimpleAudioSample.cat | A signed catalog file, which serves as the signature for the entire package. | +| SimpleAudioSample.inf | A non-componentized information (INF) file that contains information needed to install the driver. | + +## Run the sample + +The computer where you install the driver is called the *target computer* or the *test computer*. Typically this is a separate computer from the computer on which you develop and build the driver package. The computer where you develop and build the driver is called the *host computer*. + +The process of moving the driver package to the target computer and installing the driver is called *deploying* the driver. You can deploy the SimpleAudioSample driver automatically or manually. + +### Prepare the target computer + +First of all, install the latest [Windows Driver Kit](https://docs.microsoft.com/windows-hardware/drivers/download-the-wdk) (WDK) on the target computer. + +Before you manually deploy a driver, you must prepare the target computer by turning on test signing and by installing a certificate. You also need to locate the DevCon tool in your WDK installation. After that you're ready to run the built driver sample. + +Open a Command Prompt window as Administrator. Then enter the following command: + +`bcdedit /set TESTSIGNING ON` + +and reboot the target computer. + +> [!IMPORTANT] +> Before using BCDEdit to change boot information you may need to temporarily suspend Windows security features such as BitLocker and Secure Boot on the test PC. + +Re-enable these security features when testing is complete and appropriately manage the test PC, when the security features are disabled. + +After rebooting, navigate to the Tools folder in your WDK installation and locate the DevCon tool. For example, look in the following folder: + +C:\\Program Files (x86)\\Windows Kits\\10\\Tools\\x64\\devcon.exe + +Copy *devcon.exe* to a folder on the target computer where it is easier to find. For example, create a *C:\\Tools* folder and copy *devcon.exe* to that folder. + +Create a folder on the target for the built driver package (for example, *C:\\SimpleAudioSampleDriver*). Copy all the files from the built driver package on the host computer and save them to the folder that you created on the target computer. + +Create a folder on the target computer for the certificate created by the build process. For example, you could create a folder named *C:\\Certificates* on the target computer, and then copy *package.cer* to it from the host computer. You can find this certificate in the same folder on the host computer, as the *package* folder that contains the built driver files. On the target computer, right-click the certificate file, and click **Install**, then follow the prompts to install the test certificate. + +If you need more detailed instructions for setting up the target computer, see [Preparing a Computer for Manual Driver Deployment](https://docs.microsoft.com/windows-hardware/drivers/develop/preparing-a-computer-for-manual-driver-deployment). + +#### A note on signatures + +Since most of these binary files are executed in kernel mode, it is important that they are signed and, optionally, to have a kernel debugger attached. + +Without any signature or kernel debugger, the driver will not be installed in the target computer. With a kernel debugger attached, the driver can be installed and the driver files (.sys extension) would be loaded. The only way of installing and executing the driver sample without a kernel debugger attached is to have both the .sys and .cat files signed with a trusted certificate. + +For more information on the subject, see [Driver signing](https://docs.microsoft.com/windows-hardware/drivers/install/driver-signing). + +### Install the driver + +To install SimpleAudioSample.inf, open a Command Prompt window as administrator on the target computer, then navigate to your driver package folder and enter the following command: + +`devcon install SimpleAudioSample.inf Root\SimpleAudioSample` + +If you get an error message about *devcon* not being recognized, try adding the path to the *devcon* tool. For example, if you copied it to a folder called *C:\\Tools*, then try using the following command: + +`C:\\tools\\devcon install SimpleAudioSample.inf Root\SimpleAudioSample` + +This installs the Simple Audio Sample driver with a hardware ID of "Root\SimpleAudioSample". + +Once the INF file is installed, a device should appear in Device Manager named *Microsoft Virtual Audio Device (WDM) - Simple Audio Sample*. + +After successfully installing the sample driver, you're now ready to test it. + +For more detailed instructions, see [Provision a computer for driver deployment and testing](https://docs.microsoft.com/windows-hardware/drivers/gettingstarted/provision-a-target-computer-wdk-8-1). + +### Test the driver + +On the target computer, in a Command Prompt window, enter **devmgmt.msc** to open Device Manager. In Device Manager, on the **View** menu, choose **Devices by type**. In the device tree, locate *Virtual Audio Device (WDM) – Simple Audio Sample*. This is typically under the **Sound, video and game controllers** node. + +On the target computer, open Control Panel and navigate to **Hardware and Sound** \> **Manage audio devices**. In the Sound dialog box, select the speaker icon labeled as *Virtual Audio Device (WDM) – Simple Audio Sample*, then click **Set Default**, but do not click **OK**. This will keep the Sound dialog box open. + +Locate an MP3 or other audio file on the target computer and double-click to play it. Then in the Sound dialog box, verify that there is activity in the volume level indicator associated with the *Virtual Audio Device (WDM) – Simple Audio Sample* driver. diff --git a/audio/simpleaudiosample/SimpleAudioSample.sln b/audio/simpleaudiosample/SimpleAudioSample.sln new file mode 100644 index 00000000..407838ba --- /dev/null +++ b/audio/simpleaudiosample/SimpleAudioSample.sln @@ -0,0 +1,113 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{FA56D96B-290B-4C73-A5E1-02C1861D3224}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filters", "Filters", "{952A7CB1-E516-44AA-AC38-4AEED9874C41}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{0B4B8E51-20A8-43A4-A8DA-42FF90D628AC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Inc", "Inc", "{C65091AE-E5B7-4359-8984-55852792406E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{C06F6070-75EE-4F57-8D35-4AC2AB08D029}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Filters", "Source\Filters\Filters.vcxproj", "{771312CF-E5A2-4676-8142-86CEBDF99E2B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Utilities", "Source\Utilities\Utilities.vcxproj", "{33E61864-6F2C-4F9F-BE70-8F8985A4F283}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Inc", "Source\Inc\Inc.vcxproj", "{4B664BA5-057A-41B8-B365-2C99065C4DFA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Main", "Source\Main\Main.vcxproj", "{E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}" + ProjectSection(ProjectDependencies) = postProject + {33E61864-6F2C-4F9F-BE70-8F8985A4F283} = {33E61864-6F2C-4F9F-BE70-8F8985A4F283} + {771312CF-E5A2-4676-8142-86CEBDF99E2B} = {771312CF-E5A2-4676-8142-86CEBDF99E2B} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Debug|ARM64.Build.0 = Debug|ARM64 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Debug|Win32.ActiveCfg = Debug|Win32 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Debug|Win32.Build.0 = Debug|Win32 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Debug|x64.ActiveCfg = Debug|x64 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Debug|x64.Build.0 = Debug|x64 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Release|ARM64.ActiveCfg = Release|ARM64 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Release|ARM64.Build.0 = Release|ARM64 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Release|Win32.ActiveCfg = Release|Win32 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Release|Win32.Build.0 = Release|Win32 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Release|x64.ActiveCfg = Release|x64 + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}.Release|x64.Build.0 = Release|x64 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Debug|ARM64.Build.0 = Debug|ARM64 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Debug|Win32.ActiveCfg = Debug|Win32 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Debug|Win32.Build.0 = Debug|Win32 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Debug|x64.ActiveCfg = Debug|x64 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Debug|x64.Build.0 = Debug|x64 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Release|ARM64.ActiveCfg = Release|ARM64 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Release|ARM64.Build.0 = Release|ARM64 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Release|Win32.ActiveCfg = Release|Win32 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Release|Win32.Build.0 = Release|Win32 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Release|x64.ActiveCfg = Release|x64 + {771312CF-E5A2-4676-8142-86CEBDF99E2B}.Release|x64.Build.0 = Release|x64 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Debug|ARM64.Build.0 = Debug|ARM64 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Debug|Win32.ActiveCfg = Debug|Win32 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Debug|Win32.Build.0 = Debug|Win32 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Debug|x64.ActiveCfg = Debug|x64 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Debug|x64.Build.0 = Debug|x64 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Release|ARM64.ActiveCfg = Release|ARM64 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Release|ARM64.Build.0 = Release|ARM64 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Release|Win32.ActiveCfg = Release|Win32 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Release|Win32.Build.0 = Release|Win32 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Release|x64.ActiveCfg = Release|x64 + {33E61864-6F2C-4F9F-BE70-8F8985A4F283}.Release|x64.Build.0 = Release|x64 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Debug|ARM64.Build.0 = Debug|ARM64 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Debug|Win32.ActiveCfg = Debug|Win32 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Debug|Win32.Build.0 = Debug|Win32 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Debug|x64.ActiveCfg = Debug|x64 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Debug|x64.Build.0 = Debug|x64 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Release|ARM64.ActiveCfg = Release|ARM64 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Release|ARM64.Build.0 = Release|ARM64 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Release|Win32.ActiveCfg = Release|Win32 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Release|Win32.Build.0 = Release|Win32 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Release|x64.ActiveCfg = Release|x64 + {4B664BA5-057A-41B8-B365-2C99065C4DFA}.Release|x64.Build.0 = Release|x64 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Debug|ARM64.Build.0 = Debug|ARM64 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Debug|Win32.Build.0 = Debug|Win32 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Debug|x64.ActiveCfg = Debug|x64 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Debug|x64.Build.0 = Debug|x64 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Release|ARM64.ActiveCfg = Release|ARM64 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Release|ARM64.Build.0 = Release|ARM64 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Release|Win32.ActiveCfg = Release|Win32 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Release|Win32.Build.0 = Release|Win32 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Release|x64.ActiveCfg = Release|x64 + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A} = {FA56D96B-290B-4C73-A5E1-02C1861D3224} + {771312CF-E5A2-4676-8142-86CEBDF99E2B} = {952A7CB1-E516-44AA-AC38-4AEED9874C41} + {33E61864-6F2C-4F9F-BE70-8F8985A4F283} = {0B4B8E51-20A8-43A4-A8DA-42FF90D628AC} + {4B664BA5-057A-41B8-B365-2C99065C4DFA} = {C65091AE-E5B7-4359-8984-55852792406E} + {E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF} = {C06F6070-75EE-4F57-8D35-4AC2AB08D029} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6DB4FE19-4FE0-4DC4-B898-C46E01E6BDF4} + EndGlobalSection +EndGlobal diff --git a/audio/simpleaudiosample/Source/Filters/Filters.vcxproj b/audio/simpleaudiosample/Source/Filters/Filters.vcxproj new file mode 100644 index 00000000..d8496aa8 --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/Filters.vcxproj @@ -0,0 +1,264 @@ +<?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>{771312CF-E5A2-4676-8142-86CEBDF99E2B}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{F51739CE-5253-42B5-9191-57F28B5842C6}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup 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" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>Filters</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <TargetName>Filters</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>Filters</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <TargetName>Filters</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>Filters</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>Filters</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="micarraytopo.cpp" /> + <ClCompile Include="speakertopo.cpp" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project> diff --git a/audio/simpleaudiosample/Source/Filters/Filters.vcxproj.Filters b/audio/simpleaudiosample/Source/Filters/Filters.vcxproj.Filters new file mode 100644 index 00000000..b573243b --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/Filters.vcxproj.Filters @@ -0,0 +1,27 @@ +<?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>{75C3E63D-04EA-4EB3-B9F1-505497C51F71}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{5E8F8D4C-2886-4109-9E78-CE5EA6DB4AC4}</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>{75D3016A-4ED8-4FAF-9817-394BE1112534}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="*.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> diff --git a/audio/simpleaudiosample/Source/Filters/micarray1toptable.h b/audio/simpleaudiosample/Source/Filters/micarray1toptable.h new file mode 100644 index 00000000..3bbd80f3 --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/micarray1toptable.h @@ -0,0 +1,210 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray1toptable.h + +Abstract: + + Declaration of topology tables for the mic array. + +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_MICARRAY1TOPTABLE_H_ +#define _SIMPLEAUDIOSAMPLE_MICARRAY1TOPTABLE_H_ + +// +// {6ae81ff4-203e-4fe1-88aa-f2d57775cd4a} +DEFINE_GUID(MICARRAY1_CUSTOM_NAME, + 0x6ae81ff4, 0x203e, 0x4fe1, 0x88, 0xaa, 0xf2, 0xd5, 0x77, 0x75, 0xcd, 0x4a); + +//============================================================================= +static +KSDATARANGE MicArray1TopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicArray1TopoPinDataRangePointersBridge[] = +{ + &MicArray1TopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray1TopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray1TopoPinDataRangePointersBridge), // DataRangesCount + MicArray1TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE_ARRAY, // Category + &MICARRAY1_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray1TopoPinDataRangePointersBridge), // DataRangesCount + MicArray1TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1Volume, MicArray1PropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1Mute, MicArray1PropertiesMute); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray1TopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicArray1Volume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicArray1Mute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + } +}; + +C_ASSERT(KSNODE_TOPO_VOLUME == 0); +C_ASSERT(KSNODE_TOPO_MUTE == 1); + +static +PCCONNECTION_DESCRIPTOR MicArray1TopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_SNR, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_SENSITIVITY2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1TopoFilter, MicArray1PropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray1TopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray1TopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray1TopoMiniportPins), // PinCount + MicArray1TopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray1TopologyNodes), // NodeCount + MicArray1TopologyNodes, // Nodes + SIZEOF_ARRAY(MicArray1TopoMiniportConnections),// ConnectionCount + MicArray1TopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SIMPLEAUDIOSAMPLE_MICARRAY1TOPTABLE_H_ diff --git a/audio/simpleaudiosample/Source/Filters/micarraytopo.cpp b/audio/simpleaudiosample/Source/Filters/micarraytopo.cpp new file mode 100644 index 00000000..66cc7d89 --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/micarraytopo.cpp @@ -0,0 +1,825 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraytopo.cpp + +Abstract: + + Implementation of topology miniport for the mic array. + +--*/ + +#pragma warning (disable : 4127) + +#include "definitions.h" +#include "endpoints.h" +#include "common.h" +#include "kshelper.h" +#include "micarraytopo.h" +#include "micarray1toptable.h" + +constexpr float MICARRAY_SENSITIVITY = -46.5f; +constexpr float MICARRAY_SENSITIVITY2 = -23.5f; +constexpr float MICARRAY_SNR = 66.0f; + +constexpr inline LONG FloatToFixedPoint16_16(float fl) +{ + return (static_cast<LONG>((fl) * (65536.0f))); +} + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CreateMicArrayMiniportTopology +( + _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 - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CMicArrayMiniportTopology* obj = + new (PoolFlags, MINTOPORT_POOLTAG) + CMicArrayMiniportTopology(UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels, + MiniportPair->DeviceType); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + + *Unknown = reinterpret_cast<IUnknown*>(obj); + + return STATUS_SUCCESS; +} // CreateMicArrayMiniportTopology + +//============================================================================= +CMicArrayMiniportTopology::~CMicArrayMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMicArrayMiniportTopology::~CMicArrayMiniportTopology]")); +} // ~CMicArrayMiniportTopology + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySimpleAudioSample::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR* OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySimpleAudioSample::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the IUnknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CMicArrayMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySimpleAudioSample::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID* Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerMicArrayGeometry +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerMicArrayGeometry]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cElements = 2; + ULONG cbNeeded = FIELD_OFFSET(KSAUDIO_MIC_ARRAY_GEOMETRY, KsMicCoord) + + cElements * sizeof(KSAUDIO_MICROPHONE_COORDINATES); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSAUDIO_MIC_ARRAY_GEOMETRY pMAG = (PKSAUDIO_MIC_ARRAY_GEOMETRY)PropertyRequest->Value; + const SHORT MicArray_180_Degrees = 31416; // 10000 * pi + const SHORT MicArray_45_Degrees = 7854; // 10000 * pi / 4 + + // fill in mic array geometry fields + pMAG->usVersion = 0x0100; // Version of Mic array specification (0x0100) + pMAG->usMicArrayType = (USHORT)KSMICARRAY_MICARRAYTYPE_LINEAR; // Type of Mic Array + pMAG->wVerticalAngleBegin = -MicArray_45_Degrees; // Work Volume Vertical Angle Begin + pMAG->wVerticalAngleEnd = MicArray_45_Degrees; // Work Volume Vertical Angle End + pMAG->wHorizontalAngleBegin = 0; // Work Volume HorizontalAngle Begin + pMAG->wHorizontalAngleEnd = 0; // Work Volume HorizontalAngle End + pMAG->usFrequencyBandLo = 100; // Low end of Freq Range + pMAG->usFrequencyBandHi = 8000; // High end of Freq Range + + pMAG->usNumberOfMicrophones = 2; // Count of microphone coordinate structures to follow. + + pMAG->KsMicCoord[0].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[0].wXCoord = 0; + pMAG->KsMicCoord[0].wYCoord = 100; + pMAG->KsMicCoord[0].wZCoord = 0; + pMAG->KsMicCoord[0].wVerticalAngle = 0; + pMAG->KsMicCoord[0].wHorizontalAngle = 0; + + pMAG->KsMicCoord[1].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[1].wXCoord = 0; + pMAG->KsMicCoord[1].wYCoord = -100; + pMAG->KsMicCoord[1].wZCoord = 0; + pMAG->KsMicCoord[1].wVerticalAngle = 0; + pMAG->KsMicCoord[1].wHorizontalAngle = 0; + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerMicProperties +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_MIC_SNR ) +Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_MIC_SENSITIVITY2 ) + +Arguments: + +PropertyRequest - + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerMicProperties]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + KFLOATING_SAVE saveData; + NTSTATUS fstatus; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), + 0 + ); + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_MIC_SNR) + { + LONG* micSNR = (LONG*)PropertyRequest->Value; + fstatus = KeSaveFloatingPointState(&saveData); + if (NT_SUCCESS(fstatus)) + { + // Return microphone SNR information. + *micSNR = FloatToFixedPoint16_16(MICARRAY_SNR); // convert float dB to fixed point arithmetic + KeRestoreFloatingPointState(&saveData); + } + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_MIC_SENSITIVITY2) + { + LONG* micSensitivity2 = (LONG*)PropertyRequest->Value; + fstatus = KeSaveFloatingPointState(&saveData); + if (NT_SUCCESS(fstatus)) + { + // Return microphone SNR information. + *micSensitivity2 = FloatToFixedPoint16_16(MICARRAY_SENSITIVITY2); // convert float dB to fixed point arithmetic + KeRestoreFloatingPointState(&saveData); + } + ntStatus = STATUS_SUCCESS; + } + } + else + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI + 1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + pDesc->ChannelMapping = 0; // Don't specify channel mask for array mic + pDesc->Color = 0x00000000; // Black. This is an integrated device + pDesc->ConnectionType = eConnTypeUnknown; // Integrated. + pDesc->GenLocation = eGenLocPrimaryBox; + pDesc->GeoLocation = eGeoLocFront; + pDesc->PortConnection = ePortConnIntegratedDevice; + pDesc->IsConnected = TRUE; // This is an integrated device, so it's always "connected" + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI + 1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = 0; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicArrayTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicArrayTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMicArrayMiniportTopology pMiniport = (PCMicArrayMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY) + { + ntStatus = pMiniport->PropertyHandlerMicArrayGeometry(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_MIC_SENSITIVITY2) + { + ntStatus = pMiniport->PropertyHandlerMicProperties(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_MIC_SNR) + { + ntStatus = pMiniport->PropertyHandlerMicProperties(PropertyRequest); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_TopoFilter + +//============================================================================= +NTSTATUS +PropertyHandler_MicArrayTopology +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicArrayTopology]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + PCMicArrayMiniportTopology pMiniport = (PCMicArrayMiniportTopology)PropertyRequest->MajorTarget; + + return pMiniport->PropertyHandlerGeneric(PropertyRequest); +} // PropertyHandler_MicArrayTopology + +#pragma code_seg() diff --git a/audio/simpleaudiosample/Source/Filters/micarraytopo.h b/audio/simpleaudiosample/Source/Filters/micarraytopo.h new file mode 100644 index 00000000..4f4a621f --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/micarraytopo.h @@ -0,0 +1,97 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraytopo.h + +Abstract: + + Declaration of mic array topology miniport. + +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_MICARRAYTOPO_H_ +#define _SIMPLEAUDIOSAMPLE_MICARRAYTOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMicArrayMiniportTopology +// + +#pragma code_seg() +class CMicArrayMiniportTopology : + public CMiniportTopologySimpleAudioSample, + public IMiniportTopology, + public CUnknown +{ +public: + DECLARE_STD_UNKNOWN(); + CMicArrayMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR* FilterDesc, + _In_ USHORT DeviceMaxChannels, + _In_ eDeviceType DeviceType + ) + : CUnknown(UnknownOuter), + CMiniportTopologySimpleAudioSample(FilterDesc, DeviceMaxChannels), + m_DeviceType(DeviceType) + { + ASSERT(m_DeviceType == eMicArrayDevice1); + } + + ~CMicArrayMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerMicArrayGeometry + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerMicProperties + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +protected: + eDeviceType m_DeviceType; + +}; + +typedef CMicArrayMiniportTopology* PCMicArrayMiniportTopology; + + +NTSTATUS +CreateMicArrayMiniportTopology( + _Out_ PUNKNOWN* Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _In_ POOL_FLAGS PoolFlags, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +); + +NTSTATUS PropertyHandler_MicArrayTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); +NTSTATUS PropertyHandler_MicArrayTopology(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SIMPLEAUDIOSAMPLE_MICARRAYTOPO_H_ diff --git a/audio/simpleaudiosample/Source/Filters/micarraywavtable.h b/audio/simpleaudiosample/Source/Filters/micarraywavtable.h new file mode 100644 index 00000000..53656038 --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/micarraywavtable.h @@ -0,0 +1,292 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraywavtable.h + +Abstract:- + + Declaration of wave miniport tables for the mic array. + +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_MICARRAYWAVTABLE_H_ +#define _SIMPLEAUDIOSAMPLE_MICARRAYWAVTABLE_H_ + +// +// Mic array range. +// +#define MICARRAY_RAW_CHANNELS 2 // Channels for raw mode +#define MICARRAY_DEVICE_MAX_CHANNELS 2 // Max channels overall +#define MICARRAY_32_BITS_PER_SAMPLE_PCM 32 // 32 Bits Per Sample +#define MICARRAY_RAW_SAMPLE_RATE 48000 // Raw sample rate + +// +// Max # of pin instances. +// +#define MICARRAY_MAX_INPUT_STREAMS 1 + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicArrayPinSupportedDeviceFormats[] = +{ + // 48 KHz 32-bit 2 channels + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 384000, + 8, + 32, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 32, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicArrayPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicArrayPinSupportedDeviceFormats[0].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES MicArrayPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicArrayPinSupportedDeviceFormats, + SIZEOF_ARRAY(MicArrayPinSupportedDeviceFormats), + MicArrayPinSupportedDeviceModes, + SIZEOF_ARRAY(MicArrayPinSupportedDeviceModes) + } +}; + +//============================================================================= +// Data ranges +// +// See CMiniportWaveRT::DataRangeIntersection. +// +static +KSDATARANGE_AUDIO MicArrayPinDataRangesRawStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_RAW_CHANNELS, + MICARRAY_32_BITS_PER_SAMPLE_PCM, + MICARRAY_32_BITS_PER_SAMPLE_PCM, + MICARRAY_RAW_SAMPLE_RATE, + MICARRAY_RAW_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE MicArrayPinDataRangePointersStream[] = +{ + // All supported device formats should be listed in the DataRange. + PKSDATARANGE(&MicArrayPinDataRangesRawStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE MicArrayPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicArrayPinDataRangePointersBridge[] = +{ + &MicArrayPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArrayWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArrayPinDataRangePointersBridge), + MicArrayPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave In Streaming Pin (Capture) KSPIN_WAVE_HOST + { + MICARRAY_MAX_INPUT_STREAMS, + MICARRAY_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArrayPinDataRangePointersStream), + MicArrayPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArrayWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArrayWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST }, +}; + +//============================================================================= + +static +SIMPLEAUDIOSAMPLE_PROPERTY_ITEM PropertiesMicArrayWaveFilter[] = +{ + { + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL, + NULL, + 0 + }, + { + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL, + NULL, + 0 + }, + { + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL, + NULL, + 0 + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArrayWaveFilter, PropertiesMicArrayWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArrayWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArrayWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArrayWaveMiniportPins), // PinCount + MicArrayWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArrayWaveMiniportNodes), // NodeCount + MicArrayWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicArrayWaveMiniportConnections), // ConnectionCount + MicArrayWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SIMPLEAUDIOSAMPLE_MICARRAYWAVTABLE_H_ diff --git a/audio/simpleaudiosample/Source/Filters/minipairs.h b/audio/simpleaudiosample/Source/Filters/minipairs.h new file mode 100644 index 00000000..b7d8950f --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/minipairs.h @@ -0,0 +1,177 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minipairs.h + +Abstract: + + Local audio endpoint filter definitions. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_MINIPAIRS_H_ +#define _SIMPLEAUDIOSAMPLE_MINIPAIRS_H_ + +#include "speakertopo.h" +#include "speakertoptable.h" +#include "speakerwavtable.h" + +#include "micarraytopo.h" +#include "micarray1toptable.h" +#include "micarraywavtable.h" + + +NTSTATUS +CreateMiniportWaveRTSimpleAudioSample +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_FLAGS, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +NTSTATUS +CreateMiniportTopologySimpleAudioSample +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_FLAGS, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +// +// Render miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for speaker (internal) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 1|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER3_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerMiniports = +{ + eSpeakerDevice, + L"TopologySpeaker", // make sure this or the template name matches with KSNAME_TopologySpeaker in the inf's [Strings] section + NULL, // optional template name + CreateMiniportTopologySimpleAudioSample, + &SpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpeaker", // make sure this or the template name matches with KSNAME_WaveSpeaker in the inf's [Strings] section + NULL, // optional template name + CreateMiniportWaveRTSimpleAudioSample, + &SpeakerWaveMiniportFilterDescriptor, + 0, // Interface properties + NULL, + SPEAKER_DEVICE_MAX_CHANNELS, + SpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerPinDeviceFormatsAndModes), + SpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS, +}; + +// +// Capture miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic array 1 (front) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray1TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray1Miniports = +{ + eMicArrayDevice1, + L"TopologyMicArray1", // make sure this or the template name matches with KSNAME_TopologyMicArray1 in the inf's [Strings] section + NULL, // optional template name + CreateMicArrayMiniportTopology, + &MicArray1TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray1", // make sure this or the tempalte name matches with KSNAME_WaveMicArray1 in the inf's [Strings] section + NULL, // optional template name + CreateMiniportWaveRTSimpleAudioSample, + &MicArrayWaveMiniportFilterDescriptor, + 0, // Interface properties + NULL, + MICARRAY_DEVICE_MAX_CHANNELS, + MicArrayPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArrayPinDeviceFormatsAndModes), + MicArray1TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray1TopologyPhysicalConnections), + ENDPOINT_NO_FLAGS, +}; + + +//============================================================================= +// +// Render miniport pairs. NOTE: the split of render and capture is arbitrary and +// unnessary, this array could contain capture endpoints. +// +static +PENDPOINT_MINIPAIR g_RenderEndpoints[] = +{ + &SpeakerMiniports, +}; + +#define g_cRenderEndpoints (SIZEOF_ARRAY(g_RenderEndpoints)) + +//============================================================================= +// +// Capture miniport pairs. NOTE: the split of render and capture is arbitrary and +// unnessary, this array could contain render endpoints. +// +static +PENDPOINT_MINIPAIR g_CaptureEndpoints[] = +{ + &MicArray1Miniports, +}; + +#define g_cCaptureEndpoints (SIZEOF_ARRAY(g_CaptureEndpoints)) + +//============================================================================= +// +// Total miniports = # endpoints * 2 (topology + wave). +// +#define g_MaxMiniports ((g_cRenderEndpoints + g_cCaptureEndpoints) * 2) + +#endif // _SIMPLEAUDIOSAMPLE_MINIPAIRS_H_ diff --git a/audio/simpleaudiosample/Source/Filters/speakertopo.cpp b/audio/simpleaudiosample/Source/Filters/speakertopo.cpp new file mode 100644 index 00000000..d1635efa --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/speakertopo.cpp @@ -0,0 +1,118 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertopo.cpp + +Abstract: + + Implementation of topology miniport for the speaker (internal). +--*/ + +#pragma warning (disable : 4127) + +#include "definitions.h" +#include "endpoints.h" +#include "mintopo.h" +#include "speakertopo.h" +#include "speakertoptable.h" + + +#pragma code_seg("PAGE") +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription( + PropertyRequest, + ARRAYSIZE(SpeakerJackDescriptions), + SpeakerJackDescriptions + ); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2( + PropertyRequest, + ARRAYSIZE(SpeakerJackDescriptions), + SpeakerJackDescriptions, + 0 // jack capabilities + ); + } + } + + return ntStatus; +} // PropertyHandler_SpeakerTopoFilter + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerTopology +( + _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_SpeakerTopology]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + return pMiniport->PropertyHandlerGeneric(PropertyRequest); +} // PropertyHandler_SpeakerTopology + +#pragma code_seg() diff --git a/audio/simpleaudiosample/Source/Filters/speakertopo.h b/audio/simpleaudiosample/Source/Filters/speakertopo.h new file mode 100644 index 00000000..0eff1ebb --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/speakertopo.h @@ -0,0 +1,22 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertopo.h + +Abstract: + + Declaration of topology miniport for the speaker (internal). +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_SPEAKERTOPO_H_ +#define _SIMPLEAUDIOSAMPLE_SPEAKERTOPO_H_ + +NTSTATUS PropertyHandler_SpeakerTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +NTSTATUS PropertyHandler_SpeakerTopology(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SIMPLEAUDIOSAMPLE_SPEAKERTOPO_H_ diff --git a/audio/simpleaudiosample/Source/Filters/speakertoptable.h b/audio/simpleaudiosample/Source/Filters/speakertoptable.h new file mode 100644 index 00000000..1b046d96 --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/speakertoptable.h @@ -0,0 +1,206 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertoptable.h + +Abstract: + + Declaration of topology tables. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_SPEAKERTOPTABLE_H_ +#define _SIMPLEAUDIOSAMPLE_SPEAKERTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE SpeakerTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpeakerTopoPinDataRangePointersBridge[] = +{ + &SpeakerTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_SPEAKER, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION SpeakerJackDescBridge = +{ + KSAUDIO_SPEAKER_STEREO, + 0xB3C98C, // Color spec for green + eConnTypeUnknown, + eGeoLocFront, + eGenLocPrimaryBox, + ePortConnIntegratedDevice, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpeakerJackDescriptions[] = +{ + NULL, + &SpeakerJackDescBridge +}; + +//============================================================================= +static +PCPROPERTY_ITEM SpeakerPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerVolume, SpeakerPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM SpeakerPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerMute, SpeakerPropertiesMute); + +//============================================================================= +static +PCNODE_DESCRIPTOR SpeakerTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationSpeakerVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MASTER_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationSpeakerMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MASTER_MUTE // Name + } +}; + +C_ASSERT(KSNODE_TOPO_VOLUME == 0); +C_ASSERT(KSNODE_TOPO_MUTE == 1); + +static +PCCONNECTION_DESCRIPTOR SpeakerTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | + KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | + KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerTopoFilter, PropertiesSpeakerTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerTopoMiniportPins), // PinCount + SpeakerTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpeakerTopologyNodes), // NodeCount + SpeakerTopologyNodes, // Nodes + SIZEOF_ARRAY(SpeakerTopoMiniportConnections), // ConnectionCount + SpeakerTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SIMPLEAUDIOSAMPLE_SPEAKERTOPTABLE_H_ diff --git a/audio/simpleaudiosample/Source/Filters/speakerwavtable.h b/audio/simpleaudiosample/Source/Filters/speakerwavtable.h new file mode 100644 index 00000000..e67a673c --- /dev/null +++ b/audio/simpleaudiosample/Source/Filters/speakerwavtable.h @@ -0,0 +1,246 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerwavtable.h + +Abstract: + + Declaration of wave miniport tables for the render endpoints. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_SPEAKERWAVTABLE_H_ +#define _SIMPLEAUDIOSAMPLE_SPEAKERWAVTABLE_H_ + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + +#define SPEAKER_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPEAKER_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_HOST_MIN_SAMPLE_RATE 48000 // Min Sample Rate +#define SPEAKER_HOST_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPEAKER_MAX_INPUT_SYSTEM_STREAMS 1 + +//============================================================================= + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpeakerHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHostPinSupportedDeviceFormats[0].DataFormat // 48KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpeakerPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpeakerHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHostPinSupportedDeviceFormats), + SpeakerHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHostPinSupportedDeviceModes) + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpeakerPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKER_HOST_MAX_CHANNELS, + SPEAKER_HOST_MIN_BITS_PER_SAMPLE, + SPEAKER_HOST_MAX_BITS_PER_SAMPLE, + SPEAKER_HOST_MIN_SAMPLE_RATE, + SPEAKER_HOST_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpeakerPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE SpeakerPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersBridge[] = +{ + &SpeakerPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER3_SINK_SYSTEM + { + SPEAKER_MAX_INPUT_SYSTEM_STREAMS, + SPEAKER_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersStream), + SpeakerPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER3_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersBridge), + SpeakerPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +// +// ---------------------------- +// | | +// Host Pin 0-->| |--> 1 KSPIN_WAVE_RENDER3_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpeakerWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER3_SINK_SYSTEM, PCFILTER_NODE, KSPIN_WAVE_RENDER3_SOURCE } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerWaveFilter, PropertiesSpeakerWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerWaveMiniportPins), // PinCount + SpeakerWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpeakerWaveMiniportConnections), // ConnectionCount + SpeakerWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SIMPLEAUDIOSAMPLE_SPEAKERWAVTABLE_H_ diff --git a/audio/simpleaudiosample/Source/Inc/Inc.vcxproj b/audio/simpleaudiosample/Source/Inc/Inc.vcxproj new file mode 100644 index 00000000..a83375e6 --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/Inc.vcxproj @@ -0,0 +1,242 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|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>{4B664BA5-057A-41B8-B365-2C99065C4DFA}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{F51739CE-5253-42B5-9191-57F28B5842C6}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup 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" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>Inc</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <TargetName>Inc</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>Inc</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <TargetName>Inc</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>Inc</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>Inc</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project> diff --git a/audio/simpleaudiosample/Source/Inc/Inc.vcxproj.Filters b/audio/simpleaudiosample/Source/Inc/Inc.vcxproj.Filters new file mode 100644 index 00000000..353662af --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/Inc.vcxproj.Filters @@ -0,0 +1,27 @@ +<?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>{54BE0AAC-642F-4D50-80D1-CA3C4BB4BB4C}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{6A00B9EC-DB03-4D51-B351-60DE9C0C51A1}</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>{887357BD-50C0-40E6-85E8-87E0F45862DB}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="*.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> diff --git a/audio/simpleaudiosample/Source/Inc/NewDelete.h b/audio/simpleaudiosample/Source/Inc/NewDelete.h new file mode 100644 index 00000000..ebeab037 --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/NewDelete.h @@ -0,0 +1,104 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + +NewDelete.h + +Abstract: + +Declaration of placement new and delete operators. +--*/ +#pragma once + +#ifdef _NEW_DELETE_OPERATORS_ + +/***************************************************************************** +* Functions +*/ + +/***************************************************************************** +* ::new() +***************************************************************************** +* New function for creating objects with a specified allocation tag and +* pool type +*/ +PVOID operator new +( + size_t iSize, + POOL_FLAGS poolFlags, + ULONG tag +); + + +/***************************************************************************** +* ::new() +***************************************************************************** +* New function for creating objects with a specified pool type. +*/ +PVOID operator new +( + size_t iSize, + POOL_FLAGS poolFlags +); + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Delete with tag function. +*/ +void __cdecl operator delete +( + PVOID pVoid, + ULONG tag +); + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Sized Delete function. +*/ +void __cdecl operator delete +( + _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid, + _In_ size_t cbSize +); + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Basic Delete function. +*/ +void __cdecl operator delete +( + PVOID pVoid +); + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Sized Array Delete function. +*/ +void __cdecl operator delete[] +( + _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid, + _In_ size_t cbSize +); + + +/***************************************************************************** +* ::delete() +***************************************************************************** +* Array Delete function. +*/ +void __cdecl operator delete[] +( + _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid +); + +#endif//_NEW_DELETE_OPERATORS_ diff --git a/audio/simpleaudiosample/Source/Inc/basetopo.h b/audio/simpleaudiosample/Source/Inc/basetopo.h new file mode 100644 index 00000000..a893aa3e --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/basetopo.h @@ -0,0 +1,96 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + basetopo.h + +Abstract: + + Declaration of topology miniport. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_BASETOPO_H_ +#define _SIMPLEAUDIOSAMPLE_BASETOPO_H_ + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMiniportTopologySimpleAudioSample +// + +class CMiniportTopologySimpleAudioSample +{ + protected: + PADAPTERCOMMON m_AdapterCommon; // Adapter common object. + PPCFILTER_DESCRIPTOR m_FilterDescriptor; // Filter descriptor. + PPORTEVENTS m_PortEvents; // Event interface. + USHORT m_DeviceMaxChannels; // Max device channels. + + public: + CMiniportTopologySimpleAudioSample( + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ); + + ~CMiniportTopologySimpleAudioSample(); + + NTSTATUS GetDescription + ( + _Out_ PPCFILTER_DESCRIPTOR * Description + ); + + NTSTATUS DataRangeIntersection + ( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength + ); + + NTSTATUS Init + ( + _In_ PUNKNOWN UnknownAdapter, + _In_ PPORTTOPOLOGY Port_ + ); + + // PropertyHandlers. + NTSTATUS PropertyHandlerGeneric + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerMuxSource + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerDevSpecific + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + VOID AddEventToEventList + ( + _In_ PKSEVENT_ENTRY EventEntry + ); + + VOID GenerateEventList + ( + _In_opt_ GUID *Set, + _In_ ULONG EventId, + _In_ BOOL PinEvent, + _In_ ULONG PinId, + _In_ BOOL NodeEvent, + _In_ ULONG NodeId + ); +}; + +#endif diff --git a/audio/simpleaudiosample/Source/Inc/common.h b/audio/simpleaudiosample/Source/Inc/common.h new file mode 100644 index 00000000..2d2db7db --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/common.h @@ -0,0 +1,477 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Common.h + +Abstract: + + CAdapterCommon class declaration. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_COMMON_H_ +#define _SIMPLEAUDIOSAMPLE_COMMON_H_ + +#define HNSTIME_PER_MILLISECOND 10000 + +//============================================================================= +// Macros +//============================================================================= + +#define UNREFERENCED_VAR(status) \ + status = status + +//------------------------------------------------------------------------- +// Description: +// +// jump to the given label. +// +// Parameters: +// +// label - [in] label to jump if condition is met +// +#define JUMP(label) \ + goto label; + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, jump to the given label. +// +// Parameters: +// +// condition - [in] Code that fits in if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_JUMP(condition, label) \ + if (condition) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, perform the given statement +// then jump to the given label. +// +// Parameters: +// +// condition - [in] Code that fits in if statement +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_ACTION_JUMP(condition, action, label) \ + if (condition) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the ntStatus is not NT_SUCCESS, perform the given statement then jump to +// the given label. +// +// Parameters: +// +// ntStatus - [in] Value to check +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_FAILED_ACTION_JUMP(ntStatus, action, label) \ + if (!NT_SUCCESS(ntStatus)) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the ntStatus passed is not NT_SUCCESS, jump to the given label. +// +// Parameters: +// +// ntStatus - [in] Value to check +// label - [in] label to jump if condition is met +// +#define IF_FAILED_JUMP(ntStatus, label) \ + if (!NT_SUCCESS(ntStatus)) \ + { \ + goto label; \ + } + +#define SAFE_RELEASE(p) {if (p) { (p)->Release(); (p) = nullptr; } } +#define SAFE_DELETE_PTR_WITH_TAG(ptr, tag) if(ptr) { ExFreePoolWithTag((ptr), tag); (ptr) = NULL; } + +// JACKDESC_RGB(r, g, b) +#define JACKDESC_RGB(r, g, b) \ + ((COLORREF)((r << 16) | (g << 8) | (b))) + +// Min/Max defines. +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +#define MINWAVERT_POOLTAG 'RWNM' +#define MINTOPORT_POOLTAG 'RTNM' +#define MINADAPTER_POOLTAG 'uAyS' + +typedef enum +{ + eSpeakerDevice = 0, + eMicArrayDevice1, + eMaxDeviceType, +} eDeviceType; + +// +// Signal processing modes and default formats structs. +// +typedef struct _MODE_AND_DEFAULT_FORMAT { + GUID Mode; + KSDATAFORMAT* DefaultFormat; +} MODE_AND_DEFAULT_FORMAT, *PMODE_AND_DEFAULT_FORMAT; + +// +// Enumeration of the various types of pins implemented in this driver. +// +typedef enum +{ + NoPin, + BridgePin, + SystemRenderPin, + SystemCapturePin, +} PINTYPE; + +// +// PIN_DEVICE_FORMATS_AND_MODES +// +// Used to specify a pin's type (e.g. system, offload, etc.), formats, and +// modes. Conceptually serves similar purpose as the PCPIN_DESCRIPTOR to +// define a pin, but is more specific to driver implementation. +// +// Arrays of these structures follow the same order as the filter's +// pin descriptor array so that KS pin IDs can serve as an index. +// +typedef struct _PIN_DEVICE_FORMATS_AND_MODES +{ + PINTYPE PinType; + + KSDATAFORMAT_WAVEFORMATEXTENSIBLE * WaveFormats; + ULONG WaveFormatsCount; + + MODE_AND_DEFAULT_FORMAT * ModeAndDefaultFormat; + ULONG ModeAndDefaultFormatCount; + +} PIN_DEVICE_FORMATS_AND_MODES, *PPIN_DEVICE_FORMATS_AND_MODES; + +// forward declaration. +typedef struct _ENDPOINT_MINIPAIR *PENDPOINT_MINIPAIR; + +// both wave & topology miniport create function prototypes have this form: +typedef HRESULT (*PFNCREATEMINIPORT)( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _In_ POOL_FLAGS PoolFlags, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +); + +//============================================================================= +// +//============================================================================= +typedef struct _SIMPLEAUDIOSAMPLE_DEVPROPERTY { + const DEVPROPKEY *PropertyKey; + DEVPROPTYPE Type; + ULONG BufferSize; + __field_bcount_opt(BufferSize) PVOID Buffer; +} SIMPLEAUDIOSAMPLE_DEVPROPERTY, PSIMPLEAUDIOSAMPLE_DEVPROPERTY; + +#define ENDPOINT_NO_FLAGS 0x00000000 +#define ENDPOINT_CELLULAR_PROVIDER1 0x00000008 +#define ENDPOINT_CELLULAR_PROVIDER2 0x00000010 + +// +// Endpoint miniport pair (wave/topology) descriptor. +// +typedef struct _ENDPOINT_MINIPAIR +{ + eDeviceType DeviceType; + + // Topology miniport. + PWSTR TopoName; // make sure this or the template name matches with SIMPLEAUDIOSAMPLE.<TopoName>.szPname in the inf's [Strings] section + PWSTR TemplateTopoName; // optional template name + PFNCREATEMINIPORT TopoCreateCallback; + PCFILTER_DESCRIPTOR* TopoDescriptor; + ULONG TopoInterfacePropertyCount; + const SIMPLEAUDIOSAMPLE_DEVPROPERTY* TopoInterfaceProperties; + + // Wave RT miniport. + PWSTR WaveName; // make sure this or the template name matches with SIMPLEAUDIOSAMPLE.<WaveName>.szPname in the inf's [Strings] section + PWSTR TemplateWaveName; // optional template name + PFNCREATEMINIPORT WaveCreateCallback; + PCFILTER_DESCRIPTOR* WaveDescriptor; + ULONG WaveInterfacePropertyCount; + const SIMPLEAUDIOSAMPLE_DEVPROPERTY* WaveInterfaceProperties; + + USHORT DeviceMaxChannels; + PIN_DEVICE_FORMATS_AND_MODES* PinDeviceFormatsAndModes; + ULONG PinDeviceFormatsAndModesCount; + + // Miniport physical connections. + PHYSICALCONNECTIONTABLE* PhysicalConnections; + ULONG PhysicalConnectionCount; + + // General endpoint flags (one of more ENDPOINT_<flag-type>, see above) + ULONG DeviceFlags; +} ENDPOINT_MINIPAIR; + +//============================================================================= +// Defines +//============================================================================= + +DEFINE_GUID(IID_IAdapterCommon, +0x7eda2950, 0xbf9f, 0x11d0, 0x87, 0x1f, 0x0, 0xa0, 0xc9, 0x11, 0xb5, 0x44); + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// IAdapterCommon +// +DECLARE_INTERFACE_(IAdapterCommon, IUnknown) +{ + STDMETHOD_(NTSTATUS, Init) + ( + THIS_ + _In_ PDEVICE_OBJECT DeviceObject + ) PURE; + + STDMETHOD_(PDEVICE_OBJECT, GetDeviceObject) + ( + THIS + ) PURE; + + STDMETHOD_(PDEVICE_OBJECT, GetPhysicalDeviceObject) + ( + THIS + ) PURE; + + STDMETHOD_(WDFDEVICE, GetWdfDevice) + ( + THIS + ) PURE; + + STDMETHOD_(VOID, SetWaveServiceGroup) + ( + THIS_ + _In_ PSERVICEGROUP ServiceGroup + ) PURE; + + STDMETHOD_(BOOL, bDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, bDevSpecificWrite) + ( + THIS_ + _In_ BOOL bDevSpecific + ); + + STDMETHOD_(INT, iDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, iDevSpecificWrite) + ( + THIS_ + _In_ INT iDevSpecific + ); + + STDMETHOD_(UINT, uiDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, uiDevSpecificWrite) + ( + THIS_ + _In_ UINT uiDevSpecific + ); + + STDMETHOD_(BOOL, MixerMuteRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerMuteWrite) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value + ); + + STDMETHOD_(ULONG, MixerMuxRead) + ( + THIS + ); + + STDMETHOD_(VOID, MixerMuxWrite) + ( + THIS_ + _In_ ULONG Index + ); + + STDMETHOD_(LONG, MixerVolumeRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerVolumeWrite) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value + ) PURE; + + STDMETHOD_(LONG, MixerPeakMeterRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerReset) + ( + THIS + ) PURE; + + STDMETHOD_(NTSTATUS, WriteEtwEvent) + ( + THIS_ + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 + ) PURE; + + STDMETHOD_(VOID, SetEtwHelper) + ( + THIS_ + PPORTCLSETWHELPER _pPortClsEtwHelper + ) PURE; + + STDMETHOD_(NTSTATUS, InstallSubdevice) + ( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_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 + ); + + STDMETHOD_(NTSTATUS, UnregisterSubdevice) + ( + THIS_ + _In_opt_ PUNKNOWN UnknownPort + ); + + STDMETHOD_(NTSTATUS, ConnectTopologies) + ( + THIS_ + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHOD_(NTSTATUS, DisconnectTopologies) + ( + THIS_ + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHOD_(NTSTATUS, InstallEndpointFilters) + ( + THIS_ + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave, + _Out_opt_ PUNKNOWN * UnknownMiniportTopology, + _Out_opt_ PUNKNOWN * UnknownMiniportWave + ); + + STDMETHOD_(NTSTATUS, RemoveEndpointFilters) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave + ); + + STDMETHOD_(NTSTATUS, GetFilters) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN *UnknownTopologyPort, + _Out_opt_ PUNKNOWN *UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN *UnknownWavePort, + _Out_opt_ PUNKNOWN *UnknownWaveMiniport + ); + + STDMETHOD_(NTSTATUS, SetIdlePowerManagement) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnable + ); + + STDMETHOD_(VOID, Cleanup)(); + +}; + +typedef IAdapterCommon *PADAPTERCOMMON; + +//============================================================================= +// Function Prototypes +//============================================================================= +NTSTATUS +NewAdapterCommon +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _In_ POOL_FLAGS PoolFlags +); + +#endif //_SIMPLEAUDIOSAMPLE_COMMON_H_ diff --git a/audio/simpleaudiosample/Source/Inc/definitions.h b/audio/simpleaudiosample/Source/Inc/definitions.h new file mode 100644 index 00000000..9fb4547a --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/definitions.h @@ -0,0 +1,198 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Definitions.h + +Abstract: + + Header file for common stuff. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_DEFINITIONS_H_ +#define _SIMPLEAUDIOSAMPLE_DEFINITIONS_H_ + +#include <portcls.h> +#include <stdunk.h> +#include <ksdebug.h> +#include <ntintsafe.h> +#include <wdf.h> +#include <wdfminiport.h> +#include <Ntstrsafe.h> +#include "NewDelete.h" + +//============================================================================= +// Defines +//============================================================================= + +// Product Id +// {836BA6D1-3FF7-4411-8BCD-469553452DCE} +#define STATIC_PID_SIMPLEAUDIOSAMPLE\ + 0x836ba6d1, 0x3ff7, 0x4411, 0x8b, 0xcd, 0x46, 0x95, 0x53, 0x45, 0x2d, 0xce +DEFINE_GUIDSTRUCT("836BA6D1-3FF7-4411-8BCD-469553452DCE", PID_SIMPLEAUDIOSAMPLE); +#define PID_SIMPLEAUDIOSAMPLE DEFINE_GUIDNAMED(PID_SIMPLEAUDIOSAMPLE) + +// Pool tag used for SIMPLEAUDIOSAMPLE allocations +#define SIMPLEAUDIOSAMPLE_POOLTAG 'SASM' + +// Debug module name +#define STR_MODULENAME "SIMPLEAUDIOSAMPLE: " + +// Debug utility macros +#define D_FUNC 4 +#define D_BLAB DEBUGLVL_BLAB +#define D_VERBOSE DEBUGLVL_VERBOSE +#define D_TERSE DEBUGLVL_TERSE +#define D_ERROR DEBUGLVL_ERROR +#define DPF _DbgPrintF +#define DPF_ENTER(x) DPF(D_FUNC, x) + +// Channel orientation +#define CHAN_LEFT 0 +#define CHAN_RIGHT 1 +#define CHAN_MASTER (-1) + +// Dma Settings. +#define DMA_BUFFER_SIZE 0x16000 + +#define KSPROPERTY_TYPE_ALL KSPROPERTY_TYPE_BASICSUPPORT | \ + KSPROPERTY_TYPE_GET | \ + KSPROPERTY_TYPE_SET + +// Specific node numbers +#define DEV_SPECIFIC_VT_BOOL 9 +#define DEV_SPECIFIC_VT_I4 10 +#define DEV_SPECIFIC_VT_UI4 11 + +#define _100NS_PER_MILLISECOND 10000 // number of 100ns units per millisecond + +// Default volume settings. +#define VOLUME_STEPPING_DELTA 0x8000 +#define VOLUME_SIGNED_MAXIMUM 0x00000000 +#define VOLUME_SIGNED_MINIMUM (-96 * 0x10000) + +// Default peak meter settings +#define PEAKMETER_STEPPING_DELTA 0x1000 +#define PEAKMETER_SIGNED_MAXIMUM LONG_MAX +#define PEAKMETER_SIGNED_MINIMUM LONG_MIN + +#define VALUE_NORMALIZE_P(v, step) \ + ((((v) + (step)/2) / (step)) * (step)) + +#define VALUE_NORMALIZE(v, step) \ + ((v) > 0 ? VALUE_NORMALIZE_P((v), (step)) : -(VALUE_NORMALIZE_P(-(v), (step)))) + +#define VALUE_NORMALIZE_IN_RANGE_EX(v, min, max, step) \ + ((v) > (max) ? (max) : \ + (v) < (min) ? (min) : \ + VALUE_NORMALIZE((v), (step))) + +// to normalize volume values. +#define VOLUME_NORMALIZE_IN_RANGE(v) \ + VALUE_NORMALIZE_IN_RANGE_EX((v), VOLUME_SIGNED_MINIMUM, VOLUME_SIGNED_MAXIMUM, VOLUME_STEPPING_DELTA) + +// to normalize sample peak meter. +#define PEAKMETER_NORMALIZE_IN_RANGE(v) \ + VALUE_NORMALIZE_IN_RANGE_EX((v), PEAKMETER_SIGNED_MINIMUM, PEAKMETER_SIGNED_MAXIMUM, PEAKMETER_STEPPING_DELTA) + +#define ALL_CHANNELS_ID UINT32_MAX + +// Macros to assist with pin instance counting +#define VERIFY_PIN_INSTANCE_RESOURCES_AVAILABLE(status, allocated, max) \ + status = (allocated < max) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES + +#define ALLOCATE_PIN_INSTANCE_RESOURCES(allocated) \ + allocated++ + +#define FREE_PIN_INSTANCE_RESOURCES(allocated) \ + allocated-- + +// Define the value data type for supported sound detector patterns. Only +// one pattern type is supported in this sample. +typedef struct { + KSMULTIPLE_ITEM MultipleItem; + GUID PatternType[1]; +} CONTOSO_SUPPORTEDPATTERNSVALUE; + +//============================================================================= +// Typedefs +//============================================================================= + +// Flags to identify stream processing mode +typedef enum { + CONNECTIONTYPE_TOPOLOGY_OUTPUT = 0, + CONNECTIONTYPE_WAVE_OUTPUT = 1 +} CONNECTIONTYPE; + +// Connection table for registering topology/wave bridge connection +typedef struct _PHYSICALCONNECTIONTABLE +{ + ULONG ulTopology; + ULONG ulWave; + CONNECTIONTYPE eType; +} PHYSICALCONNECTIONTABLE, *PPHYSICALCONNECTIONTABLE; + +// +// This is the structure of the portclass FDO device extension Nt has created +// for us. We keep the adapter common object here. +// +struct IAdapterCommon; +typedef struct _PortClassDeviceContext // 32 64 Byte offsets for 32 and 64 bit architectures +{ + ULONG_PTR m_pulReserved1[2]; // 0-7 0-15 First two pointers are reserved. + PDEVICE_OBJECT m_DoNotUsePhysicalDeviceObject; // 8-11 16-23 Reserved pointer to our Physical Device Object (PDO). + PVOID m_pvReserved2; // 12-15 24-31 Reserved pointer to our Start Device function. + PVOID m_pvReserved3; // 16-19 32-39 "Out Memory" according to DDK. + IAdapterCommon* m_pCommon; // 20-23 40-47 Pointer to our adapter common object. + PVOID m_pvUnused1; // 24-27 48-55 Unused space. + PVOID m_pvUnused2; // 28-31 56-63 Unused space. + + // Anything after above line should not be used. + // This actually goes on for (64*sizeof(ULONG_PTR)) but it is all opaque. +} PortClassDeviceContext; + +// +// Major/MinorTarget to object casting. +// +#define MajorTarget_to_Obj(ptr) \ + reinterpret_cast<CMiniportWaveRT*>(ptr) + +#define MinorTarget_to_Obj(ptr) \ + static_cast<CMiniportWaveRTStream*>(reinterpret_cast<PMINIPORTWAVERTSTREAM>(ptr)) + +// +// Global settings. +// +extern DWORD g_DoNotCreateDataFiles; +extern DWORD g_DisableBthScoBypass; +extern UNICODE_STRING g_RegistryPath; + +//============================================================================= +// Function prototypes +//============================================================================= + +// Generic topology handler +NTSTATUS PropertyHandler_Topology +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// Default WaveFilter automation table. +// Handles the GeneralComponentId request. +NTSTATUS PropertyHandler_WaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +NTSTATUS PropertyHandler_GenericPin +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// common.h uses some of the above definitions. +#include "common.h" +#include "kshelper.h" + +#endif // _SIMPLEAUDIOSAMPLE_DEFINITIONS_H_ diff --git a/audio/simpleaudiosample/Source/Inc/endpoints.h b/audio/simpleaudiosample/Source/Inc/endpoints.h new file mode 100644 index 00000000..44a858a0 --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/endpoints.h @@ -0,0 +1,133 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + endpoints.h + +Abstract: + + Node and Pin numbers and other common definitions for simple audio sample. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_ENDPOINTS_H_ +#define _SIMPLEAUDIOSAMPLE_ENDPOINTS_H_ + +// Name Guid +// {0104947F-82AE-4291-A6F3-5E2DE1AD7DC2} +#define STATIC_NAME_SIMPLE_AUDIO_SAMPLE\ + 0x104947f, 0x82ae, 0x4291, 0xa6, 0xf3, 0x5e, 0x2d, 0xe1, 0xad, 0x7d, 0xc2 +DEFINE_GUIDSTRUCT("0104947F-82AE-4291-A6F3-5E2DE1AD7DC2", NAME_SIMPLE_AUDIO_SAMPLE); +#define NAME_SIMPLE_AUDIO_SAMPLE DEFINE_GUIDNAMED(NAME_SIMPLE_AUDIO_SAMPLE) + +//---------------------------------------------------- +// New defines for the render endpoints. +//---------------------------------------------------- + +// Default pin instances. +#define MAX_INPUT_SYSTEM_STREAMS 1 + +// Wave pins - no mix, no offload +enum +{ + KSPIN_WAVE_RENDER3_SINK_SYSTEM = 0, + KSPIN_WAVE_RENDER3_SOURCE +}; + +// Wave pins - offloading is NOT supported. +enum +{ + KSPIN_WAVE_RENDER2_SINK_SYSTEM = 0, + KSPIN_WAVE_RENDER2_SINK_LOOPBACK, + KSPIN_WAVE_RENDER2_SOURCE +}; + +// Wave Topology nodes - offloading is NOT supported. +enum +{ + KSNODE_WAVE_SUM = 0, + KSNODE_WAVE_VOLUME, + KSNODE_WAVE_MUTE, + KSNODE_WAVE_PEAKMETER +}; + +// Topology pins. +enum +{ + KSPIN_TOPO_WAVEOUT_SOURCE = 0, + KSPIN_TOPO_LINEOUT_DEST, +}; + +// Topology nodes. +enum +{ + KSNODE_TOPO_WAVEOUT_VOLUME = 0, + KSNODE_TOPO_WAVEOUT_MUTE, + KSNODE_TOPO_WAVEOUT_PEAKMETER +}; + +//---------------------------------------------------- +// New defines for the capture endpoints. +//---------------------------------------------------- + +// Default pin instances. +#define MAX_INPUT_STREAMS 1 // Number of capture streams. + +// Wave pins +enum +{ + KSPIN_WAVE_BRIDGE = 0, + KSPIN_WAVEIN_HOST, +}; + +// Wave Topology nodes. +enum +{ + KSNODE_WAVE_ADC = 0 +}; + +// Wave Topology nodes. +enum +{ + KSNODE_WAVE_DAC = 0 +}; + +// topology pins. +enum +{ + KSPIN_TOPO_MIC_ELEMENTS, + KSPIN_TOPO_BRIDGE +}; + +// topology nodes. +enum +{ + KSNODE_TOPO_VOLUME, + KSNODE_TOPO_MUTE, + KSNODE_TOPO_PEAKMETER +}; + +// data format attribute range definitions. +static +KSATTRIBUTE PinDataRangeSignalProcessingModeAttribute = +{ + sizeof(KSATTRIBUTE), + 0, + STATICGUIDOF(KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE), +}; + +static +PKSATTRIBUTE PinDataRangeAttributes[] = +{ + &PinDataRangeSignalProcessingModeAttribute, +}; + +static +KSATTRIBUTE_LIST PinDataRangeAttributeList = +{ + ARRAYSIZE(PinDataRangeAttributes), + PinDataRangeAttributes, +}; + +#endif // _SIMPLEAUDIOSAMPLE_ENDPOINTS_H_ diff --git a/audio/simpleaudiosample/Source/Inc/kshelper.h b/audio/simpleaudiosample/Source/Inc/kshelper.h new file mode 100644 index 00000000..023f773a --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/kshelper.h @@ -0,0 +1,159 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + kshelper.h + +Abstract: + + Helper functions for simple audio sample +--*/ +#ifndef _SIMPLEAUDIOSAMPLE_KSHELPER_H_ +#define _SIMPLEAUDIOSAMPLE_KSHELPER_H_ + +#include <portcls.h> +#include <ksdebug.h> + +PWAVEFORMATEX +GetWaveFormatEx +( + _In_ PKSDATAFORMAT pDataFormat +); + +NTSTATUS +ValidatePropertyParams +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cbValueSize, + _In_ ULONG cbInstanceSize = 0 +); + +NTSTATUS +PropertyHandler_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG Flags, + _In_ DWORD PropTypeSetId +); + +NTSTATUS +PropertyHandler_BasicSupportVolume +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_BasicSupportMute +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_BasicSupportPeakMeter2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_CpuResources +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +NTSTATUS +PropertyHandler_Volume +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_Mute +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_PeakMeter2 +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +//============================================================================= +// Property helpers +//============================================================================= + +NTSTATUS +SimpleAudioSamplePropertyDispatch +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// Use this structure to define property items with extra data allowing easier +// definition of separate get, set, and support handlers dispatched through +// SimpleAudioSamplePropertyDispatch. +typedef struct +{ + PCPROPERTY_ITEM PropertyItem; // Standard PCPROPERTY_ITEM + ULONG MinProperty; // Minimum size of the property instance data + ULONG MinData; // Minimum size of the property value + PCPFNPROPERTY_HANDLER GetHandler; // Property get handler (NULL if GET not supported) + PCPFNPROPERTY_HANDLER SetHandler; // Property set handler (NULL if SET not supported) + PCPFNPROPERTY_HANDLER SupportHandler; // Property support handler (NULL for common handler) + VOID *ContextData; // Optional context information for the handler, NULL for unused + ULONG ContextDataSize; // Size of the data held at ContextData, 0 for unused +} SIMPLEAUDIOSAMPLE_PROPERTY_ITEM; + +// The following macros facilitate adding property handlers to a class, allowing +// easier declaration and definition of a "thunk" routine that directly handles +// the property request and calls into a class instance method. Note that as +// written, the thunk routine assumes PAGED_CODE. +#define DECLARE_CLASSPROPERTYHANDLER(theClass, theMethod) \ +NTSTATUS theClass##_##theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +); + +#define DECLARE_PROPERTYHANDLER(theMethod) \ +NTSTATUS theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +); + +#define DEFINE_CLASSPROPERTYHANDLER(theClass, theMethod) \ +NTSTATUS theClass##_##theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +) \ +{ \ + NTSTATUS status; \ + \ + PAGED_CODE(); \ + \ + theClass* p = reinterpret_cast<theClass*>(PropertyRequest->MajorTarget); \ + \ + p->AddRef(); \ + status = p->theMethod(PropertyRequest); \ + p->Release(); \ + \ + return status; \ +} \ +NTSTATUS theClass::theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +) + + + + +#endif // _SIMPLEAUDIOSAMPLE_KSHELPER_H_ diff --git a/audio/simpleaudiosample/Source/Inc/mintopo.h b/audio/simpleaudiosample/Source/Inc/mintopo.h new file mode 100644 index 00000000..5e7fe466 --- /dev/null +++ b/audio/simpleaudiosample/Source/Inc/mintopo.h @@ -0,0 +1,81 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minitopo.h + +Abstract: + + Declaration of topology miniport. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_MINTOPO_H_ +#define _SIMPLEAUDIOSAMPLE_MINTOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMiniportTopology +// + +class CMiniportTopology : + public CMiniportTopologySimpleAudioSample, + public IMiniportTopology, + public CUnknown +{ + private: + eDeviceType m_DeviceType; + + union { + PVOID m_DeviceContext; + }; + +public: + DECLARE_STD_UNKNOWN(); + CMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels, + _In_ eDeviceType DeviceType, + _In_opt_ PVOID DeviceContext + ) + : CUnknown(UnknownOuter), + CMiniportTopologySimpleAudioSample(FilterDesc, DeviceMaxChannels), + m_DeviceType(DeviceType), + m_DeviceContext(DeviceContext) + { + } + + ~CMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *JackDescriptions + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *JackDescriptions, + _In_ DWORD JackCapabilities + ); + + PVOID GetDeviceContext() { return m_DeviceContext; } +}; + +typedef CMiniportTopology *PCMiniportTopology; + +#endif // _SIMPLEAUDIOSAMPLE_MINTOPO_H_ 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_ + diff --git a/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp new file mode 100644 index 00000000..f50e81e1 --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp @@ -0,0 +1,285 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + ToneGenerator + +Abstract: + + Implementation of Simple Audio Sample sine wave generator + +--*/ +#include "definitions.h" +#include "ToneGenerator.h" + +const double TWO_PI = M_PI * 2; + +extern DWORD g_DisableToneGenerator; + +// +// Double to long conversion. +// +long ConvertToLong(double Value) +{ + return (long)(Value * _I32_MAX); +}; + +// +// Double to short conversion. +// +short ConvertToShort(double Value) +{ + return (short)(Value * _I16_MAX); +}; + +// +// Double to char conversion. +// +unsigned char ConvertToUChar(double Value) +{ + const double F_127_5 = 127.5; + return (unsigned char)(Value * F_127_5 + F_127_5); +}; + +// +// Ctor: basic init. +// +ToneGenerator::ToneGenerator() +: m_Frequency(0), + m_ChannelCount(0), + m_BitsPerSample(0), + m_SamplesPerSecond(0), + m_Mute(false), + m_PartialFrame(NULL), + m_PartialFrameBytes(0), + m_FrameSize(0) +{ + // Theta (double) and SampleIncrement (double) are init in the Init() method + // after saving the floating point state. +} + +// +// Dtor: free resources. +// +ToneGenerator::~ToneGenerator() +{ + if (m_PartialFrame) + { + ExFreePoolWithTag(m_PartialFrame, SIMPLEAUDIOSAMPLE_POOLTAG); + m_PartialFrame = NULL; + m_PartialFrameBytes = 0; + } +} + +// +// Init a new frame. +// Note: caller will save and restore the floatingpoint state. +// +#pragma warning(push) +// Caller wraps this routine between KeSaveFloatingPointState/KeRestoreFloatingPointState calls. +#pragma warning(disable: 28110) + +VOID ToneGenerator::InitNewFrame +( + _Out_writes_bytes_(FrameSize) BYTE* Frame, + _In_ DWORD FrameSize +) +{ + double sinValue = m_ToneDCOffset + m_ToneAmplitude * sin( m_Theta ); + + if (FrameSize != (DWORD)m_ChannelCount * m_BitsPerSample/8) + { + ASSERT(FALSE); + RtlZeroMemory(Frame, FrameSize); + return; + } + + for(ULONG i = 0; i < m_ChannelCount; ++i) + { + if (m_BitsPerSample == 8) + { + unsigned char *dataBuffer = reinterpret_cast<unsigned char *>(Frame); + dataBuffer[i] = ConvertToUChar(sinValue); + } + else if (m_BitsPerSample == 16) + { + short *dataBuffer = reinterpret_cast<short *>(Frame); + dataBuffer[i] = ConvertToShort(sinValue); + } + else if (m_BitsPerSample == 24) + { + BYTE *dataBuffer = Frame; + long val = ConvertToLong(sinValue); + val = val >> 8; + RtlCopyMemory(dataBuffer, &val, 3); + } + else if (m_BitsPerSample == 32) + { + long *dataBuffer = reinterpret_cast<long *>(Frame); + dataBuffer[i] = ConvertToLong(sinValue); + } + } + + m_Theta += m_SampleIncrement; + if (m_Theta >= TWO_PI) + { + m_Theta -= TWO_PI; + } +} +#pragma warning(pop) + +// +// GenerateSamples() +// +// Generate a sine wave that fits into the specified buffer. +// +// Buffer - Buffer to hold the samples +// BufferLength - Length of the buffer. +// +// +void ToneGenerator::GenerateSine +( + _Out_writes_bytes_(BufferLength) BYTE *Buffer, + _In_ size_t BufferLength +) +{ + NTSTATUS status; + KFLOATING_SAVE saveData; + BYTE * buffer; + size_t length; + size_t copyBytes; + + // if muted, or tone generator disabled via registry, + // we deliver silence. + if (m_Mute || g_DisableToneGenerator) + { + goto ZeroBuffer; + } + + status = KeSaveFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + goto ZeroBuffer; + } + + buffer = Buffer; + length = BufferLength; + + // + // Check if we have any residual frame bytes from the last time. + // + if (m_PartialFrameBytes) + { + ASSERT(m_FrameSize > m_PartialFrameBytes); + DWORD offset = m_FrameSize - m_PartialFrameBytes; + copyBytes = MIN(m_PartialFrameBytes, length); + RtlCopyMemory(buffer, m_PartialFrame + offset, copyBytes); + RtlZeroMemory(m_PartialFrame + offset, copyBytes); + length -= copyBytes; + buffer += copyBytes; + m_PartialFrameBytes = 0; + } + + IF_TRUE_JUMP(length == 0, Done); + + // + // Copy all the aligned frames. + // + + size_t frames = length/m_FrameSize; + + for (size_t i = 0; i < frames; ++i) + { + InitNewFrame(buffer, m_FrameSize); + buffer += m_FrameSize; + length -= m_FrameSize; + } + + IF_TRUE_JUMP(length == 0, Done); + + // + // Copy any partial frame at the end. + // + ASSERT(m_FrameSize > length); + InitNewFrame(m_PartialFrame, m_FrameSize); + RtlCopyMemory(buffer, m_PartialFrame, length); + RtlZeroMemory(m_PartialFrame, length); + m_PartialFrameBytes = m_FrameSize - (DWORD)length; + +Done: + KeRestoreFloatingPointState(&saveData); + return; + +ZeroBuffer: + RtlZeroMemory(Buffer, BufferLength); + return; +} + +NTSTATUS ToneGenerator::Init +( + _In_ DWORD ToneFrequency, + _In_ double ToneAmplitude, + _In_ double ToneDCOffset, + _In_ double ToneInitialPhase, + _In_ PWAVEFORMATEXTENSIBLE WfExt +) +{ + NTSTATUS status = STATUS_SUCCESS; + KFLOATING_SAVE saveData; + + // + // This sample supports PCM formats only. + // + if ((WfExt->Format.wFormatTag != WAVE_FORMAT_PCM && + !(WfExt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && + IsEqualGUIDAligned(WfExt->SubFormat, KSDATAFORMAT_SUBTYPE_PCM)))) + { + status = STATUS_NOT_SUPPORTED; + } + IF_FAILED_JUMP(status, Done); + + // + // Save floating state (just in case). + // + status = KeSaveFloatingPointState(&saveData); + IF_FAILED_JUMP(status, Done); + + // + // Basic init. + // + m_Theta = ToneInitialPhase; + m_Frequency = ToneFrequency; + m_ToneAmplitude = ToneAmplitude; + m_ToneDCOffset = ToneDCOffset; + + m_ChannelCount = WfExt->Format.nChannels; // # channels. + m_BitsPerSample = WfExt->Format.wBitsPerSample; // bits per sample. + m_SamplesPerSecond = WfExt->Format.nSamplesPerSec; // samples per sec. + m_Mute = false; + m_SampleIncrement = (m_Frequency * TWO_PI) / (double)m_SamplesPerSecond; + m_FrameSize = (DWORD)m_ChannelCount * m_BitsPerSample/8; + ASSERT(m_FrameSize == WfExt->Format.nBlockAlign); + + // + // Restore floating state. + // + KeRestoreFloatingPointState(&saveData); + + // + // Allocate a buffer to hold a partial frame. + // + m_PartialFrame = (BYTE*)ExAllocatePool2( + POOL_FLAG_NON_PAGED, + m_FrameSize, + SIMPLEAUDIOSAMPLE_POOLTAG); + + IF_TRUE_ACTION_JUMP(m_PartialFrame == NULL, status = STATUS_INSUFFICIENT_RESOURCES, Done); + + status = STATUS_SUCCESS; + +Done: + return status; +} + diff --git a/audio/simpleaudiosample/Source/Utilities/ToneGenerator.h b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.h new file mode 100644 index 00000000..85b1229a --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.h @@ -0,0 +1,74 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + ToneGenerator.h + +Abstract: + + Declaration of Simple Audio Sample sine wave generator. +--*/ +#ifndef _SIMPLEAUDIOSAMPLE_TONEGENERATOR_H +#define _SIMPLEAUDIOSAMPLE_TONEGENERATOR_H + +#define _USE_MATH_DEFINES +#include <math.h> +#include <limits.h> + +class ToneGenerator +{ +public: + DWORD m_Frequency; + WORD m_ChannelCount; + WORD m_BitsPerSample; + DWORD m_SamplesPerSecond; + double m_Theta; + double m_SampleIncrement; + bool m_Mute; + BYTE* m_PartialFrame; + DWORD m_PartialFrameBytes; + DWORD m_FrameSize; + double m_ToneAmplitude; + double m_ToneDCOffset; + +public: + ToneGenerator(); + ~ToneGenerator(); + + NTSTATUS + Init + ( + _In_ DWORD ToneFrequency, + _In_ double ToneAmplitude, + _In_ double ToneDCOffset, + _In_ double ToneInitialPhase, + _In_ PWAVEFORMATEXTENSIBLE WfExt + ); + + VOID + GenerateSine + ( + _Out_writes_bytes_(BufferLength) BYTE *Buffer, + _In_ size_t BufferLength + ); + + VOID + SetMute + ( + _In_ bool Value + ) + { + m_Mute = Value; + } + +private: + VOID InitNewFrame + ( + _Out_writes_bytes_(FrameSize) BYTE* Frame, + _In_ DWORD FrameSize + ); +}; + +#endif // _SIMPLEAUDIOSAMPLE_TONEGENERATOR_H diff --git a/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj b/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj new file mode 100644 index 00000000..9af7c493 --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj @@ -0,0 +1,266 @@ +<?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>{33E61864-6F2C-4F9F-BE70-8F8985A4F283}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{F51739CE-5253-42B5-9191-57F28B5842C6}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup 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" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>Utilities</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <TargetName>Utilities</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>Utilities</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <TargetName>Utilities</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>Utilities</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>Utilities</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="hw.cpp" /> + <ClCompile Include="kshelper.cpp" /> + <ClCompile Include="savedata.cpp" /> + <ClCompile Include="tonegenerator.cpp" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project> diff --git a/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj.Filters b/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj.Filters new file mode 100644 index 00000000..7a4ebde2 --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj.Filters @@ -0,0 +1,31 @@ +<?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>{CDBBEDA3-98E9-4C7A-BDB1-25EC47D0B087}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{FA3DF57E-BA19-4783-831B-CF9D4D594F70}</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>{CB830E4F-01DA-4D35-9789-E7DB0D02A79C}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{49F92783-3B39-4D0C-A153-8C346F754749}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="*.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> diff --git a/audio/simpleaudiosample/Source/Utilities/hw.cpp b/audio/simpleaudiosample/Source/Utilities/hw.cpp new file mode 100644 index 00000000..133bcc99 --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/hw.cpp @@ -0,0 +1,446 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hw.cpp + +Abstract: + + Implementation of Simple Audio Sample HW class. + Simple Audio Sample HW has an array for storing mixer and volume settings + for the topology. +--*/ +#include "definitions.h" +#include "hw.h" + +//============================================================================= +// CSimpleAudioSampleHW +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +CSimpleAudioSampleHW::CSimpleAudioSampleHW() +: m_ulMux(0), + m_bDevSpecific(FALSE), + m_iDevSpecific(0), + m_uiDevSpecific(0) +/*++ + +Routine Description: + + Constructor for SimpleAudioSampleHW. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + MixerReset(); +} // SimpleAudioSampleHW +#pragma code_seg() + + +//============================================================================= +BOOL +CSimpleAudioSampleHW::bGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + True or False (in this example). + +--*/ +{ + return m_bDevSpecific; +} // bGetDevSpecific + +//============================================================================= +void +CSimpleAudioSampleHW::bSetDevSpecific +( + _In_ BOOL bDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + fDevSpecific - true or false for this example. + +Return Value: + + void + +--*/ +{ + m_bDevSpecific = bDevSpecific; +} // bSetDevSpecific + +//============================================================================= +INT +CSimpleAudioSampleHW::iGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + int (in this example). + +--*/ +{ + return m_iDevSpecific; +} // iGetDevSpecific + +//============================================================================= +void +CSimpleAudioSampleHW::iSetDevSpecific +( + _In_ INT iDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + fDevSpecific - true or false for this example. + +Return Value: + + void + +--*/ +{ + m_iDevSpecific = iDevSpecific; +} // iSetDevSpecific + +//============================================================================= +UINT +CSimpleAudioSampleHW::uiGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + UINT (in this example). + +--*/ +{ + return m_uiDevSpecific; +} // uiGetDevSpecific + +//============================================================================= +void +CSimpleAudioSampleHW::uiSetDevSpecific +( + _In_ UINT uiDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + uiDevSpecific - int for this example. + +Return Value: + + void + +--*/ +{ + m_uiDevSpecific = uiDevSpecific; +} // uiSetDevSpecific + + +//============================================================================= +BOOL +CSimpleAudioSampleHW::GetMixerMute +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) mute levels for Simple Audio Sample + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + mute setting + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_MuteControls[ulNode]; + } + + return 0; +} // GetMixerMute + +//============================================================================= +ULONG +CSimpleAudioSampleHW::GetMixerMux() +/*++ + +Routine Description: + + Return the current mux selection + +Arguments: + +Return Value: + + ULONG + +--*/ +{ + return m_ulMux; +} // GetMixerMux + +//============================================================================= +LONG +CSimpleAudioSampleHW::GetMixerVolume +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) volume for Simple Audio Sample. + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + LONG - volume level + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_VolumeControls[ulNode]; + } + + return 0; +} // GetMixerVolume + +//============================================================================= +LONG +CSimpleAudioSampleHW::GetMixerPeakMeter +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) peak meter for Simple Audio Sample. + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + LONG - sample peak meter level + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_PeakMeterControls[ulNode]; + } + + return 0; +} // GetMixerVolume + +//============================================================================= +#pragma code_seg("PAGE") +void +CSimpleAudioSampleHW::MixerReset() +/*++ + +Routine Description: + + Resets the mixer registers. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + RtlFillMemory(m_VolumeControls, sizeof(LONG) * MAX_TOPOLOGY_NODES, 0xFF); + // Endpoints are not muted by default. + RtlZeroMemory(m_MuteControls, sizeof(BOOL) * MAX_TOPOLOGY_NODES); + + for (ULONG i=0; i<MAX_TOPOLOGY_NODES; ++i) + { + m_PeakMeterControls[i] = PEAKMETER_SIGNED_MAXIMUM/2; + } + + // BUGBUG change this depending on the topology + m_ulMux = 2; +} // MixerReset +#pragma code_seg() + +//============================================================================= +void +CSimpleAudioSampleHW::SetMixerMute +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel, + _In_ BOOL fMute +) +/*++ + +Routine Description: + + Sets the HW (!) mute levels for Simple Audio Sample + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we setting? + + fMute - mute flag + +Return Value: + + void + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + m_MuteControls[ulNode] = fMute; + } +} // SetMixerMute + +//============================================================================= +void +CSimpleAudioSampleHW::SetMixerMux +( + _In_ ULONG ulNode +) +/*++ + +Routine Description: + + Sets the HW (!) mux selection + +Arguments: + + ulNode - topology node id + +Return Value: + + void + +--*/ +{ + m_ulMux = ulNode; +} // SetMixMux + +//============================================================================= +void +CSimpleAudioSampleHW::SetMixerVolume +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel, + _In_ LONG lVolume +) +/*++ + +Routine Description: + + Sets the HW (!) volume for Simple Audio Sample. + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we setting? + + lVolume - volume level + +Return Value: + + void + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + m_VolumeControls[ulNode] = lVolume; + } +} // SetMixerVolume diff --git a/audio/simpleaudiosample/Source/Utilities/hw.h b/audio/simpleaudiosample/Source/Utilities/hw.h new file mode 100644 index 00000000..3360c24a --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/hw.h @@ -0,0 +1,105 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hw.h + +Abstract: + + Declaration of Simple Audio Sample HW class. + Simple Audio Sample HW has an array for storing mixer and volume settings + for the topology. +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_HW_H_ +#define _SIMPLEAUDIOSAMPLE_HW_H_ + +//============================================================================= +// Defines +//============================================================================= +// BUGBUG we should dynamically allocate this... +#define MAX_TOPOLOGY_NODES 20 + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CSimpleAudioSampleHW +// This class represents virtual Simple Audio Sample HW. An array representing volume +// registers and mute registers. + +class CSimpleAudioSampleHW +{ +public: +protected: + BOOL m_MuteControls[MAX_TOPOLOGY_NODES]; + LONG m_VolumeControls[MAX_TOPOLOGY_NODES]; + LONG m_PeakMeterControls[MAX_TOPOLOGY_NODES]; + ULONG m_ulMux; // Mux selection + BOOL m_bDevSpecific; + INT m_iDevSpecific; + UINT m_uiDevSpecific; + +private: + +public: + CSimpleAudioSampleHW(); + + void MixerReset(); + BOOL bGetDevSpecific(); + void bSetDevSpecific + ( + _In_ BOOL bDevSpecific + ); + INT iGetDevSpecific(); + void iSetDevSpecific + ( + _In_ INT iDevSpecific + ); + UINT uiGetDevSpecific(); + void uiSetDevSpecific + ( + _In_ UINT uiDevSpecific + ); + BOOL GetMixerMute + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel + ); + void SetMixerMute + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel, + _In_ BOOL fMute + ); + ULONG GetMixerMux(); + void SetMixerMux + ( + _In_ ULONG ulNode + ); + LONG GetMixerVolume + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel + ); + void SetMixerVolume + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel, + _In_ LONG lVolume + ); + + LONG GetMixerPeakMeter + ( + _In_ ULONG ulNode, + _In_ ULONG ulChannel + ); + +protected: +private: +}; +typedef CSimpleAudioSampleHW *PCSimpleAudioSampleHW; + +#endif // _SIMPLEAUDIOSAMPLE_HW_H_ diff --git a/audio/simpleaudiosample/Source/Utilities/kshelper.cpp b/audio/simpleaudiosample/Source/Utilities/kshelper.cpp new file mode 100644 index 00000000..e71c6021 --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/kshelper.cpp @@ -0,0 +1,958 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + kshelper.cpp + +Abstract: + + Helper functions for simple audio sample +--*/ + +#include "definitions.h" + +//4127: conditional expression is constant +#pragma warning (disable : 4127) + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +PWAVEFORMATEX +GetWaveFormatEx +( + _In_ PKSDATAFORMAT pDataFormat +) +/*++ + +Routine Description: + + Returns the waveformatex for known formats. + +Arguments: + + pDataFormat - data format. + +Return Value: + + waveformatex in DataFormat. + NULL for unknown data formats. + +--*/ +{ + PAGED_CODE(); + + PWAVEFORMATEX pWfx = NULL; + + // If this is a known dataformat extract the waveformat info. + // + if + ( + pDataFormat && + ( IsEqualGUIDAligned(pDataFormat->MajorFormat, + KSDATAFORMAT_TYPE_AUDIO) && + ( IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) || + IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND) ) ) + ) + { + pWfx = PWAVEFORMATEX(pDataFormat + 1); + + if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND)) + { + PKSDSOUND_BUFFERDESC pwfxds; + + pwfxds = PKSDSOUND_BUFFERDESC(pDataFormat + 1); + pWfx = &pwfxds->WaveFormatEx; + } + } + + return pWfx; +} // GetWaveFormatEx + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +ValidatePropertyParams +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cbValueSize, + _In_ ULONG cbInstanceSize /* = 0 */ +) +/*++ + +Routine Description: + + Validates property parameters. + +Arguments: + + PropertyRequest - + cbValueSize - + cbInstanceSize - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + + if (PropertyRequest && cbValueSize) + { + // If the caller is asking for ValueSize. + // + if (0 == PropertyRequest->ValueSize) + { + PropertyRequest->ValueSize = cbValueSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + // If the caller passed an invalid ValueSize. + // + else if (PropertyRequest->ValueSize < cbValueSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else if (PropertyRequest->InstanceSize < cbInstanceSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + // If all parameters are OK. + // + else if (PropertyRequest->ValueSize >= cbValueSize) + { + if (PropertyRequest->Value) + { + ntStatus = STATUS_SUCCESS; + // + // Caller should set ValueSize, if the property + // call is successful. + // + } + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + // Clear the ValueSize if unsuccessful. + // + if (PropertyRequest && + STATUS_SUCCESS != ntStatus && + STATUS_BUFFER_OVERFLOW != ntStatus) + { + PropertyRequest->ValueSize = 0; + } + + return ntStatus; +} // ValidatePropertyParams + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +SimpleAudioSamplePropertyDispatch +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + Handles and dispatches a SIMPLEAUDIOSAMPLE_PROPERTY_ITEM. + + Use this as the property handler only if the property item is a + SIMPLEAUDIOSAMPLE_PROPERTY_ITEM. +--*/ +{ + PAGED_CODE(); + + SIMPLEAUDIOSAMPLE_PROPERTY_ITEM* item = (SIMPLEAUDIOSAMPLE_PROPERTY_ITEM*)PropertyRequest->PropertyItem; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + if (item->SupportHandler != nullptr) + { + return item->SupportHandler(PropertyRequest); + } + else + { + return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL); + } + } + + // Verify instance data size + if (PropertyRequest->InstanceSize < item->MinProperty) + { + return STATUS_INVALID_PARAMETER; + } + + ULONG cbMinSize = item->MinData; + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + PropertyRequest->ValueSize = 0; + return STATUS_BUFFER_TOO_SMALL; + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + if (item->GetHandler != nullptr) + { + return item->GetHandler(PropertyRequest); + } + else + { + return STATUS_NOT_SUPPORTED; + } + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (item->SetHandler != nullptr) + { + return item->SetHandler(PropertyRequest); + } + else + { + return STATUS_NOT_SUPPORTED; + } + } + + return STATUS_INVALID_DEVICE_REQUEST; +} + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG Flags, + _In_ DWORD PropTypeSetId +) +/*++ + +Routine Description: + + Default basic support handler. Basic processing depends on the size of data. + For ULONG it only returns Flags. For KSPROPERTY_DESCRIPTION, the structure + is filled. + +Arguments: + + PropertyRequest - + + Flags - Support flags. + + PropTypeSetId - PropTypeSetId + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Flags & KSPROPERTY_TYPE_BASICSUPPORT); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = Flags; + PropDesc->DescriptionSize = sizeof(KSPROPERTY_DESCRIPTION); + if (VT_ILLEGAL != PropTypeSetId) + { + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = PropTypeSetId; + } + else + { + PropDesc->PropTypeSet.Set = GUID_NULL; + PropDesc->PropTypeSet.Id = 0; + } + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 0; + PropDesc->Reserved = 0; + + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + // + *(PULONG(PropertyRequest->Value)) = Flags; + + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandler_BasicSupport + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportVolume +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for Volume nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_I4; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; i<MaxChannels; ++i) + { + Range[i].Bounds.SignedMaximum = VOLUME_SIGNED_MAXIMUM; // 0 dB + Range[i].Bounds.SignedMinimum = VOLUME_SIGNED_MINIMUM; // -96 dB + Range[i].SteppingDelta = VOLUME_STEPPING_DELTA; // .5 dB + Range[i].Reserved = 0; + } + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportMute +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for Mute nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_BOOL; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; i<MaxChannels; ++i) + { + Range[i].Bounds.SignedMaximum = 1; // true + Range[i].Bounds.SignedMinimum = 0; // false + Range[i].SteppingDelta = 1; // false <- -> true + Range[i].Reserved = 0; + } + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportPeakMeter2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for peak meter nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_I4; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; i<MaxChannels; ++i) + { + Range[i].Bounds.SignedMaximum = PEAKMETER_SIGNED_MAXIMUM; + Range[i].Bounds.SignedMinimum = PEAKMETER_SIGNED_MINIMUM; + Range[i].SteppingDelta = PEAKMETER_STEPPING_DELTA; + Range[i].Reserved = 0; + } + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_CpuResources +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Processes KSPROPERTY_AUDIO_CPURESOURCES + +Arguments: + + PropertyRequest - property request structure. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(ULONG)); + if (NT_SUCCESS(ntStatus)) + { + *(PULONG(PropertyRequest->Value)) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU; + PropertyRequest->ValueSize = sizeof(ULONG); + } + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + VT_UI4 + ); + } + + return ntStatus; +} // PropertyHandlerCpuResources + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_Volume +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_VOLUMELEVEL + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG ulChannel; + PLONG plVolume; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportVolume( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // volume value is a LONG + sizeof(ULONG) // instance is the channel number + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + plVolume = PLONG (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *plVolume = + AdapterCommon->MixerVolumeRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + ); + PropertyRequest->ValueSize = sizeof(ULONG); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (ALL_CHANNELS_ID == ulChannel) + { + for (ULONG i=0; i<ulChannel; ++i) + { + AdapterCommon->MixerVolumeWrite + ( + PropertyRequest->Node, + i, + VOLUME_NORMALIZE_IN_RANGE(*plVolume) + ); + } + } + else + { + AdapterCommon->MixerVolumeWrite + ( + PropertyRequest->Node, + ulChannel, + VOLUME_NORMALIZE_IN_RANGE(*plVolume) + ); + } + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_Mute +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_MUTE + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus; + ULONG ulChannel; + PBOOL pfMute; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportMute( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(BOOL), + sizeof(ULONG) + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + pfMute = PBOOL (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *pfMute = + AdapterCommon->MixerMuteRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + ); + PropertyRequest->ValueSize = sizeof(BOOL); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (ALL_CHANNELS_ID == ulChannel) + { + for (ULONG i=0; i<ulChannel; ++i) + { + AdapterCommon->MixerMuteWrite + ( + PropertyRequest->Node, + i, + (*pfMute) ? TRUE : FALSE + ); + } + } + else + { + AdapterCommon->MixerMuteWrite + ( + PropertyRequest->Node, + ulChannel, + (*pfMute) ? TRUE : FALSE + ); + } + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerMute + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_PeakMeter2 +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_PEAKMETER2 + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG ulChannel; + PLONG plSample; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportPeakMeter2( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // sample value is a LONG + sizeof(ULONG) // instance is the channel number + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + plSample = PLONG (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *plSample = + PEAKMETER_NORMALIZE_IN_RANGE( + AdapterCommon->MixerPeakMeterRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + )); + + PropertyRequest->ValueSize = sizeof(ULONG); + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerVolume + diff --git a/audio/simpleaudiosample/Source/Utilities/savedata.cpp b/audio/simpleaudiosample/Source/Utilities/savedata.cpp new file mode 100644 index 00000000..7b2a11e9 --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/savedata.cpp @@ -0,0 +1,1033 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + savedata.cpp + +Abstract: + + Implementation of Simple Audio Sample data saving class. + + To save the playback data to disk, this class maintains a circular data + buffer, associated frame structures and worker items to save frames to + disk. + Each frame structure represents a portion of buffer. When that portion + of frame is full, a workitem is scheduled to save it to disk. +--*/ +#pragma warning (disable : 4127) +#pragma warning (disable : 26165) + +#include "definitions.h" +#include "savedata.h" +#include <ntstrsafe.h> // This is for using RtlStringcbPrintf + +#define SAVEDATA_POOLTAG 'TDVS' +#define SAVEDATA_POOLTAG1 '1DVS' +#define SAVEDATA_POOLTAG2 '2DVS' +#define SAVEDATA_POOLTAG3 '3DVS' +#define SAVEDATA_POOLTAG4 '4DVS' +#define SAVEDATA_POOLTAG5 '5DVS' +#define SAVEDATA_POOLTAG6 '6DVS' +#define SAVEDATA_POOLTAG7 '7DVS' + +//============================================================================= +// Defines +//============================================================================= +#define RIFF_TAG 0x46464952; +#define WAVE_TAG 0x45564157; +#define FMT__TAG 0x20746D66; +#define DATA_TAG 0x61746164; + +#define DEFAULT_FRAME_COUNT 4 +#define DEFAULT_FRAME_SIZE PAGE_SIZE * 4 +#define DEFAULT_BUFFER_SIZE DEFAULT_FRAME_SIZE * DEFAULT_FRAME_COUNT + +#define DEFAULT_FILE_NAME L"\\DosDevices\\C:\\STREAM" +#define OSDATA_FILE_NAME L"\\DosDevices\\O:\\STREAM" +#define OFFLOAD_FILE_NAME L"OFFLOAD" +#define HOST_FILE_NAME L"HOST" + +#define MAX_WORKER_ITEM_COUNT 15 + +//============================================================================= +// Statics +//============================================================================= +ULONG CSaveData::m_ulStreamId = 0; + +#pragma code_seg("PAGE") +//============================================================================= +// CSaveData +//============================================================================= + +//============================================================================= +CSaveData::CSaveData() +: m_pDataBuffer(NULL), + m_FileHandle(NULL), + m_ulFrameCount(DEFAULT_FRAME_COUNT), + m_ulBufferSize(DEFAULT_BUFFER_SIZE), + m_ulFrameSize(DEFAULT_FRAME_SIZE), + m_ulBufferOffset(0), + m_ulFrameIndex(0), + m_fFrameUsed(NULL), + m_waveFormat(NULL), + m_pFilePtr(NULL), + m_fWriteDisabled(FALSE), + m_bInitialized(FALSE) +{ + PAGED_CODE(); + + m_FileHeader.dwRiff = RIFF_TAG; + m_FileHeader.dwFileSize = 0; + m_FileHeader.dwWave = WAVE_TAG; + m_FileHeader.dwFormat = FMT__TAG; + m_FileHeader.dwFormatLength = sizeof(WAVEFORMATEX); + + m_DataHeader.dwData = DATA_TAG; + m_DataHeader.dwDataLength = 0; + + RtlZeroMemory(&m_objectAttributes, sizeof(m_objectAttributes)); +} // CSaveData + +//============================================================================= +CSaveData::~CSaveData() +{ + PAGED_CODE(); + + DPF_ENTER(("[CSaveData::~CSaveData]")); + + // Update the wave header in data file with real file size. + // + if(m_pFilePtr) + { + m_FileHeader.dwFileSize = + (DWORD) m_pFilePtr->QuadPart - 2 * sizeof(DWORD); + m_DataHeader.dwDataLength = (DWORD) m_pFilePtr->QuadPart - + sizeof(m_FileHeader) - + m_FileHeader.dwFormatLength - + sizeof(m_DataHeader); + + if (STATUS_SUCCESS == KeWaitForSingleObject + ( + &m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + )) + { + if (NT_SUCCESS(FileOpen(FALSE))) + { + FileWriteHeader(); + + FileClose(); + } + + KeReleaseMutex(&m_FileSync, FALSE); + } + } + + if (m_waveFormat) + { + ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1); + m_waveFormat = NULL; + } + + if (m_fFrameUsed) + { + ExFreePoolWithTag(m_fFrameUsed, SAVEDATA_POOLTAG2); + m_fFrameUsed = NULL; + // NOTE : Do not release m_pFilePtr. + } + + if (m_FileName.Buffer) + { + ExFreePoolWithTag(m_FileName.Buffer, SAVEDATA_POOLTAG3); + m_FileName.Buffer = NULL; + } + + if (m_pDataBuffer) + { + ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4); + m_pDataBuffer = NULL; + } +} // CSaveData + +//============================================================================= +void +CSaveData::DestroyWorkItems +( + void +) +{ + PAGED_CODE(); + + if (m_pWorkItems) + { + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + if (m_pWorkItems[i].WorkItem!=NULL) + { + IoFreeWorkItem(m_pWorkItems[i].WorkItem); + m_pWorkItems[i].WorkItem = NULL; + } + } + ExFreePoolWithTag(m_pWorkItems, SAVEDATA_POOLTAG); + m_pWorkItems = NULL; + } + +} // DestroyWorkItems + +//============================================================================= +void +CSaveData::Disable +( + _In_ BOOL fDisable +) +{ + PAGED_CODE(); + + m_fWriteDisabled = fDisable; +} // Disable + +//============================================================================= +NTSTATUS +CSaveData::FileClose(void) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (m_FileHandle) + { + ntStatus = ZwClose(m_FileHandle); + m_FileHandle = NULL; + } + + return ntStatus; +} // FileClose + +//============================================================================= +NTSTATUS +CSaveData::FileOpen +( + _In_ BOOL fOverWrite +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + IO_STATUS_BLOCK ioStatusBlock; + + if( FALSE == m_bInitialized ) + { + return STATUS_UNSUCCESSFUL; + } + + if(!m_FileHandle) + { + ntStatus = + ZwCreateFile + ( + &m_FileHandle, + GENERIC_WRITE | SYNCHRONIZE, + &m_objectAttributes, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + fOverWrite ? FILE_OVERWRITE_IF : FILE_OPEN_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileOpen : Error opening data file]")); + } + } + + return ntStatus; +} // FileOpen + +//============================================================================= +NTSTATUS +CSaveData::FileWrite +( + _In_reads_bytes_(ulDataSize) PBYTE pData, + _In_ ULONG ulDataSize +) +{ + PAGED_CODE(); + + ASSERT(pData); + ASSERT(m_pFilePtr); + + NTSTATUS ntStatus; + + if (m_FileHandle) + { + IO_STATUS_BLOCK ioStatusBlock; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + pData, + ulDataSize, + m_pFilePtr, + NULL); + + if (NT_SUCCESS(ntStatus)) + { + ASSERT(ioStatusBlock.Information == ulDataSize); + + m_pFilePtr->QuadPart += ulDataSize; + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWrite : WriteFileError]")); + } + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWrite : File not open]")); + ntStatus = STATUS_INVALID_HANDLE; + } + + return ntStatus; +} // FileWrite + +//============================================================================= +NTSTATUS +CSaveData::FileWriteHeader(void) +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + + if (m_FileHandle && m_waveFormat) + { + IO_STATUS_BLOCK ioStatusBlock; + + m_pFilePtr->QuadPart = 0; + + m_FileHeader.dwFormatLength = (m_waveFormat->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + m_waveFormat->cbSize; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + &m_FileHeader, + sizeof(m_FileHeader), + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write File Header Error]")); + } + + m_pFilePtr->QuadPart += sizeof(m_FileHeader); + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + m_waveFormat, + m_FileHeader.dwFormatLength, + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Format Error]")); + } + + m_pFilePtr->QuadPart += m_FileHeader.dwFormatLength; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + &m_DataHeader, + sizeof(m_DataHeader), + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Data Header Error]")); + } + + m_pFilePtr->QuadPart += sizeof(m_DataHeader); + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : File not open]")); + ntStatus = STATUS_INVALID_HANDLE; + } + + return ntStatus; +} // FileWriteHeader +NTSTATUS +CSaveData::SetDeviceObject +( + _In_ PDEVICE_OBJECT DeviceObject +) +{ + PAGED_CODE(); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + m_pDeviceObject = DeviceObject; + return ntStatus; +} + +PDEVICE_OBJECT +CSaveData::GetDeviceObject +( + void +) +{ + PAGED_CODE(); + + return m_pDeviceObject; +} + +#pragma code_seg() +//============================================================================= +PSAVEWORKER_PARAM +CSaveData::GetNewWorkItem +( + void +) +{ + LARGE_INTEGER timeOut = { 0 }; + NTSTATUS ntStatus; + + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + ntStatus = + KeWaitForSingleObject + ( + &m_pWorkItems[i].EventDone, + Executive, + KernelMode, + FALSE, + &timeOut + ); + if (STATUS_SUCCESS == ntStatus) + { + if (m_pWorkItems[i].WorkItem) + return &(m_pWorkItems[i]); + else + return NULL; + } + } + + return NULL; +} // GetNewWorkItem +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CSaveData::Initialize +( +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WCHAR szTemp[MAX_PATH]; + size_t cLen; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING osDataVolumeString; + HANDLE osDataFileHandle = NULL; + IO_STATUS_BLOCK ioStatusBlock; + + DPF_ENTER(("[CSaveData::Initialize]")); + + m_ulStreamId++; + + // Probe if OSData volume exists. + // + RtlStringCchPrintfW(szTemp, MAX_PATH, L"%s_probe.txt", OSDATA_FILE_NAME); + RtlInitUnicodeString(&osDataVolumeString, szTemp); + InitializeObjectAttributes + ( + &objectAttributes, + &osDataVolumeString, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + ntStatus = + ZwCreateFile + ( + &osDataFileHandle, + GENERIC_WRITE | SYNCHRONIZE, + &objectAttributes, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + if (NT_SUCCESS(ntStatus)) + { + ZwClose(osDataFileHandle); + } + + // Allocate data file name. + // + RtlStringCchPrintfW(szTemp, MAX_PATH, L"%s_%s_%d.wav", NT_SUCCESS(ntStatus) ? OSDATA_FILE_NAME : DEFAULT_FILE_NAME, HOST_FILE_NAME, m_ulStreamId); + m_FileName.Length = 0; + ntStatus = RtlStringCchLengthW (szTemp, sizeof(szTemp)/sizeof(szTemp[0]), &cLen); + if (NT_SUCCESS(ntStatus)) + { + m_FileName.MaximumLength = (USHORT)((cLen * sizeof(WCHAR)) + sizeof(WCHAR));//convert to wchar and add room for NULL + m_FileName.Buffer = (PWSTR) + ExAllocatePool2 + ( + POOL_FLAG_PAGED, + m_FileName.MaximumLength, + SAVEDATA_POOLTAG3 + ); + if (!m_FileName.Buffer) + { + DPF(D_TERSE, ("[Could not allocate memory for FileName]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Allocate memory for data buffer. + // + if (NT_SUCCESS(ntStatus)) + { + RtlStringCbCopyW(m_FileName.Buffer, m_FileName.MaximumLength, szTemp); + m_FileName.Length = (USHORT)wcslen(m_FileName.Buffer) * sizeof(WCHAR); + DPF(D_BLAB, ("[New DataFile -- %S", m_FileName.Buffer)); + + m_pDataBuffer = (PBYTE) + ExAllocatePool2 + ( + POOL_FLAG_NON_PAGED, + m_ulBufferSize, + SAVEDATA_POOLTAG4 + ); + if (!m_pDataBuffer) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Allocate memory for frame usage flags and m_pFilePtr. + // + if (NT_SUCCESS(ntStatus)) + { + m_fFrameUsed = (PBOOL) + ExAllocatePool2 + ( + POOL_FLAG_NON_PAGED, + m_ulFrameCount * sizeof(BOOL) + + sizeof(LARGE_INTEGER), + SAVEDATA_POOLTAG2 + ); + if (!m_fFrameUsed) + { + DPF(D_TERSE, ("[Could not allocate memory for frame flags]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Initialize the spinlock to synchronize access to the frames + // + KeInitializeSpinLock ( &m_FrameInUseSpinLock ) ; + + // Initialize the file mutex + // + KeInitializeMutex( &m_FileSync, 1 ) ; + + // Open the data file. + // + if (NT_SUCCESS(ntStatus)) + { + // m_fFrameUsed has additional memory to hold m_pFilePtr + // + m_pFilePtr = (PLARGE_INTEGER) + (((PBYTE) m_fFrameUsed) + m_ulFrameCount * sizeof(BOOL)); + + // Create data file. + InitializeObjectAttributes + ( + &m_objectAttributes, + &m_FileName, + OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + m_bInitialized = TRUE; + + // Write wave header information to data file. + ntStatus = KeWaitForSingleObject + ( + &m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + ); + + if (STATUS_SUCCESS == ntStatus) + { + ntStatus = FileOpen(TRUE); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = FileWriteHeader(); + + FileClose(); + } + + KeReleaseMutex( &m_FileSync, FALSE ); + } + } + + return ntStatus; +} // Initialize + +//============================================================================= +NTSTATUS +CSaveData::InitializeWorkItems +( + _In_ PDEVICE_OBJECT DeviceObject +) +{ + PAGED_CODE(); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + DPF_ENTER(("[CSaveData::InitializeWorkItems]")); + + if (m_pWorkItems != NULL) + { + return ntStatus; + } + + m_pWorkItems = (PSAVEWORKER_PARAM) + ExAllocatePool2 + ( + POOL_FLAG_NON_PAGED, + sizeof(SAVEWORKER_PARAM) * MAX_WORKER_ITEM_COUNT, + SAVEDATA_POOLTAG + ); + if (m_pWorkItems) + { + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + + m_pWorkItems[i].WorkItem = IoAllocateWorkItem(DeviceObject); + if(m_pWorkItems[i].WorkItem == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + KeInitializeEvent + ( + &m_pWorkItems[i].EventDone, + NotificationEvent, + TRUE + ); + } + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + return ntStatus; +} // InitializeWorkItems + +//============================================================================= + +IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; + +VOID +SaveFrameWorkerCallback +( + _In_ PDEVICE_OBJECT pDeviceObject, + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(pDeviceObject); + + PAGED_CODE(); + + ASSERT(Context); + + PSAVEWORKER_PARAM pParam = (PSAVEWORKER_PARAM) Context; + PCSaveData pSaveData; + + if (NULL == pParam) + { + // This is completely unexpected, assert here. + // + ASSERT(pParam); + return; + } + + DPF(D_VERBOSE, ("[SaveFrameWorkerCallback], %d", pParam->ulFrameNo)); + + ASSERT(pParam->pSaveData); + ASSERT(pParam->pSaveData->m_fFrameUsed); + + if (pParam->WorkItem) + { + pSaveData = pParam->pSaveData; + + if (STATUS_SUCCESS == KeWaitForSingleObject + ( + &pSaveData->m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + )) + { + if (NT_SUCCESS(pSaveData->FileOpen(FALSE))) + { + pSaveData->FileWrite(pParam->pData, pParam->ulDataSize); + pSaveData->FileClose(); + } + InterlockedExchange( (LONG *)&(pSaveData->m_fFrameUsed[pParam->ulFrameNo]), FALSE ); + + KeReleaseMutex( &pSaveData->m_FileSync, FALSE ); + } + } + + KeSetEvent(&pParam->EventDone, 0, FALSE); +} // SaveFrameWorkerCallback + +//============================================================================= +NTSTATUS +CSaveData::SetDataFormat +( + _In_ PKSDATAFORMAT pDataFormat +) +{ + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; + + DPF_ENTER(("[CSaveData::SetDataFormat]")); + + ASSERT(pDataFormat); + + PWAVEFORMATEX pwfx = NULL; + + if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND)) + { + pwfx = + &(((PKSDATAFORMAT_DSOUND) pDataFormat)->BufferDesc.WaveFormatEx); + } + else if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) + { + pwfx = &((PKSDATAFORMAT_WAVEFORMATEX) pDataFormat)->WaveFormatEx; + } + + if (pwfx) + { + // Free the previously allocated waveformat + if (m_waveFormat) + { + ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1); + } + + m_waveFormat = (PWAVEFORMATEX) + ExAllocatePool2 + ( + POOL_FLAG_NON_PAGED, + (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + pwfx->cbSize, + SAVEDATA_POOLTAG1 + ); + + if(m_waveFormat) + { + RtlCopyMemory( m_waveFormat, + pwfx, + (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + pwfx->cbSize); + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + return ntStatus; +} // SetDataFormat + +//============================================================================= +NTSTATUS +CSaveData::SetMaxWriteSize +( + _In_ ULONG ulMaxWriteSize +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG bufferSize = 0; + PBYTE buffer = NULL; + + DPF_ENTER(("[CSaveData::SetMaxWriteSize]")); + + // + // Compute new buffer size. + // + ntStatus = RtlULongMult(ulMaxWriteSize, DEFAULT_FRAME_COUNT, &bufferSize); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data, MaxWriteSize %u is too big]", ulMaxWriteSize)); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + // + // Alloc memory for buffer. + // + buffer = (PBYTE) + ExAllocatePool2 + ( + POOL_FLAG_NON_PAGED, + bufferSize, + SAVEDATA_POOLTAG4 + ); + if (!buffer) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + // + // Free old one. + // + if (m_pDataBuffer) + { + ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4); + m_pDataBuffer = NULL; + } + + // + // Init new buffer settings. + // + m_pDataBuffer = buffer; + m_ulBufferSize = bufferSize; + m_ulFrameSize = ulMaxWriteSize; + + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} // SetDataFormat + +//============================================================================= +void +CSaveData::ReadData +( + _Inout_updates_bytes_all_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount +) +{ + UNREFERENCED_PARAMETER(pBuffer); + UNREFERENCED_PARAMETER(ulByteCount); + + PAGED_CODE(); + + // Not implemented yet. +} // ReadData + +//============================================================================= +#pragma code_seg() +void +CSaveData::SaveFrame +( + _In_ ULONG ulFrameNo, + _In_ ULONG ulDataSize +) +{ + PSAVEWORKER_PARAM pParam = NULL; + + DPF_ENTER(("[CSaveData::SaveFrame]")); + + pParam = GetNewWorkItem(); + if (pParam) + { + pParam->pSaveData = this; + pParam->ulFrameNo = ulFrameNo; + pParam->ulDataSize = ulDataSize; + pParam->pData = m_pDataBuffer + ulFrameNo * m_ulFrameSize; + KeResetEvent(&pParam->EventDone); + IoQueueWorkItem(pParam->WorkItem, SaveFrameWorkerCallback, + CriticalWorkQueue, (PVOID)pParam); + } +} // SaveFrame +#pragma code_seg("PAGE") +//============================================================================= +void +CSaveData::WaitAllWorkItems +( + void +) +{ + PAGED_CODE(); + + DPF_ENTER(("[CSaveData::WaitAllWorkItems]")); + + // Save the last partially-filled frame + if (m_ulBufferOffset > m_ulFrameIndex * m_ulFrameSize) + { + ULONG size; + + size = m_ulBufferOffset - m_ulFrameIndex * m_ulFrameSize; + SaveFrame(m_ulFrameIndex, size); + } + + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + DPF(D_VERBOSE, ("[Waiting for WorkItem] %d", i)); + KeWaitForSingleObject + ( + &(m_pWorkItems[i].EventDone), + Executive, + KernelMode, + FALSE, + NULL + ); + } +} // WaitAllWorkItems + +#pragma code_seg() +//============================================================================= +void +CSaveData::WriteData +( + _In_reads_bytes_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount +) +{ + ASSERT(pBuffer); + + BOOL fSaveFrame = FALSE; + ULONG ulSaveFrameIndex = 0; + KIRQL oldIrql; + + // If stream writing is disabled, then exit. + // + if (m_fWriteDisabled) + { + return; + } + + DPF_ENTER(("[CSaveData::WriteData ulByteCount=%lu]", ulByteCount)); + + if( 0 == ulByteCount ) + { + return; + } + + // The logic below assumes that write size is <= than frame size. + if (ulByteCount > m_ulFrameSize) + { + ulByteCount = m_ulFrameSize; + } + + // Check to see if this frame is available. + KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql); + if (!m_fFrameUsed[m_ulFrameIndex]) + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + + ULONG ulWriteBytes = ulByteCount; + + if( (m_ulBufferSize - m_ulBufferOffset) < ulWriteBytes ) + { + ulWriteBytes = m_ulBufferSize - m_ulBufferOffset; + } + + RtlCopyMemory(m_pDataBuffer + m_ulBufferOffset, pBuffer, ulWriteBytes); + m_ulBufferOffset += ulWriteBytes; + + // Check to see if we need to save this frame + if (m_ulBufferOffset >= ((m_ulFrameIndex + 1) * m_ulFrameSize)) + { + fSaveFrame = TRUE; + } + + // Loop the buffer, if we reached the end. + if (m_ulBufferOffset == m_ulBufferSize) + { + fSaveFrame = TRUE; + m_ulBufferOffset = 0; + } + + if (fSaveFrame) + { + InterlockedExchange( (LONG *)&(m_fFrameUsed[m_ulFrameIndex]), TRUE ); + ulSaveFrameIndex = m_ulFrameIndex; + m_ulFrameIndex = (m_ulFrameIndex + 1) % m_ulFrameCount; + } + + // Write the left over if the next frame is available. + if (ulWriteBytes != ulByteCount) + { + KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql ); + if (!m_fFrameUsed[m_ulFrameIndex]) + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + RtlCopyMemory + ( + m_pDataBuffer + m_ulBufferOffset, + pBuffer + ulWriteBytes, + ulByteCount - ulWriteBytes + ); + + m_ulBufferOffset += ulByteCount - ulWriteBytes; + } + else + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql); + DPF(D_BLAB, ("[Frame overflow, next frame is in use]")); + } + } + + if (fSaveFrame) + { + SaveFrame(ulSaveFrameIndex, m_ulFrameSize); + } + } + else + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + DPF(D_BLAB, ("[Frame %d is in use]", m_ulFrameIndex)); + } + +} // WriteData + diff --git a/audio/simpleaudiosample/Source/Utilities/savedata.h b/audio/simpleaudiosample/Source/Utilities/savedata.h new file mode 100644 index 00000000..c658ec71 --- /dev/null +++ b/audio/simpleaudiosample/Source/Utilities/savedata.h @@ -0,0 +1,189 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + savedata.h + +Abstract: + + Declaration of Simple Audio Sample data saving class. This class supplies services +to save data to disk. + +--*/ + +#ifndef _SIMPLEAUDIOSAMPLE_SAVEDATA_H +#define _SIMPLEAUDIOSAMPLE_SAVEDATA_H + +//----------------------------------------------------------------------------- +// Forward declaration +//----------------------------------------------------------------------------- +class CSaveData; +typedef CSaveData *PCSaveData; + + +//----------------------------------------------------------------------------- +// Structs +//----------------------------------------------------------------------------- + +// Parameter to workitem. +#include <pshpack1.h> +typedef struct _SAVEWORKER_PARAM { + PIO_WORKITEM WorkItem; + ULONG ulFrameNo; + ULONG ulDataSize; + PBYTE pData; + PCSaveData pSaveData; + KEVENT EventDone; +} SAVEWORKER_PARAM; +typedef SAVEWORKER_PARAM *PSAVEWORKER_PARAM; +#include <poppack.h> + +// wave file header. +#include <pshpack1.h> +typedef struct _OUTPUT_FILE_HEADER +{ + DWORD dwRiff; + DWORD dwFileSize; + DWORD dwWave; + DWORD dwFormat; + DWORD dwFormatLength; +} OUTPUT_FILE_HEADER; +typedef OUTPUT_FILE_HEADER *POUTPUT_FILE_HEADER; + +typedef struct _OUTPUT_DATA_HEADER +{ + DWORD dwData; + DWORD dwDataLength; +} OUTPUT_DATA_HEADER; +typedef OUTPUT_DATA_HEADER *POUTPUT_DATA_HEADER; + +#include <poppack.h> + +//----------------------------------------------------------------------------- +// Classes +//----------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////////// +// CSaveData +// Saves the wave data to disk. +// +IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; + +class CSaveData +{ +protected: + UNICODE_STRING m_FileName; // DataFile name. + HANDLE m_FileHandle; // DataFile handle. + PBYTE m_pDataBuffer; // Data buffer. + ULONG m_ulBufferSize; // Total buffer size. + + ULONG m_ulFrameIndex; // Current Frame. + ULONG m_ulFrameCount; // Frame count. + ULONG m_ulFrameSize; + ULONG m_ulBufferOffset; // index in buffer. + PBOOL m_fFrameUsed; // Frame usage table. + KSPIN_LOCK m_FrameInUseSpinLock; // Spinlock for synch. + KMUTEX m_FileSync; // Synchronizes file access + + OBJECT_ATTRIBUTES m_objectAttributes; // Used for opening file. + + OUTPUT_FILE_HEADER m_FileHeader; + PWAVEFORMATEX m_waveFormat; + OUTPUT_DATA_HEADER m_DataHeader; + PLARGE_INTEGER m_pFilePtr; + + static PDEVICE_OBJECT m_pDeviceObject; + static ULONG m_ulStreamId; + static PSAVEWORKER_PARAM m_pWorkItems; + + BOOL m_fWriteDisabled; + + BOOL m_bInitialized; + +public: + CSaveData(); + ~CSaveData(); + + static NTSTATUS InitializeWorkItems + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + static void DestroyWorkItems + ( + void + ); + void Disable + ( + _In_ BOOL fDisable + ); + static PSAVEWORKER_PARAM GetNewWorkItem + ( + void + ); + NTSTATUS Initialize + ( + ); + static NTSTATUS SetDeviceObject + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + static PDEVICE_OBJECT GetDeviceObject + ( + void + ); + void ReadData + ( + _Inout_updates_bytes_all_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount + ); + NTSTATUS SetDataFormat + ( + _In_ PKSDATAFORMAT pDataFormat + ); + NTSTATUS SetMaxWriteSize + ( + _In_ ULONG ulMaxWriteSize + ); + void WaitAllWorkItems + ( + void + ); + void WriteData + ( + _In_reads_bytes_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount + ); + +private: + NTSTATUS FileClose + ( + void + ); + NTSTATUS FileOpen + ( + _In_ BOOL fOverWrite + ); + NTSTATUS FileWrite + ( + _In_reads_bytes_(ulDataSize) PBYTE pData, + _In_ ULONG ulDataSize + ); + NTSTATUS FileWriteHeader + ( + void + ); + + void SaveFrame + ( + _In_ ULONG ulFrameNo, + _In_ ULONG ulDataSize + ); + + friend + IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; +}; +typedef CSaveData *PCSaveData; + +#endif |
