diff options
| author | Shantanu Gupta <[email protected]> | 2021-03-19 15:30:28 -0700 |
|---|---|---|
| committer | Shantanu Gupta <[email protected]> | 2021-03-19 15:30:28 -0700 |
| commit | 3ac047145909d338f1edbae9c11dc536aee3cda5 (patch) | |
| tree | 1d3b731ad5678396ed514d07148777db658ab367 | |
| parent | bee1573a5ba8d82f089e0dcb2b8113b12d4b60ba (diff) | |
| parent | b5fcd95c891f44000619c26635a483e5a7448ee1 (diff) | |
Merge remote-tracking branch 'upstream/master'
161 files changed, 24620 insertions, 2199 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..b0a8d776 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wil"] + path = wil + url = https://github.com/microsoft/wil 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 diff --git a/audio/sysvad/EndpointsCommon/mintopo.cpp b/audio/sysvad/EndpointsCommon/mintopo.cpp index 28ddf419..4e417c4a 100644 --- a/audio/sysvad/EndpointsCommon/mintopo.cpp +++ b/audio/sysvad/EndpointsCommon/mintopo.cpp @@ -570,7 +570,7 @@ Return Value: // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY // pDesc->JackCapabilities = JackCapabilities; - + ntStatus = STATUS_SUCCESS; } } @@ -581,6 +581,7 @@ Return Value: return ntStatus; } + #if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND) //============================================================================= #pragma code_seg() diff --git a/audio/sysvad/EndpointsCommon/mintopo.h b/audio/sysvad/EndpointsCommon/mintopo.h index 4aa3d2de..b4eda617 100644 --- a/audio/sysvad/EndpointsCommon/mintopo.h +++ b/audio/sysvad/EndpointsCommon/mintopo.h @@ -33,6 +33,8 @@ class CMiniportTopology : { private: eDeviceType m_DeviceType; + + union { PVOID m_DeviceContext; #if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND) @@ -55,6 +57,7 @@ public: m_DeviceType(DeviceType), m_DeviceContext(DeviceContext) { + #if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND) if (IsSidebandDevice()) { @@ -73,18 +76,19 @@ public: NTSTATUS PropertyHandlerJackDescription ( - _In_ PPCPROPERTY_REQUEST PropertyRequest, - _In_ ULONG cJackDescriptions, - _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + _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 + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *JackDescriptions, + _In_ DWORD JackCapabilities ); + #if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND) BOOL IsSidebandDevice() diff --git a/audio/sysvad/EndpointsCommon/minwavertstream.cpp b/audio/sysvad/EndpointsCommon/minwavertstream.cpp index 93ecdcf3..499d5b38 100644 --- a/audio/sysvad/EndpointsCommon/minwavertstream.cpp +++ b/audio/sysvad/EndpointsCommon/minwavertstream.cpp @@ -968,12 +968,12 @@ NTSTATUS CMiniportWaveRTStream::GetReadPacket // Return next packet number to be read *PacketNumber = availablePacketNumber; - // Compute and return timestamp corresponding to start of the available packet. In a real hardware + // 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 - 1) * (m_ulDmaBufferSize / m_ulNotificationsPerBuffer); + 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; diff --git a/audio/sysvad/EndpointsCommon/speakertopo.cpp b/audio/sysvad/EndpointsCommon/speakertopo.cpp index cca4dac9..fee3d542 100644 --- a/audio/sysvad/EndpointsCommon/speakertopo.cpp +++ b/audio/sysvad/EndpointsCommon/speakertopo.cpp @@ -22,7 +22,6 @@ Abstract: #pragma code_seg("PAGE") - //============================================================================= NTSTATUS PropertyHandler_SpeakerTopoFilter diff --git a/audio/sysvad/EndpointsCommon/speakertoptable.h b/audio/sysvad/EndpointsCommon/speakertoptable.h index 50f58c7b..6850c0cf 100644 --- a/audio/sysvad/EndpointsCommon/speakertoptable.h +++ b/audio/sysvad/EndpointsCommon/speakertoptable.h @@ -85,7 +85,7 @@ PCPIN_DESCRIPTOR SpeakerTopoMiniportPins[] = //============================================================================= static -KSJACK_DESCRIPTION SpeakerJackDescSpeakers = +KSJACK_DESCRIPTION SpeakerJackDescBridge = { KSAUDIO_SPEAKER_STEREO, 0xB3C98C, // Color spec for green @@ -101,9 +101,10 @@ static PKSJACK_DESCRIPTION SpeakerJackDescriptions[] = { NULL, - &SpeakerJackDescSpeakers + &SpeakerJackDescBridge }; + //============================================================================= static PCCONNECTION_DESCRIPTOR SpeakerTopoMiniportConnections[] = @@ -120,13 +121,15 @@ PCPROPERTY_ITEM PropertiesSpeakerTopoFilter[] = { &KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION, - KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + KSPROPERTY_TYPE_GET | + KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SpeakerTopoFilter }, { &KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2, - KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + KSPROPERTY_TYPE_GET | + KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SpeakerTopoFilter } }; @@ -152,4 +155,4 @@ PCFILTER_DESCRIPTOR SpeakerTopoMiniportFilterDescriptor = }; #endif // _SYSVAD_SPEAKERTOPTABLE_H_ - + diff --git a/audio/sysvad/Package/package.VcxProj b/audio/sysvad/Package/package.VcxProj index cef0bc33..0b47036c 100644 --- a/audio/sysvad/Package/package.VcxProj +++ b/audio/sysvad/Package/package.VcxProj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?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"> @@ -109,4 +109,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/audio/sysvad/README.md b/audio/sysvad/README.md index 281af1ad..3a799ba2 100644 --- a/audio/sysvad/README.md +++ b/audio/sysvad/README.md @@ -175,3 +175,11 @@ On the target computer, in a Command Prompt window, enter **devmgmt.msc** to ope 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 *SYSVAD (with APO Extensions)*, 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 *SYSVAD (with APO Extensions)* driver. + +## HLK testing + +The sample uploaded here is tested using the latest HLK version available to make sure it passes all audio tests in the current playlist. However, since it is a virtual audio driver it does not implement audio mixing and simulates capture and loopback by generating a tone. Given these limitations, there are some HLK tests that are expected to fail because they rely on the described functionality. + +In the case of audio tests, one of these exceptions is the Hardware Offload of Audio Processing Test. This test is aimed at devices that support offload capabilities and performs checks to make sure that the device complies with the appropiate requirements. In the particular case of SysVAD, this test will fail for endpoints with offload and loopback. + +For endpoints with offload, the test will fail because the driver includes offload pins but it does not implement a mixer with volume, mute and peak meter nodes, etc. For the case of endpoints with loopback, the test will fail because the driver simulates loopback by returning a sine tone instead of performing real mixing of streams in host and/or offload pins.
\ No newline at end of file diff --git a/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx b/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx Binary files differindex a6bfb4af..be9428fb 100644 --- a/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx +++ b/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx diff --git a/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx b/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx Binary files differindex a5e6e73f..8f04a934 100644 --- a/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx +++ b/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx diff --git a/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx b/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx Binary files differindex ce3ed335..1c89827a 100644 --- a/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx +++ b/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx diff --git a/audio/sysvad/TabletAudioSample/tabletaudiosample.inx b/audio/sysvad/TabletAudioSample/tabletaudiosample.inx Binary files differindex ba96faee..58ee4d88 100644 --- a/audio/sysvad/TabletAudioSample/tabletaudiosample.inx +++ b/audio/sysvad/TabletAudioSample/tabletaudiosample.inx diff --git a/audio/sysvad/adapter.cpp b/audio/sysvad/adapter.cpp index 7cfa54ee..cc7960a5 100644 --- a/audio/sysvad/adapter.cpp +++ b/audio/sysvad/adapter.cpp @@ -1177,6 +1177,7 @@ Return Value: switch (stack->MinorFunction) { +#ifdef SYSVAD_USB_SIDEBAND case IRP_MN_QUERY_DEVICE_RELATIONS: switch (stack->Parameters.QueryDeviceRelations.Type) @@ -1203,6 +1204,7 @@ Return Value: break; } break; +#endif // SYSVAD_USB_SIDEBAND case IRP_MN_REMOVE_DEVICE: case IRP_MN_SURPRISE_REMOVAL: diff --git a/audio/sysvad/common.cpp b/audio/sysvad/common.cpp index f88ca093..9292c96d 100644 --- a/audio/sysvad/common.cpp +++ b/audio/sysvad/common.cpp @@ -302,7 +302,9 @@ class CAdapterCommon : STDMETHODIMP_(VOID) Cleanup(); +#ifdef SYSVAD_USB_SIDEBAND STDMETHODIMP_(NTSTATUS) UpdatePowerRelations(_In_ PIRP Irp); +#endif // SYSVAD_USB_SIDEBAND //===================================================================== // friends @@ -608,7 +610,9 @@ Return Value: InterlockedDecrement(&CAdapterCommon::m_AdapterInstances); ASSERT(CAdapterCommon::m_AdapterInstances == 0); +#ifdef SYSVAD_USB_SIDEBAND ASSERT(IsListEmpty(&m_PowerRelations)); +#endif // SYSVAD_USB_SIDEBAND } // ~CAdapterCommon //============================================================================= @@ -2300,6 +2304,7 @@ CAdapterCommon::Cleanup() EmptySubdeviceCache(); } +#ifdef SYSVAD_USB_SIDEBAND //============================================================================= #pragma code_seg("PAGE") NTSTATUS @@ -2426,6 +2431,7 @@ Exit: return status; } +#endif // SYSVAD_USB_SIDEBAND //============================================================================= #pragma code_seg("PAGE") diff --git a/audio/sysvad/common.h b/audio/sysvad/common.h index 794b3797..8ce0a866 100644 --- a/audio/sysvad/common.h +++ b/audio/sysvad/common.h @@ -206,6 +206,7 @@ typedef struct _PIN_DEVICE_FORMATS_AND_MODES } PIN_DEVICE_FORMATS_AND_MODES, *PPIN_DEVICE_FORMATS_AND_MODES; + // // Parameter module handler function prototypes. // @@ -670,7 +671,9 @@ DECLARE_INTERFACE_(IAdapterCommon, IUnknown) STDMETHOD_(VOID, Cleanup)(); +#ifdef SYSVAD_USB_SIDEBAND STDMETHOD_(NTSTATUS, UpdatePowerRelations)(_In_ PIRP Irp); +#endif // SYSVAD_USB_SIDEBAND STDMETHOD_(NTSTATUS, NotifyEndpointPair) ( diff --git a/audio/sysvad/sysvad.sln b/audio/sysvad/sysvad.sln index 1935f749..6a58b6df 100644 --- a/audio/sysvad/sysvad.sln +++ b/audio/sysvad/sysvad.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27703.2042 diff --git a/audio/tests/KSPosTst/KsPosTestTaef.cpp b/audio/tests/KSPosTst/KsPosTestTaef.cpp new file mode 100644 index 00000000..b92f9e4f --- /dev/null +++ b/audio/tests/KSPosTst/KsPosTestTaef.cpp @@ -0,0 +1,215 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// File Name: +// +// KsPosTestTaef.cpp +// +// Abstract: +// +// Implementation file for KsPosTestTaef +// +// ------------------------------------------------------------------------------- +#include "PreComp.h" +#include "KsPosTestTaef.h" +#include "tests.h" + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +#define RUN_TEST_CASE(testfn) \ +{ \ + SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); \ + testfn(); \ +} + +IBasicLog * g_pBasicLog = NULL; +WDMAudio::KsPosTest* g_pKsPosTst = NULL; + +TIMER_MECHANISM g_Timer = +{ + tpQPC, "QueryPerformanceCounter" +}; + +void LogWaveFormat(WAVEFORMATEX* pwfx) +{ + WAVEFORMATEXTENSIBLE* wfex = (WAVEFORMATEXTENSIBLE*)pwfx; + + switch (pwfx->wFormatTag) + { + case WAVE_FORMAT_PCM: + LOG(g_pBasicLog, L" wFormatTag = WAVE_FORMAT_PCM"); + break; + case WAVE_FORMAT_IEEE_FLOAT: + LOG(g_pBasicLog, L" wFormatTag = WAVE_FORMAT_IEEE_FLOAT"); + break; + case WAVE_FORMAT_EXTENSIBLE: + LOG(g_pBasicLog, L" wFormatTag = WAVE_FORMAT_EXTENSIBLE"); + if (wfex->SubFormat.Data1 == WAVE_FORMAT_PCM) + LOG(g_pBasicLog, L" SubFormat = WAVE_FORMAT_PCM"); + if (wfex->SubFormat.Data1 == WAVE_FORMAT_IEEE_FLOAT) + LOG(g_pBasicLog, L" SubFormat = WAVE_FORMAT_IEEE_FLOAT"); + break; + default: + LOG(g_pBasicLog, L" wFormatTag = UNKNOWN!"); + } + LOG(g_pBasicLog, L" nChannel = %d", pwfx->nChannels); + LOG(g_pBasicLog, L" nSamplesPerSec = %d", pwfx->nSamplesPerSec); + LOG(g_pBasicLog, L" nAvgBytesPerSe = %d", pwfx->nAvgBytesPerSec); + LOG(g_pBasicLog, L" nBlockAlign = %d", pwfx->nBlockAlign); + LOG(g_pBasicLog, L" wBitsPerSample = %d", pwfx->wBitsPerSample); + + + if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + LOG(g_pBasicLog, L" wValidBitsPerSample = %d", wfex->Samples.wValidBitsPerSample); + LOG(g_pBasicLog, L" dwChannelMask = %hd", wfex->dwChannelMask); + } +} + +// ------------------------------------------------------------------------------- +namespace WDMAudio +{ + BEGIN_MODULE() + MODULE_PROPERTY(L"Feature", L"PortCls HCK Tests") + MODULE_PROPERTY(L"TestResourceDependent", L"true") + END_MODULE() + + // Create WEXBasicLog, Coinitialize Etc + MODULE_SETUP(WDMAudioSetup) + + // Release stuff acquired in WDMAudioSetup + MODULE_CLEANUP(WDMAudioCleanup) +}; + +bool WDMAudio::WDMAudioSetup() +{ + if (NULL == g_pBasicLog) + { + if (!(VERIFY_SUCCEEDED(CreateWexBasicLog(&g_pBasicLog)))) + { + return false; + } + } + + return true; +} + +bool WDMAudio::WDMAudioCleanup() +{ + if (g_pBasicLog) + { + g_pBasicLog->Release(); + } + + return true; +} + +// ------------------------------------------------------------------------------- +bool WDMAudio::KsPosTest::KsPosTestSetup() +{ + g_pKsPosTst = this; + m_pTimer = &g_Timer; + // The histograms can be displayed by using the command line parameter checked below + // If the parameter is not specified, the default is to not log anything + bool param = true; + WEX::TestExecution::RuntimeParameters::TryGetValue(L"logHistograms", param); + m_fLogHistograms = param; + return true; +} + +bool WDMAudio::KsPosTest::KsPosTestCleanUp() +{ + g_pKsPosTst = NULL; + + return true; +} + +// ------------------------------------------------------------------------------- +bool WDMAudio::KsPosTest::TestCaseSetup() +{ + CComPtr<ITestResource> spResource; + CComQIPtr<IHalfAppContainer> spHalfContainer; + + SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); + + Log::Comment(L""); + Log::Comment(L"------------------------------------------------------"); + Log::Comment(L"Running test case setup"); + + size_t count = Resources::Count(); + if (!VERIFY_ARE_EQUAL(count, (size_t)1)) { return false; } + if (!VERIFY_SUCCEEDED(Resources::Item(0, &spResource))) { return false; } + + // 1. Assign m_pHalf + spHalfContainer = spResource; + if (!VERIFY_IS_NOT_NULL(spHalfContainer)) { return false; } + if (!VERIFY_SUCCEEDED(spHalfContainer->GetHalfApp(&m_pHalf))) { return false; } + + // 2. Check if this is a loopback pin. If so, assign the host pin's HalfApp + if (m_pHalf->m_ConnectorType == eLoopbackConnector) + { + VERIFY_SUCCEEDED(m_pHalf->GetHostHalfApp(&m_pHostHalf)); + } + else + { + m_pHostHalf = NULL; + } + + Log::Comment(L"The test will run on the following format:"); + if (m_pHostHalf != NULL) + { + LogWaveFormat(m_pHostHalf->m_pCurrentFormat.get()); + } + else + { + LogWaveFormat(m_pHalf->m_pCurrentFormat.get()); + } + + return true; +} + +bool WDMAudio::KsPosTest::TestCaseCleanUp() +{ + // Stop the timer here, in case any latency test fails + NtSetTimerResolution(0, FALSE, &actualTimerResolution); + + if (!CompareWaveFormat(m_pHalf->m_pCurrentFormat.get(), m_pHalf->m_pPreferredFormat.get())) { + CloneWaveFormat(m_pHalf->m_pPreferredFormat.get(), wil::out_param(m_pHalf->m_pCurrentFormat)); + m_pHalf->m_u32CurrentDefaultPeriodicityInFrames = m_pHalf->m_u32DefaultPeriodicityInFrames; + m_pHalf->m_u32CurrentFundamentalPeriodicityInFrames = m_pHalf->m_u32FundamentalPeriodicityInFrames; + m_pHalf->m_u32CurrentMinPeriodicityInFrames = m_pHalf->m_u32MinPeriodicityInFrames; + m_pHalf->m_u32CurrentMaxPeriodicityInFrames = m_pHalf->m_u32MaxPeriodicityInFrames; + } + + if (m_pHostHalf != NULL) { + if (!VERIFY_SUCCEEDED(m_pHostHalf->CleanupStream())) { return false; } + if (!VERIFY_SUCCEEDED(m_pHostHalf->ReleaseEndpoint())) { return false; } + if (!VERIFY_SUCCEEDED(m_pHostHalf->ReleaseSineToneDataBuffer())) { return false; } + delete m_pHostHalf; + m_pHostHalf = NULL; + } + + if (m_pHalf != NULL) { + if (!VERIFY_SUCCEEDED(m_pHalf->CleanupStream())) { return false; } + if (!VERIFY_SUCCEEDED(m_pHalf->ReleaseEndpoint())) { return false; } + if (!VERIFY_SUCCEEDED(m_pHalf->ReleaseSineToneDataBuffer())) { return false; } + + m_pHalf = NULL; + } + + return true; +} + +// ------------------------------------------------------------------------------- +void WDMAudio::KsPosTest::TAEF_DriftAndJitter() +{ + RUN_TEST_CASE(Test_DriftAndJitter_Default); +} + +void WDMAudio::KsPosTest::TAEF_Latency() +{ + RUN_TEST_CASE(Test_Latency_Default); +} diff --git a/audio/tests/KSPosTst/PreComp.h b/audio/tests/KSPosTst/PreComp.h new file mode 100644 index 00000000..5fdfc9db --- /dev/null +++ b/audio/tests/KSPosTst/PreComp.h @@ -0,0 +1,108 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// File Name: +// +// PreComp.h +// +// Abstract: +// +// Precompiled common headers +// +// ------------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// Number of milliseconds that the test will be delayed to wait for the stream to stabilize +#define DELAY_FOR_STABILIZE 100 + +// Number of milliseconds in a second +#define MILLISECONDS_PER_SECOND 1000 + +// Number of HNS units in one second +#define HNSTIME_UNITS_PER_SECOND 10000000 + +// Number of HNS units in one millisecond +#define HNSTIME_UNITS_PER_MILLISECOND 10000 + +typedef double (*TIMEPROC) (void); +double tpQPC(void); + +typedef struct _tag_tm +{ + TIMEPROC Proc; + LPCSTR strRep; +} TIMER_MECHANISM, * PTIMER_MECHANISM; + +// ---------------------------------------------------------- +// Timer functions +extern "C" __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI NtSetTimerResolution( + _In_ ULONG DesiredTime, _In_ BOOLEAN SetResolution, _Out_ PULONG ActualTime); + +// 1 ms timer resolution +static ULONG timerResolution = (1 * 10000); +static ULONG actualTimerResolution; + +// ------------------------------------------------------------------------------ +// structs used to hold results of perf tests +#define DATA_POINTS 100 + +typedef struct +{ + double testTime; // Time the sample was taken, in milliseconds, relative to the start of the sample collection + double positionTime; // Time of the reported position, in milliseconds, relative to the start of the sample collection + double position; // Position, in seconds, relative to the start of the sample collection +} POSITION_SET, * PPOSITION_SET; + +typedef struct +{ + PPOSITION_SET argPosSets; + ULONG cPosSets; + ULONG ctLatencyMeas; + ULONG csBufferMeas; + double bytesPerSecond; // measured (in bytes/sec, NOT samples/sec) + double dOffset; // measured +} PERF_RESULTS, * PPERF_RESULTS; + +typedef struct +{ + WAVEFORMATEXTENSIBLE wfxFormat; + PERF_RESULTS perfResults; // +} FORMAT_ENTRY, * PFORMAT_ENTRY; diff --git a/audio/tests/KSPosTst/TestResourceBuild.cpp b/audio/tests/KSPosTst/TestResourceBuild.cpp new file mode 100644 index 00000000..93959089 --- /dev/null +++ b/audio/tests/KSPosTst/TestResourceBuild.cpp @@ -0,0 +1,522 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// File Name: +// +// TestResourceBuild.cpp +// +// Abstract: +// +// TAEF BuildResourceList implementation +// +// ------------------------------------------------------------------------------ +#include "PreComp.h" +#include <TestResource.h> +#include <PropertyHelper.h> + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// CreateTestResource +// +// Create the test resource with MMDevice, device id, device name, data flow, connector type, connector id, mode, +// list of formats +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT CreateTestResource +( + ResourceList& resourceList, + DeviceDescriptor descriptor +) +{ + HRESULT hr = S_OK; + + CComHeapPtr<CHalfApp> spHalfApp; + wil::com_ptr_nothrow<ITestResource> spTestResource; + GUID ResourceGUID; + CComBSTR szResourceName; + + // Create HalfApp + spHalfApp.Attach(new CHalfApp(descriptor)); + if (!VERIFY_IS_NOT_NULL(spHalfApp)) { + hr = E_OUTOFMEMORY; + return hr; + } + + // Create PinTestResource + if (!VERIFY_SUCCEEDED(hr = CoCreateGuid(&ResourceGUID))) { + return hr; + } + if (!VERIFY_SUCCEEDED(hr = CPinTestResource::CreateInstance( + spHalfApp, ResourceGUID, &spTestResource))) { + return hr; + } + + // Add to resource list + spHalfApp.Detach(); + if (!VERIFY_SUCCEEDED(hr = resourceList.Add(spTestResource.get()))) { + return hr; + } + if (!VERIFY_SUCCEEDED(hr = spTestResource->GetValue(CComBSTR(TestResourceProperty::c_szName), &szResourceName))) { + return hr; + } + Log::Comment(String().Format(L"Test Resource (%s) added", (PCWSTR)szResourceName)); + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetProcessingModesForConnector +// +// For host/keyword detector pin, processing mode info is cached in property store and can be directly read. For offload +// pin, query for ks processing mode property. For loopback pin, it doesn't support any processing modes, return GUID_NULL. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetProcessingModesForConnector +( + IMMDevice* pDevice, + UINT uConnectorId, + EndpointConnectorType eConnectorType, + ULONG *pCount, + AUDIO_SIGNALPROCESSINGMODE **ppModes +) +{ + HRESULT hr = S_OK; + + *pCount = 0; + *ppModes = NULL; + + if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { + if (!VERIFY_SUCCEEDED(hr = GetCachedProcessingModes(pDevice, eConnectorType, pCount, ppModes))) { + return hr; + } + } + else if (eConnectorType == eOffloadConnector) { + if (!VERIFY_SUCCEEDED(hr = GetProcessingModes(pDevice, uConnectorId, pCount, ppModes))) { + return hr; + } + } + else if (eConnectorType == eLoopbackConnector) { + // Loopback pin doesn't support any processing modes, so put 1 GUID_NULL + CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE> spModes; + + if (!spModes.Allocate(1)) + return E_OUTOFMEMORY; + + spModes[0] = GUID_NULL; + + *pCount = 1; + *ppModes = spModes.Detach(); + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetDefaultFormatForConnector +// +// Read audio engine device format from property store as default format. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetDefaultFormatForConnector +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + WAVEFORMATEX **ppDefaultFormat +) +{ + HRESULT hr = S_OK; + + *ppDefaultFormat = NULL; + if (!VERIFY_SUCCEEDED(hr = GetCachedDefaultFormat(pDevice, eConnectorType, ppDefaultFormat))){ + return hr; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetSupportedFormatsForConnector +// +// For host/keyword detector pin, supported formats info is cached in property store and can be directly read. For offload +// pin, provide with a predefined list of formats and check whether format is supported. For loopback pin, it matches the format +// of host pin so put 0 format record. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetSupportedFormatRecordsForConnector +( + IMMDevice* pDevice, + UINT uConnectorId, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + STACKWISE_DATAFLOW dataFlow, + ULONG *pCount, + FORMAT_RECORD **ppFormatRecords +) +{ + HRESULT hr = S_OK; + + *pCount = 0; + *ppFormatRecords = NULL; + + if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { + if (!VERIFY_SUCCEEDED(hr = GetCachedSupportedFormatRecords(pDevice, eConnectorType, mode, pCount, ppFormatRecords))) { + return hr; + } + } + else if (eConnectorType == eOffloadConnector) { + if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecords(pDevice, uConnectorId, eConnectorType, mode, dataFlow, pCount, ppFormatRecords))) { + return hr; + } + } + else if (eConnectorType == eLoopbackConnector) { + *pCount = 0; + *ppFormatRecords = NULL; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetPreferredFormatForConnector +// +// If KSPROPERTY_PIN_PROPOSEDATAFORMAT2 is supported, use the proposed format for mode as preferred format for mode. +// Otherwise, use default format as preferred format. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetPreferredFormatForConnector +( + IMMDevice* pDevice, + UINT uConnectorId, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + WAVEFORMATEX **ppPreferredFormat +) +{ + HRESULT hr = S_OK; + wil::unique_cotaskmem_ptr<WAVEFORMATEX> pDefaultFormat; + wil::unique_cotaskmem_ptr<WAVEFORMATEX> pProposedFormat; + + *ppPreferredFormat = NULL; + + + // Get default format for connector + if (!VERIFY_SUCCEEDED(hr = GetDefaultFormatForConnector(pDevice, eConnectorType, wil::out_param(pDefaultFormat)))) { + return hr; + } + + // Return the processing mode specific format proposed by the driver + hr = GetProposedFormatForProcessingMode(pDevice, uConnectorId, mode, wil::out_param(pProposedFormat)); + if (hr == S_OK) { + CloneWaveFormat(pProposedFormat.get(), ppPreferredFormat); + } + else { + CloneWaveFormat(pDefaultFormat.get(), ppPreferredFormat); + hr = S_OK; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetPreferredFormatPeriodicityCharacteristicsForConnector +// +// Get periodicity characteristics for format. For host/keyword detector pin, periodicity info is cached along with format +// in property store and can be searched from supported format list. For offload pin, it should be calculate from +// DiscoverPeriodicityCharacteristicsForFormat. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetPreferredFormatPeriodicityCharacteristicsForConnector +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + STACKWISE_DATAFLOW dataFlow, + WAVEFORMATEX *pPreferredFormat, + ULONG cFormatRecords, + PFORMAT_RECORD pFormatRecords, + UINT32 *pDefaultPeriodicityInFrames, + UINT32 *pFundamentalPeriodicityInFrames, + UINT32 *pMinPeriodicityInFrames, + UINT32 *pMaxPeriodicityInFrames, + UINT32 *pMaxPeriodicityInFramesExtended +) +{ + HRESULT hr = S_OK; + bool bFormatInList = false; + + for (ULONG i = 0; i < cFormatRecords; i++) { + if (CompareWaveFormat((WAVEFORMATEX *)&pFormatRecords[i].wfxEx, pPreferredFormat)) { + bFormatInList = true; + *pDefaultPeriodicityInFrames = pFormatRecords[i].defaultPeriodInFrames; + *pFundamentalPeriodicityInFrames = pFormatRecords[i].fundamentalPeriodInFrames; + *pMinPeriodicityInFrames = pFormatRecords[i].minPeriodInFrames; + *pMaxPeriodicityInFrames = pFormatRecords[i].maxPeriodInFrames; + *pMaxPeriodicityInFramesExtended = pFormatRecords[i].maxPeriodInFramesExtended; + break; + } + } + + if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { + if (!VERIFY_IS_TRUE(bFormatInList)) { + hr = E_NOTFOUND; + return hr; + } + } + else if (eConnectorType == eOffloadConnector || eConnectorType == eLoopbackConnector) { + if (!VERIFY_SUCCEEDED(hr = DiscoverPeriodicityCharacteristicsForFormat(pDevice, eConnectorType, mode, pPreferredFormat, dataFlow, pDefaultPeriodicityInFrames, pFundamentalPeriodicityInFrames, pMinPeriodicityInFrames, pMaxPeriodicityInFrames, pMaxPeriodicityInFramesExtended))) { + return hr; + } + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// AddTestResourceForConnector +// +// For each connector, read connector id, get all processing modes, identify default and preferred format for each mode +// and also enumerate a list of supported formats for each mode. Store all the infos in test resource. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT AddTestResourceForConnector +( + ResourceList& resourceList, + LPWSTR deviceId, + LPWSTR deviceName, + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + STACKWISE_DATAFLOW dataFlow +) +{ + HRESULT hr = S_OK; + bool bHasConnector = false; + UINT uConnectorId; + ULONG cModes = 0; + CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE> spModes; + AUDIO_SIGNALPROCESSINGMODE mode; + ULONG cFormatRecords = 0; + CComHeapPtr<FORMAT_RECORD> spFormatRecords; + wil::unique_cotaskmem_ptr<WAVEFORMATEX> pPreferredFormat; + UINT32 u32DefaultPeriodicityInFrames; + UINT32 u32FundamentalPeriodicityInFrames; + UINT32 u32MinPeriodicityInFrames; + UINT32 u32MaxPeriodicityInFrames; + UINT32 u32MaxPeriodicityInFramesExtended; + bool isAVStream = false; + bool isBluetooth = false; + bool isSideband = false; + + // Get connector id + if (!VERIFY_SUCCEEDED(hr = GetConnectorId(pDevice, eConnectorType, &bHasConnector, &uConnectorId))) { + return hr; + } + if (!bHasConnector) { + return hr; + } + + Log::Comment(String().Format(L"Adding Test Resource for pin [%u]:", (uConnectorId & PARTID_MASK))); + + + // Get all signal processing modes for connector + if (!VERIFY_SUCCEEDED(hr = GetProcessingModesForConnector(pDevice, uConnectorId, eConnectorType, &cModes, &spModes))) { + return hr; + } + + // Loop through each mode, get preferred format and list of formats and create test resource for each mode + for (ULONG i = 0; i < cModes; i++) { + + mode = spModes[i]; + + if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(pDevice, uConnectorId, eConnectorType, mode, dataFlow, &cFormatRecords, &spFormatRecords))) { + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatForConnector(pDevice, uConnectorId, eConnectorType, mode, wil::out_param(pPreferredFormat)))) { + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatPeriodicityCharacteristicsForConnector(pDevice, eConnectorType, mode, dataFlow, pPreferredFormat.get(), cFormatRecords, spFormatRecords, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) { + return hr; + } + + // Check if audio endpoint is AVStream + if (!VERIFY_SUCCEEDED(hr = IsAVStream(pDevice, &isAVStream))) { + return hr; + } + + // Check if audio endpoint is Bluetooth + if (!VERIFY_SUCCEEDED(hr = IsBluetooth(pDevice, &isBluetooth))) { + return hr; + } + + // Check if audio endpoint is side band + if (!VERIFY_SUCCEEDED(hr = IsSideband(pDevice, &isSideband))) { + return hr; + } + + DeviceDescriptor descriptor = { 0 }; + descriptor.pDevice = pDevice; + descriptor.pwstrAudioEndpointId = deviceId; + descriptor.pwstrAudioEndpointFriendlyName = deviceName; + descriptor.dataFlow = dataFlow; + descriptor.eConnectorType = eConnectorType; + descriptor.uConnectorId = uConnectorId; + descriptor.mode = mode; + descriptor.cModes = cModes; + descriptor.pModes = spModes; + descriptor.cFormatRecords = cFormatRecords; + descriptor.pFormatRecords = spFormatRecords; + descriptor.pPreferredFormat = pPreferredFormat.get(); + descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; + descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; + descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; + descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsAVStream = isAVStream; + descriptor.bIsBluetooth = isBluetooth; + descriptor.bIsSideband = isSideband; + + if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, descriptor))) { + return hr; + } + + spFormatRecords.Free(); + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// AddTestResourcesForDevice +// +// Identify the existence of all pin types and add test resources for each pin +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT AddTestResourcesForDevice +( + ResourceList& resourceList, + LPWSTR deviceId, + LPWSTR deviceName, + STACKWISE_DATAFLOW dataFlow +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator; + wil::com_ptr_nothrow<IMMDevice> spDevice; + + Log::Comment(String().Format(L"Adding Test Resource for Device [%s]:", deviceName)); + + // Read the default device format from the property store + if (!VERIFY_SUCCEEDED(hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator))) { + return hr; + } + if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(deviceId, &spDevice))) { + return hr; + } + + // Identify existence of host pin. Add test resources for host pin. + if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eHostProcessConnector, dataFlow))) { + return hr; + } + + // Identify existence of offload pin. Add test resources for offload pin. + if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eOffloadConnector, dataFlow))) { + return hr; + } + + // Identify existence of loopback pin. Add test resources for loopback pin. + if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eLoopbackConnector, capture ))) { + return hr; + } + + /* + // Identify existence of keyword detector pin. Add test resources for keyword detector pin. + if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eKeywordDetectorConnector, dataFlow))) { + return hr; + } + */ + + return hr; +} + +HRESULT __cdecl BuildResourceList(ResourceList& resourceList) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator; + wil::com_ptr_nothrow<IMMDeviceCollection> spRenderEndpoints; + wil::com_ptr_nothrow<IMMDeviceCollection> spCaptureEndpoints; + wil::com_ptr_nothrow<IMMDevice> spEndpoint; + UINT cRenderDevices = 0; + UINT cCaptureDevices = 0; + UINT i = 0; + wil::unique_cotaskmem_string id; + wil::unique_cotaskmem_string friendlyName; + + SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); + DisableVerifyExceptions disable; + + VERIFY_SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED)); + + Log::Comment(L"In BuildResourceList"); + + // Create IMMDevice Enumerator + VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator)); + + // Enumerate all render endpoints + VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &spRenderEndpoints)); + VERIFY_SUCCEEDED(spRenderEndpoints->GetCount(&cRenderDevices)); + + // Enumerate all capture endpoints + VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &spCaptureEndpoints)); + VERIFY_SUCCEEDED(spCaptureEndpoints->GetCount(&cCaptureDevices)); + + if (!VERIFY_IS_TRUE(cRenderDevices | cCaptureDevices)) + { + hr = E_FAIL; + Log::Comment(L"No device was found!"); + goto Exit; + } + + Log::Comment(String().Format(L"Found %d viable rendering device!", cRenderDevices)); + // Add test resources for render endpoints + if (cRenderDevices) { + for (i = 0; i < cRenderDevices; i++) { + VERIFY_SUCCEEDED(spRenderEndpoints->Item(i, &spEndpoint)); + VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); + VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); + + Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); + VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), render)); + } + } + + Log::Comment(String().Format(L"Found %d viable capture device!", cCaptureDevices)); + // Add test resources for capture endpoints + if (cCaptureDevices) { + for (i = 0; i < cCaptureDevices; i++) { + VERIFY_SUCCEEDED(spCaptureEndpoints->Item(i, &spEndpoint)); + VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); + VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); + + Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); + VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), capture)); + } + } + + Log::Comment(L"Resource enumeration complete."); + Log::Comment(String().Format(L"Enumerated %u resources", resourceList.Count())); + +Exit: + return hr; +} diff --git a/audio/tests/KSPosTst/locallimits.h b/audio/tests/KSPosTst/locallimits.h new file mode 100644 index 00000000..60336eb5 --- /dev/null +++ b/audio/tests/KSPosTst/locallimits.h @@ -0,0 +1,25 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// locallimits.h +// +// Abstract: +// +// defines the tolerances for WHQL failures for this test app +// +// ------------------------------------------------------------------------------- + +// max latency for rendering or capturing is 20 ms +#define LATENCY_THRESHOLD 20. + +// Real time sampling rates must be within 1% of the theoretical rate, +// per the format, as required by the PC98/PC99 HW Design Guide. +#define SAMPLERATE_THRESHOLD 1.0 + +// audio devices are required to be SAMPLEACCURATE (PC9x HW Design Guide).. +// but for some reason we are making them ms accurate?? +#define JITTER_STD_THRESHOLD 1.0 // 1 ms max allowed for std-deviation +#define JITTER_MAX_THRESHOLD 4.0 // 4 ms max allowed for max deviation diff --git a/audio/tests/KSPosTst/pintest.cpp b/audio/tests/KSPosTst/pintest.cpp new file mode 100644 index 00000000..57e35b07 --- /dev/null +++ b/audio/tests/KSPosTst/pintest.cpp @@ -0,0 +1,518 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// pintest.cpp +// +// Abstract: +// +// Implementation file for test cases +// +// ------------------------------------------------------------------------------- + +#include "PreComp.h" +#include "KsPosTestTaef.h" +#include "tests.h" + +#include <bestfit.h> +#include <cmath> + +#define DEFAULT_PERIODICITY 0 +#define DEFAULT_LATENCY_COEFFICIENT 0 +#define xGetTime g_pKsPosTst->m_pTimer->Proc + +// ------------------------------------ +// Test_DriftAndJitter helper functions +// ------------------------------------ + +HRESULT CollectSampleData +( + CHalfApp* pHalfApp, + PFORMAT_ENTRY pFormatEntry, + HNSTIME requestedPeriodicity +) +{ + DWORD periodicityInMs = (DWORD)ceil(HNSTIME_TO_MILLISECONDS_DOUBLE(requestedPeriodicity)); + + if (!pFormatEntry) + { + ERR(g_pBasicLog, "FAIL: Current Format Entry is NULL."); + return E_INVALIDARG; + } + + PPERF_RESULTS pResults = &pFormatEntry->perfResults; + PPOSITION_SET pPosSet = pResults->argPosSets; + double tStart = 0, tPre = 0, tPost = 0; + UINT i = 0; + UINT64 u64Position; + UINT64 positionHNS; + UINT64 u64Frequency; + double testStartHns; + LARGE_INTEGER liNow; + LARGE_INTEGER liFrequency; + QueryPerformanceFrequency(&liFrequency); + + VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT)); + + // run the adapter sink pin + VERIFY_SUCCEEDED(pHalfApp->StartStream()); + + // Get a HNS reference from the begining of the test so the HNS from system and driver times won't be so large + QueryPerformanceCounter(&liNow); + testStartHns = (double)liNow.QuadPart * HNSTIME_UNITS_PER_SECOND / (double)liFrequency.QuadPart; + + tStart = xGetTime(); + Sleep(DELAY_FOR_STABILIZE); + // Get the stream frequency in order to determine the time offset + VERIFY_SUCCEEDED(pHalfApp->m_pAudioClock->GetFrequency(&u64Frequency)); + + // Start collecting the data + for (i = 0; i < DATA_POINTS;) + { + Sleep(periodicityInMs); + + tPre = xGetTime(); + VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &positionHNS)); + QueryPerformanceCounter(&liNow); + tPost = xGetTime(); + + if (u64Position > 0) + { + // only count this result if the GetPosition and GetTime happened relatively atomically + if ((tPost - tPre) < 1.) + { + double testHNS = (double)liNow.QuadPart * HNSTIME_UNITS_PER_SECOND / (double)liFrequency.QuadPart; + // In both sampling rate and jitter calculation, the time used is in milliseconds, so we can convert it right here + pPosSet[i].testTime = HNSTIME_TO_MILLISECONDS_DOUBLE(testHNS - testStartHns); + pPosSet[i].positionTime = HNSTIME_TO_MILLISECONDS_DOUBLE(positionHNS - testStartHns); + // We store the values as seconds because it is easier to convert to bytes per second, on sampling rate calculation, and milliseconds, on jitter calculation. + pPosSet[i].position = (double)u64Position / (double)u64Frequency; + + // Ignore repeated positions (that means they have the same QPC and should have the same position value) + if ((i > 0) && (pPosSet[i - 1].positionTime == pPosSet[i].positionTime)) + { + // If the position is the same, decrement the count so that sample gets discarded by overwriting it + i--; + WARN(g_pBasicLog, "The same position was reported at %f ms: pos = %f seconds", pPosSet[i].testTime, pPosSet[i].position); + } + i++; + } + else + { + LOG(g_pBasicLog, "GetPosition function too slow"); + } + } + // If a non-zero position has not been reported during one second, break the loop and check if there is enough data + else if ((tPost - tStart) > MILLISECONDS_PER_SECOND) + { + break; + } + + pResults->cPosSets = i; + } + + VERIFY_SUCCEEDED(pHalfApp->StopStream()); + + // Did we get enough data? + VERIFY_IS_TRUE(pResults->cPosSets >= 30); + + return S_OK; +} + +// -------------------------------------------------------------------------------------------------- +// CalculateSampleRate +// -------------------------------------------------------------------------------------------------- +HRESULT CalculateSampleRate +( + PFORMAT_ENTRY pFormatEntry +) +{ + WAVEFORMATEX* pwfx = &pFormatEntry->wfxFormat.Format; + PPERF_RESULTS pResults = &pFormatEntry->perfResults; + + double dError, dErrorPercent; + + // Pair the positions with test HNS time to find the slope + // Alocate memory for this type-specific array + wil::unique_cotaskmem_ptr<DPOINT> pointArray((PDPOINT)CoTaskMemAlloc(DATA_POINTS * sizeof(DPOINT))); + PDPOINT pArray = pointArray.get(); + // Get a pointer to the sample set + PPOSITION_SET posSet = pResults->argPosSets; + for (int i = 0; i < DATA_POINTS; i++) + { + pArray[i].x = posSet[i].positionTime; + pArray[i].y = posSet[i].position * pwfx->nAvgBytesPerSec; // Convert to bytes + } + + ULONG cArray = pResults->cPosSets; // number of points + + // calculate the slope and intercept (bytes per second and latency) + pResults->bytesPerSecond = SLOPE(pArray, cArray); + pResults->dOffset = INTERCEPT(pArray, cArray, pResults->bytesPerSecond); + + // Convert from bytes/ms to bytes/sec + pResults->bytesPerSecond *= MILLISECONDS_PER_SECOND; + + // The offset is used in the jitter calculation and it is used in milliseconds, so we can save it in those units + pResults->dOffset = pResults->dOffset / pResults->bytesPerSecond * MILLISECONDS_PER_SECOND; + + // calculate errors + dError = pResults->bytesPerSecond - (double)pwfx->nAvgBytesPerSec; + dErrorPercent = (dError / (double)pwfx->nAvgBytesPerSec) * 100.0; + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + LOG(g_pBasicLog, "SampleRate calculated from histogram (method of least squares)\n"); + + LOG(g_pBasicLog, "Collected %d data points", pResults->cPosSets); + LOG(g_pBasicLog, "Specified sample rate = %6.3f bytes/sec, %6.3f samples/sec", (double)(pwfx->nBlockAlign * pwfx->nSamplesPerSec), (double)pwfx->nSamplesPerSec); + LOG(g_pBasicLog, "Calculated sample rate = %6.3f bytes/sec, %6.3f samples/sec", pResults->bytesPerSecond, pResults->bytesPerSecond / (double)pwfx->nBlockAlign); + LOG(g_pBasicLog, "Error = %.3f %%", dErrorPercent); + LOG(g_pBasicLog, "Calculated time offset = %f ms", pResults->dOffset); + } + + // evaluate the results + VERIFY_IS_TRUE(abs(dErrorPercent) <= SAMPLERATE_THRESHOLD); + + return S_OK; +} + +// -------------------------------------------------------------------------------------------------- +// CalculateJitter +// -------------------------------------------------------------------------------------------------- +HRESULT CalculateJitter +( + PFORMAT_ENTRY pFormatEntry +) +{ + PPERF_RESULTS pResults = &pFormatEntry->perfResults; + + double dError = 0.0; + double dErrorAve = 0.0; + double dErrorMax = 0.0; + double dStdDeviation = 0.0; + double reportedPosition; + double timeDifference; + ULONG i; + PPOSITION_SET pPosSet = pResults->argPosSets; + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "Reported QPC Compensated Expected"); + LOG(g_pBasicLog, "position adjust position position Error"); + LOG(g_pBasicLog, "(ms) (ms) (ms) (ms) (ms)"); + LOG(g_pBasicLog, "========== ========== ========== ========== =========="); + } + + for (i = 0; i < pResults->cPosSets; i++) + { + // Convert the time offset from seconds to milliseconds + reportedPosition = pPosSet[i].position * MILLISECONDS_PER_SECOND; // milliseconds + + // First, we compensate for the latency of the samples (they were taken 100 ms after stream start) + pPosSet[i].position = reportedPosition - pResults->dOffset; // The offset is already in ms + + // Then, before comparing the expected and actual position, adjust the given sample to get the position corresponding to the test time. + timeDifference = pPosSet[i].testTime - pPosSet[i].positionTime; // milliseconds + // If the difference is positive, the time of the position is behind of what is expected and that position gets increased accordingly. + // If the difference is negative, the time of the position is ahead of what is expected and that position gets decreased accordingly. + // In the rare case when the QPC and time are the same, the difference is zero because we are on the spot and that is the position that should be compared. + pPosSet[i].position += timeDifference; // milliseconds + + dError = pPosSet[i].testTime - pPosSet[i].position; + dErrorAve += dError; + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "%10.2f, %10.2f, %10.2f, %10.2f, %10.2f", reportedPosition, timeDifference, pPosSet[i].position, pPosSet[i].testTime, dError); + } + + if (dError < 0.) + dError = -dError; + if (dError > dErrorMax) + dErrorMax = dError; + } + + dErrorAve /= (double)pResults->cPosSets; + + VERIFY_IS_TRUE((ULONG)dErrorAve < 2); // if this is not true then, there's something wrong with our compensation... + + for (i = 0; i < pResults->cPosSets; i++) + { + dError = pPosSet[i].testTime - pPosSet[i].position; + dStdDeviation += ((dError - dErrorAve) * (dError - dErrorAve)); + } + + dStdDeviation /= (double)pResults->cPosSets; + dStdDeviation = sqrt(dStdDeviation); + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + LOG(g_pBasicLog, "Jitter calculated from histogram (with (samplerate drift and latency) compensation)\n"); + LOG(g_pBasicLog, "Max deviation = %6.3f ms", dErrorMax); + LOG(g_pBasicLog, "Standard deviation = %6.3f ms", dStdDeviation); + } + + // evaluate the results ~~~~~~~~~~~~~ + VERIFY_IS_TRUE(abs(dErrorMax) < JITTER_MAX_THRESHOLD); + VERIFY_IS_TRUE(abs(dStdDeviation) < JITTER_STD_THRESHOLD); + + return S_OK; +} + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +void Test_DriftAndJitter_Main +( + HNSTIME requestedPeriodicity +) +{ + CHalfApp* pHalfApp = g_pKsPosTst->m_pHalf; + CHalfApp* pHostHalfApp = g_pKsPosTst->m_pHostHalf; + BOOL isLoopbackPin = pHalfApp->m_ConnectorType == eLoopbackConnector; + + // Test will ignore Bluetooth devices until proper thresholds are in place. + if (pHalfApp->m_bIsBluetooth) + { + SKIP(g_pBasicLog, "Test is not supported for Bluetooth pins"); + return; + } + + // If this is a loobpack pin, start the host pin's stream + // This can be done before collecting the samples, since the host pin will keep playing until stopped + if (isLoopbackPin) + { + // Match loopback stream format with host stream format + CloneWaveFormat(pHostHalfApp->m_pCurrentFormat.get(), wil::out_param(pHalfApp->m_pCurrentFormat)); + pHalfApp->m_u32CurrentDefaultPeriodicityInFrames = pHostHalfApp->m_u32CurrentDefaultPeriodicityInFrames; + pHalfApp->m_u32CurrentFundamentalPeriodicityInFrames = pHostHalfApp->m_u32CurrentFundamentalPeriodicityInFrames; + pHalfApp->m_u32CurrentMinPeriodicityInFrames = pHostHalfApp->m_u32CurrentMinPeriodicityInFrames; + pHalfApp->m_u32CurrentMaxPeriodicityInFrames = pHostHalfApp->m_u32CurrentMaxPeriodicityInFrames; + + // Initialize and start host stream + VERIFY_SUCCEEDED(pHostHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHostHalfApp->CreateSineToneDataBuffer(pHostHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHostHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT)); + VERIFY_SUCCEEDED(pHostHalfApp->StartStream()); + } + + FORMAT_ENTRY results; + memset(&results, 0, sizeof(FORMAT_ENTRY)); + results.wfxFormat.Format = *pHalfApp->m_pCurrentFormat.get(); + // Alocate memory for the sample data + wil::unique_cotaskmem_ptr<POSITION_SET> pPositionSet((PPOSITION_SET)CoTaskMemAlloc(DATA_POINTS * sizeof(POSITION_SET))); + results.perfResults.argPosSets = pPositionSet.get(); + + // collect the data ~~~~~~~~~~~~~~~~~ + VERIFY_SUCCEEDED(CollectSampleData(pHalfApp, &results, requestedPeriodicity)); + + // Stop and cleanup the host stream since we are done collecting samples + if (isLoopbackPin) + { + VERIFY_SUCCEEDED(pHostHalfApp->StopStream()); + VERIFY_SUCCEEDED(pHostHalfApp->CleanupStream()); + VERIFY_SUCCEEDED(pHostHalfApp->ReleaseEndpoint()); + VERIFY_SUCCEEDED(pHostHalfApp->ReleaseSineToneDataBuffer()); + } + + // calculate drift ~~~~~~~~~~~~~~~~~~ + VERIFY_SUCCEEDED(CalculateSampleRate(&results)); + + // calculate Jitter ~~~~~~~~~~~~~~~~~ + VERIFY_SUCCEEDED(CalculateJitter(&results)); +} + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +void Test_DriftAndJitter_Default(void) +{ + HNSTIME requestedTime = FRAMES_TO_HNSTIME_DOUBLE(g_pKsPosTst->m_pHalf->m_u32CurrentDefaultPeriodicityInFrames, g_pKsPosTst->m_pHalf->m_pCurrentFormat->nSamplesPerSec); + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "Periodicity: %d frames, %f ms", g_pKsPosTst->m_pHalf->m_u32CurrentDefaultPeriodicityInFrames, HNSTIME_TO_MILLISECONDS_DOUBLE(requestedTime)); + } + Test_DriftAndJitter_Main(requestedTime); +} + +/* + Perform a cycle of creating a pin, starting a stream, stoping and destroying. + + This is done with the idea of putting the driver and audio hardware in an active state + before doing time sensitive tests. +*/ +void ActivateDriver(CHalfApp* pHalfApp) +{ + VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT)); + VERIFY_SUCCEEDED(pHalfApp->StartStream()); + Sleep(100); + VERIFY_SUCCEEDED(pHalfApp->StopStream()); + VERIFY_SUCCEEDED(pHalfApp->CleanupStream()); + VERIFY_SUCCEEDED(pHalfApp->ReleaseEndpoint()); + VERIFY_SUCCEEDED(pHalfApp->ReleaseSineToneDataBuffer()); +} + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +void Test_Latency_Main (HNSTIME requestedPeriodicity) +{ + CHalfApp* pHalfApp = g_pKsPosTst->m_pHalf; + CHalfApp* pHostHalfApp = g_pKsPosTst->m_pHostHalf; + NTSTATUS lTimerResStatus; + BOOL isLoopbackPin = pHalfApp->m_ConnectorType == eLoopbackConnector; + + // Test will ignore Bluetooth devices until proper thresholds are in place. + if (pHalfApp->m_bIsBluetooth) + { + SKIP(g_pBasicLog, "Test is not supported for Bluetooth pins"); + return; + } + + lTimerResStatus = NtSetTimerResolution(timerResolution, TRUE, &actualTimerResolution); + + if (!NT_SUCCESS(lTimerResStatus)) + { + SKIP(g_pBasicLog, "Fail to set timer precision. Skip the test."); + return; + } + + // If it is a loopback pin, get the host pin and start the stream + if (isLoopbackPin) + { + // Match loopback stream format with host stream format + CloneWaveFormat(pHostHalfApp->m_pCurrentFormat.get(), wil::out_param(pHalfApp->m_pCurrentFormat)); + pHalfApp->m_u32CurrentDefaultPeriodicityInFrames = pHostHalfApp->m_u32CurrentDefaultPeriodicityInFrames; + pHalfApp->m_u32CurrentFundamentalPeriodicityInFrames = pHostHalfApp->m_u32CurrentFundamentalPeriodicityInFrames; + pHalfApp->m_u32CurrentMinPeriodicityInFrames = pHostHalfApp->m_u32CurrentMinPeriodicityInFrames; + pHalfApp->m_u32CurrentMaxPeriodicityInFrames = pHostHalfApp->m_u32CurrentMaxPeriodicityInFrames; + + // Initialize and start host stream + VERIFY_SUCCEEDED(pHostHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHostHalfApp->CreateSineToneDataBuffer(pHostHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHostHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT)); + VERIFY_SUCCEEDED(pHostHalfApp->StartStream()); + } + + // Perform a stream cycle to make sure the driver is on a good state + ActivateDriver(pHalfApp); + + // Initialize the stream + VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT)); + + UINT64 u64Position = 0; + UINT64 driverHns = 0; + + double tPosPre = 0.0; + double tPosPost = 0.0; + + LARGE_INTEGER liFrequency = { 0 }; + LARGE_INTEGER timeStamp = { 0 }; + QueryPerformanceFrequency(&liFrequency); + + // mark the time + double tRunPre = xGetTime(); + // Start the stream + VERIFY_SUCCEEDED(pHalfApp->StartStream()); + // mark the time + double tRunPost = xGetTime(); + + LOG(g_pBasicLog, "Starting the stream took %.03f ms", (tRunPost - tRunPre)); + + // loop until we get a non-zero position + while (u64Position == 0) + { + Sleep(1); + // timer before position call + tPosPre = xGetTime(); + VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &driverHns)); + QueryPerformanceCounter(&timeStamp); + // timer after position call + tPosPost = xGetTime(); + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "Testing first non-zero position reported: %u samples, %u HNS at %.03f ms", u64Position, driverHns, tPosPost - tRunPre); + } + + // Fail if the cursor does not move within 500 ms + VERIFY_IS_TRUE((tPosPost - tRunPost) < 500.0); + } + + // Before stopping, get the frequency of the stream + UINT64 u64Frequency; + VERIFY_SUCCEEDED(pHalfApp->m_pAudioClock->GetFrequency(&u64Frequency)); + + // the uncertainty in time is the max possible value minus the min possible value divided by 2 (for +/- purposes) + double tMax = tPosPost - tRunPre; + double tMin = tPosPre - tRunPost; + double tAve = (tMax + tMin) / 2.0; + double tUncertainty = (tMax - tMin) / 2.0; + + // Convert the stream position to an offset of milliseconds + double tOffset = MILLISECONDS_PER_SECOND * (double)u64Position / (double)u64Frequency; + + // Calculate the difference in time of the position time versus the test time + UINT64 testHns = 10000000 * timeStamp.QuadPart / liFrequency.QuadPart; + INT64 timeDiff = testHns - driverHns; + + // Compensate the positon to take this difference into account + tOffset += (double)timeDiff / HNSTIME_UNITS_PER_MILLISECOND; + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "Times (HNS): %u, %u", testHns, driverHns); + LOG(g_pBasicLog, "Time difference: %d HNS (%f ms)", timeDiff, (double)timeDiff / HNSTIME_UNITS_PER_MILLISECOND); + LOG(g_pBasicLog, "First non-zero position = %u at %g ms", u64Position, tAve); + LOG(g_pBasicLog, "Time offset / max time: %f ms / %f ms", tOffset, tMax); + LOG(g_pBasicLog, "Frequency: %lu", u64Frequency); + } + + double tLatency; + tLatency = tAve - tOffset; + if (tLatency < 0) + { + // This means that the position is between the average and max times, which should be OK (as long as it is not above the max) + tLatency *= -1; + LOG(g_pBasicLog, "The position is greater than the average time"); + } + LOG(g_pBasicLog, "Adjusted latency = %.03f ms (+/- %.3f ms)", tLatency, tUncertainty); + + // if the reported position is outside the max time for this call (see tUncertainty calculation above), then fail + // temp fix until we know how far can be the data in front of the + // real time because of buffering + VERIFY_IS_FALSE(tOffset > tMax); + + // Verify that the latency is within the threshold + VERIFY_IS_TRUE(tLatency <= LATENCY_THRESHOLD); // ms + + // Stop the endpoint + VERIFY_SUCCEEDED(pHalfApp->StopStream()); + // If this was a loopback pin, stop the host stream also + if (isLoopbackPin) + { + VERIFY_SUCCEEDED(pHostHalfApp->StopStream()); + VERIFY_SUCCEEDED(pHostHalfApp->CleanupStream()); + VERIFY_SUCCEEDED(pHostHalfApp->ReleaseEndpoint()); + VERIFY_SUCCEEDED(pHostHalfApp->ReleaseSineToneDataBuffer()); + } + + // Turn off the higher resolution timer + NtSetTimerResolution(0, FALSE, &actualTimerResolution); +} + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +void Test_Latency_Default(void) +{ + HNSTIME requestedTime = FRAMES_TO_HNSTIME_DOUBLE(g_pKsPosTst->m_pHalf->m_u32CurrentDefaultPeriodicityInFrames, g_pKsPosTst->m_pHalf->m_pCurrentFormat->nSamplesPerSec); + LOG(g_pBasicLog, "Periodicity: %d frames, %f ms", g_pKsPosTst->m_pHalf->m_u32CurrentDefaultPeriodicityInFrames, HNSTIME_TO_MILLISECONDS_DOUBLE(requestedTime)); + Test_Latency_Main(requestedTime); +} diff --git a/audio/tests/KSPosTst/tests.h b/audio/tests/KSPosTst/tests.h new file mode 100644 index 00000000..ec0a68cc --- /dev/null +++ b/audio/tests/KSPosTst/tests.h @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// tests.h +// +// Abstract: +// +// Header for testcases +// +// ------------------------------------------------------------------------------- +#pragma once + +void Test_DriftAndJitter_Default(void); + +void Test_Latency_Default(void); diff --git a/audio/tests/KSPosTst/timetest.cpp b/audio/tests/KSPosTst/timetest.cpp new file mode 100644 index 00000000..d8026716 --- /dev/null +++ b/audio/tests/KSPosTst/timetest.cpp @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// timetest.cpp +// +// Abstract: +// +// Functions to precisely measure time. +// +// ------------------------------------------------------------------------------- + +#include "PreComp.h" +#include <math.h> + +// ------------------------------------------------------------------------------ +// returns millisecs +double tpQPC (void) +{ + static LARGE_INTEGER liFrequency = { 0 }; + static LARGE_INTEGER liStart = { 0 }; + static bool bInitialized = false; + + if (!bInitialized) + { + if (!QueryPerformanceFrequency(&liFrequency)) + { + throw ("QueryPerformanceFrequency failed"); + } + + if (!QueryPerformanceCounter(&liStart)) + { + throw ("QueryPerformanceCounter failed"); + } + + bInitialized = true; + } + + LARGE_INTEGER liNow = { 0 }; + + if (!QueryPerformanceCounter(&liNow)) + { + throw ("QueryPerformanceCounter failed"); + } + + // milliseconds since test start + return 1000.0 * (liNow.QuadPart - liStart.QuadPart) / liFrequency.QuadPart; +} diff --git a/audio/tests/wavetest/HalfApp.cpp b/audio/tests/PinResourceHelpers/HalfApp.cpp index 14a15c08..83c9c8f3 100644 --- a/audio/tests/wavetest/HalfApp.cpp +++ b/audio/tests/PinResourceHelpers/HalfApp.cpp @@ -12,15 +12,16 @@ // // ------------------------------------------------------------------------------- -#include "PreComp.h" -#include "WaveTestTaef.h" -#include "HalfApp.h" -#include "TestResourceHelper.h" -#include "PropertyHelper.h" -#include <dithering.h> -#include <signal-source.h> -#include <sine-signal-source.h> -#include <dvmodule.h> + + + + + + + + + + HRESULT CHalfApp::InitializeEndpoint @@ -114,6 +115,9 @@ CHalfApp::InitializeEndpoint if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint.query_to(&m_pAudioEndpointRT))) { return hr; } + if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint.query_to(&m_pAudioClock))) { + return hr; + } return hr; } @@ -134,6 +138,7 @@ CHalfApp::ReleaseEndpoint m_pAudioEndpointControl.reset(); m_pAudioEndpoint.reset(); m_pAudioEndpointRT.reset(); + m_pAudioClock.reset(); return hr; } @@ -380,6 +385,7 @@ CHalfApp::GetSecondHalfApp UINT32 u32MinPeriodicityInFrames; UINT32 u32MaxPeriodicityInFrames; UINT32 u32MaxPeriodicityInFramesExtended; + DeviceDescriptor descriptor = { 0 }; if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(m_pDevice.get(), m_uConnectorId, m_ConnectorType, secondMode, m_DataFlow, &cFormatRecords, &spFormatRecords))) { return hr; @@ -393,7 +399,26 @@ CHalfApp::GetSecondHalfApp return hr; } - *ppSecondHalfApp = new CHalfApp(m_pDevice.get(), m_pwstrDeviceId.get(), m_pwstrDeviceFriendlyName.get(), m_DataFlow, m_ConnectorType, m_uConnectorId, secondMode, m_cModes, m_pModes, cFormatRecords, spFormatRecords, pPreferredFormat.get(), u32DefaultPeriodicityInFrames, u32FundamentalPeriodicityInFrames, u32MinPeriodicityInFrames, u32MaxPeriodicityInFrames); + descriptor.pDevice = m_pDevice.get(); + descriptor.pwstrAudioEndpointId = m_pwstrDeviceId.get(); + descriptor.pwstrAudioEndpointFriendlyName = m_pwstrDeviceFriendlyName.get(); + descriptor.dataFlow = m_DataFlow; + descriptor.eConnectorType = m_ConnectorType; + descriptor.uConnectorId = m_uConnectorId; + descriptor.mode = secondMode; + descriptor.cModes = m_cModes; + descriptor.pModes = m_pModes; + descriptor.cFormatRecords = cFormatRecords; + descriptor.pFormatRecords = spFormatRecords; + descriptor.pPreferredFormat = pPreferredFormat.get(); + descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; + descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; + descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; + descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsAVStream = m_bIsAVStream; + descriptor.bIsBluetooth = m_bIsBluetooth; + descriptor.bIsSideband = m_bIsSideband; + *ppSecondHalfApp = new CHalfApp(descriptor); if (*ppSecondHalfApp == NULL) { hr = E_OUTOFMEMORY; return hr; @@ -432,6 +457,7 @@ CHalfApp::GetHostHalfApp UINT32 u32MinPeriodicityInFrames; UINT32 u32MaxPeriodicityInFrames; UINT32 u32MaxPeriodicityInFramesExtended; + DeviceDescriptor descriptor = { 0 }; // It is only used when testing on loopback pin. When preparing loopback pin, we also need prepare the host pin. VERIFY_IS_TRUE(m_ConnectorType == eLoopbackConnector); @@ -474,7 +500,26 @@ CHalfApp::GetHostHalfApp return hr; } - *ppHostHalfApp = new CHalfApp(m_pDevice.get(), m_pwstrDeviceId.get(), m_pwstrDeviceFriendlyName.get(), render, eHostProcessConnector, uConnectorId, mode, cModes, spModes, cFormatRecords, spFormatRecords, pPreferredFormat.get(), u32DefaultPeriodicityInFrames, u32FundamentalPeriodicityInFrames, u32MinPeriodicityInFrames, u32MaxPeriodicityInFrames); + descriptor.pDevice = m_pDevice.get(); + descriptor.pwstrAudioEndpointId = m_pwstrDeviceId.get(); + descriptor.pwstrAudioEndpointFriendlyName = m_pwstrDeviceFriendlyName.get(); + descriptor.dataFlow = render; + descriptor.eConnectorType = eHostProcessConnector; + descriptor.uConnectorId = uConnectorId; + descriptor.mode = mode; + descriptor.cModes = cModes; + descriptor.pModes = spModes; + descriptor.cFormatRecords = cFormatRecords; + descriptor.pFormatRecords = spFormatRecords; + descriptor.pPreferredFormat = pPreferredFormat.get(); + descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; + descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; + descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; + descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsAVStream = m_bIsAVStream; + descriptor.bIsBluetooth = m_bIsBluetooth; + descriptor.bIsSideband = m_bIsSideband; + *ppHostHalfApp = new CHalfApp(descriptor); if (*ppHostHalfApp == NULL) { hr = E_OUTOFMEMORY; return hr; @@ -987,13 +1032,9 @@ inline void CHalfApp::CancelTimer(HANDLE timer) HRESULT CHalfApp::GetPosition(UINT64* pu64Position, UINT64* pu64hnsQPCPosition) { HRESULT hr = S_OK; - wil::com_ptr_nothrow<IAudioClock> spAudioClock; auto lock = m_CritSec.lock(); - if (!VERIFY_SUCCEEDED(m_pAudioDeviceEndpoint.query_to(&spAudioClock))) { - return hr; - } - if (!VERIFY_SUCCEEDED(spAudioClock->GetPosition(pu64Position, pu64hnsQPCPosition))) { + if (!VERIFY_SUCCEEDED(m_pAudioClock->GetPosition(pu64Position, pu64hnsQPCPosition))) { return hr; } diff --git a/audio/tests/PinResourceHelpers/PreComp.h b/audio/tests/PinResourceHelpers/PreComp.h new file mode 100644 index 00000000..85fc4f83 --- /dev/null +++ b/audio/tests/PinResourceHelpers/PreComp.h @@ -0,0 +1,143 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft. All rights reserved. +// +// File Name: +// +// PreComp.h +// +// Abstract: +// +// Precompiled common headers +// +// ------------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// Define custom test resource properties +namespace WEX { + namespace TestExecution { + namespace TestResourceProperty + { + static const wchar_t c_szMode[] = L"Mode"; + static const wchar_t c_szPin[] = L"Pin"; + } + } +} + +// ---------------------------------------------------------- +// Parameters for test sine tone +#define TEST_AMPLITUDE 1.0f +#define TEST_FREQUENCY 200.0f + +// ---------------------------------------------------------- +// Predefined format list for offload streaming +#define COUNT_FORMATS 4 + +static WAVEFORMATEXTENSIBLE ListofFormats[COUNT_FORMATS] = +{ + // 1 + { + // Format + { + WAVE_FORMAT_EXTENSIBLE, // wFormatTag + 2, // nChannels + 44100, // nSamplesPerSec + 176400, // nAvgBytesPerSec + 4, // nBlockAlign + 16, // wBitsPerSample + 22 // cbSize + }, + // Samples + { + 16 // wValidBitsPerSample + }, + KSAUDIO_SPEAKER_STEREO, // dwChannelMask + KSDATAFORMAT_SUBTYPE_PCM // SubFormat + }, + // 2 + { + // Format + { + WAVE_FORMAT_EXTENSIBLE, // wFormatTag + 2, // nChannels + 48000, // nSamplesPerSec + 192000, // nAvgBytesPerSec + 4, // nBlockAlign + 16, // wBitsPerSample + 22 // cbSize + }, + // Samples + { + 16 // wValidBitsPerSample + }, + KSAUDIO_SPEAKER_STEREO, // dwChannelMask + KSDATAFORMAT_SUBTYPE_PCM // SubFormat + }, + // 3 + { + // Format + { + WAVE_FORMAT_EXTENSIBLE, // wFormatTag + 2, // nChannels + 88200, // nSamplesPerSec + 352800, // nAvgBytesPerSec + 4, // nBlockAlign + 16, // wBitsPerSample + 22 // cbSize + }, + // Samples + { + 16 // wValidBitsPerSample + }, + KSAUDIO_SPEAKER_STEREO, // dwChannelMask + KSDATAFORMAT_SUBTYPE_PCM // SubFormat + }, + // 4 + { + // Format + { + WAVE_FORMAT_EXTENSIBLE, // wFormatTag + 2, // nChannels + 96000, // nSamplesPerSec + 384000, // nAvgBytesPerSec + 4, // nBlockAlign + 16, // wBitsPerSample + 22 // cbSize + }, + // Samples + { + 16 // wValidBitsPerSample + }, + KSAUDIO_SPEAKER_STEREO, // dwChannelMask + KSDATAFORMAT_SUBTYPE_PCM // SubFormat + }, +}; diff --git a/audio/tests/wavetest/PropertyHelper.cpp b/audio/tests/PinResourceHelpers/PropertyHelper.cpp index fb60d3cc..6e85e53e 100644 --- a/audio/tests/wavetest/PropertyHelper.cpp +++ b/audio/tests/PinResourceHelpers/PropertyHelper.cpp @@ -4,7 +4,7 @@ // // Module Name: // -// EndpointPropertyHelpers.cpp +// PropertyHelper.cpp // // Abstract: // @@ -14,7 +14,8 @@ #include "PreComp.h" #include <AVEndpointKeys.h> #include <Functiondiscoverykeys_devpkey.h> -#include "PropertyHelper.h" +#include <devpkey.h> +#include <PropertyHelper.h> /////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -722,6 +723,8 @@ HRESULT IsFormatSupported return hr; } + CoTaskMemFree(pKsRequestedFormat); + return hr; } @@ -1093,3 +1096,168 @@ HRESULT GetAvailiablePinInstanceCount return hr; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// IsAVStream +// +// Check if audio device is AVStream +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT IsAVStream +( + IMMDevice* pDevice, + bool* pIsAVStream +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IMMDevice> spDevnodeDevice; + wil::com_ptr_nothrow<IPropertyStore> spPnpProperties; + wil::unique_prop_variant varDeviceService; + + *pIsAVStream = false; + + // Find the associated PnP device + if (!VERIFY_SUCCEEDED(hr = GetPnpDevnodeFromMMDevice(pDevice, &spDevnodeDevice))) { + return hr; + } + + // Open pnp device property store + if (!VERIFY_SUCCEEDED(hr = spDevnodeDevice->OpenPropertyStore(STGM_READ, &spPnpProperties))) { + return hr; + } + + // Read the DEVPKEY_Device_Service + if (!VERIFY_SUCCEEDED(hr = spPnpProperties->GetValue((REFPROPERTYKEY)DEVPKEY_Device_Service, &varDeviceService))) { + return hr; + } + + if (!VERIFY_IS_TRUE(varDeviceService.vt == VT_LPWSTR && varDeviceService.pwszVal != nullptr)) { + hr = E_FAIL; + return hr; + } + + if (0 == _wcsicmp(varDeviceService.pwszVal, L"usbaudio") || 0 == _wcsicmp(varDeviceService.pwszVal, L"BthHFAud") || 0 == _wcsicmp(varDeviceService.pwszVal, L"BthA2dp")) { + *pIsAVStream = true; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// IsBluetooth +// +// Check if audio device is Bluetooth +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT IsBluetooth +( + IMMDevice* pDevice, + bool* pIsBluetooth +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IPropertyStore> spPropertyStore; + wil::unique_prop_variant varIsBluetooth; + + *pIsBluetooth = false; + + // Open pnp device property store + if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) { + return hr; + } + + // Read the PKEY_Endpoint_IsBluetooth + hr = spPropertyStore->GetValue((REFPROPERTYKEY)PKEY_Endpoint_IsBluetooth, &varIsBluetooth); + if (hr != S_OK) { + return hr; + } + + if (varIsBluetooth.vt == VT_BOOL) + { + *pIsBluetooth = varIsBluetooth.boolVal == VARIANT_TRUE ? true : false; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// IsSideband +// +// Check if audio device is side band +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT IsSideband +( + IMMDevice* pDevice, + bool* pIsSideband +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IPropertyStore> spPropertyStore; + wil::unique_prop_variant varIsSideband; + + *pIsSideband = false; + + // Open pnp device property store + if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) { + return hr; + } + + // Read the PKEY_Endpoint_IsBluetooth + hr = spPropertyStore->GetValue((REFPROPERTYKEY)PKEY_Endpoint_IsSideband, &varIsSideband); + if (hr != S_OK) { + return hr; + } + + if (varIsSideband.vt == VT_BOOL) + { + *pIsSideband = varIsSideband.boolVal == VARIANT_TRUE ? true : false; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetPnpDevnodeFromMMDeivce +// +// Get pnp devnode from mm device +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetPnpDevnodeFromMMDevice +( + IMMDevice* pDevice, + IMMDevice** pDevnodeDevice +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator; + wil::com_ptr_nothrow<IPropertyStore> spProps; + wil::com_ptr_nothrow<IMMDevice> spDevnodeAsDevice; + wil::com_ptr_nothrow<IPropertyStore> spDevnodeProps; + wil::unique_prop_variant varDevnode; + + // Create an enumerator + if (!VERIFY_SUCCEEDED(hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator))) { + return hr; + } + + // Read the PKEY_Endpoint_Devnode + if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spProps))) { + return hr; + } + if (!VERIFY_SUCCEEDED(hr = spProps->GetValue(PKEY_Endpoint_Devnode, &varDevnode))) { + return hr; + } + + // Get the devnode as an IMMDevice + if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(varDevnode.pwszVal, &spDevnodeAsDevice))) { + return hr; + } + *pDevnodeDevice = spDevnodeAsDevice.detach(); + + return hr; +} diff --git a/audio/tests/wavetest/TestResource.cpp b/audio/tests/PinResourceHelpers/TestResource.cpp index 67f3ca79..f3762b84 100644 --- a/audio/tests/wavetest/TestResource.cpp +++ b/audio/tests/PinResourceHelpers/TestResource.cpp @@ -13,8 +13,8 @@ // ------------------------------------------------------------------------------- #include "PreComp.h" -#include "HalfApp.h" -#include "TestResource.h" +#include <HalfApp.h> +#include <TestResource.h> using namespace WEX::Logging; using namespace WEX::TestExecution; @@ -328,4 +328,4 @@ LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType) default: return L"UNKNOWN"; } -}
\ No newline at end of file +} diff --git a/audio/tests/wavetest/TestResourceHelper.h b/audio/tests/PinResourceHelpers/TestResourceHelper.h index 24bae634..08fafa23 100644 --- a/audio/tests/wavetest/TestResourceHelper.h +++ b/audio/tests/PinResourceHelpers/TestResourceHelper.h @@ -13,6 +13,8 @@ // ------------------------------------------------------------------------------- #pragma once +#include <HalfApp.h> + // ---------------------------------------------------------------------- HRESULT GetProcessingModesForConnector ( @@ -68,4 +70,4 @@ HRESULT GetSupportedFormatRecordsForConnector STACKWISE_DATAFLOW dataFlow, ULONG* pCount, FORMAT_RECORD** ppFormatRecords -);
\ No newline at end of file +); diff --git a/audio/tests/wavetest/HalfApp.h b/audio/tests/wavetest/HalfApp.h deleted file mode 100644 index 1bf14de9..00000000 --- a/audio/tests/wavetest/HalfApp.h +++ /dev/null @@ -1,195 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// File Name: -// -// HalfApp.h -// -// Abstract: -// -// CHalfApp declarations -// -// ------------------------------------------------------------------------------- -#pragma once - -#define BUF_LEN_IN_MS 1000 // 1 second - -class CHalfApp -{ -public: - // Basic pin infos - wil::com_ptr_nothrow<IMMDevice> m_pDevice; - wil::unique_cotaskmem_string m_pwstrDeviceId; - wil::unique_cotaskmem_string m_pwstrDeviceFriendlyName; - STACKWISE_DATAFLOW m_DataFlow; - EndpointConnectorType m_ConnectorType; - UINT m_uConnectorId; - GUID m_Mode; - ULONG m_cModes; - GUID* m_pModes; // All processing modes - - // Formats - wil::unique_cotaskmem_ptr<WAVEFORMATEX> m_pPreferredFormat; - UINT32 m_u32DefaultPeriodicityInFrames; - UINT32 m_u32FundamentalPeriodicityInFrames; - UINT32 m_u32MinPeriodicityInFrames; - UINT32 m_u32MaxPeriodicityInFrames; - - PFORMAT_RECORD m_pFormatRecords; - ULONG m_cFormatRecords; - - wil::unique_cotaskmem_ptr<WAVEFORMATEX> m_pCurrentFormat; - UINT32 m_u32CurrentDefaultPeriodicityInFrames; - UINT32 m_u32CurrentFundamentalPeriodicityInFrames; - UINT32 m_u32CurrentMinPeriodicityInFrames; - UINT32 m_u32CurrentMaxPeriodicityInFrames; - - // Audio endpoint interfaces - wil::com_ptr_nothrow<IAudioDeviceEndpoint> m_pAudioDeviceEndpoint; - wil::com_ptr_nothrow<IAudioEndpoint> m_pAudioEndpoint; - wil::com_ptr_nothrow<IAudioEndpointControl> m_pAudioEndpointControl; - wil::com_ptr_nothrow<IAudioEndpointRT> m_pAudioEndpointRT; - - // Data buffer - wil::unique_hlocal_ptr<BYTE> m_pbSineToneDataBuffer; - DWORD m_dwSineToneDataBufferSize; - DWORD m_dwSineToneDataBufferPosition; - - // Stream - HNSTIME m_hnsPeriod = 0; - FLOAT32 m_f32EndpointFrameRate = 0.F; - HNSTIME m_hnsEndpointLatency = 0; - bool m_bIsEventCapable = false; - bool m_bStreamInitialized = false; - volatile bool m_bStreamThreadTerminate = false; - bool m_bYieldActive = false; - bool m_bIsBackupTimerRequired = true; - - wil::unique_handle m_hStreamThread; - - LPTHREAD_START_ROUTINE m_pStreamRoutine = nullptr; - wil::unique_event_nothrow m_hProcessThreadStartedEvent; - wil::unique_event_nothrow m_hTerminate; - wil::unique_event_nothrow m_hEndpointBufferCompleteEvent; - wil::unique_handle m_hTimer; - - wil::critical_section m_CritSec; - - typedef enum - { - ET_DO_NOTHING = 0x00, - ET_TERMINATE = 0x01, - ET_OBJECT_BUFFER_COMPLETE = 0x02, - ET_CLIENT_RELEASE_BUFFER = 0x04, - ET_TIMER = 0x08, - ET_PAUSE_PUMP = 0x10, - ET_RESUME_PUMP = 0x20, - } TEventType; - - CHalfApp( - _In_ IMMDevice* pDevice, - _In_ LPWSTR pwstrAudioEndpointId, - _In_ LPWSTR pwstrAudioEndpointFriendlyName, - _In_ STACKWISE_DATAFLOW dataFlow, - _In_ EndpointConnectorType eConnectorType, - _In_ UINT uConnectorId, - _In_ GUID mode, - _In_ ULONG cModes, - _In_ GUID* pModes, - _In_ ULONG cFormatRecords, - _In_ PFORMAT_RECORD pFormatRecords, - _In_ PWAVEFORMATEX pPreferredFormat, - _In_ UINT32 u32DefaultPeriodicityInFrames, - _In_ UINT32 u32FundamentalPeriodicityInFrames, - _In_ UINT32 u32MinPeriodicityInFrames, - _In_ UINT32 u32MaxPeriodicityInFrames - ) - : m_DataFlow(dataFlow) - , m_ConnectorType(eConnectorType) - , m_uConnectorId(uConnectorId) - , m_cModes(cModes) - , m_cFormatRecords(cFormatRecords) - , m_u32DefaultPeriodicityInFrames(u32DefaultPeriodicityInFrames) - , m_u32FundamentalPeriodicityInFrames(u32FundamentalPeriodicityInFrames) - , m_u32MinPeriodicityInFrames(u32MinPeriodicityInFrames) - , m_u32MaxPeriodicityInFrames(u32MaxPeriodicityInFrames) - , m_u32CurrentDefaultPeriodicityInFrames(u32DefaultPeriodicityInFrames) - , m_u32CurrentFundamentalPeriodicityInFrames(u32FundamentalPeriodicityInFrames) - , m_u32CurrentMinPeriodicityInFrames(u32MinPeriodicityInFrames) - , m_u32CurrentMaxPeriodicityInFrames(u32MaxPeriodicityInFrames) - { - m_pDevice = pDevice; - m_pAudioDeviceEndpoint = nullptr; - m_pAudioEndpointControl = nullptr; - - m_pwstrDeviceId = wil::make_cotaskmem_string_nothrow(pwstrAudioEndpointId, wcslen(pwstrAudioEndpointId)); - m_pwstrDeviceFriendlyName = wil::make_cotaskmem_string_nothrow(pwstrAudioEndpointFriendlyName, wcslen(pwstrAudioEndpointFriendlyName)); - m_Mode = mode; - - m_pModes = new GUID[cModes]; - memcpy(m_pModes, pModes, sizeof(GUID)*cModes); - m_pFormatRecords = new FORMAT_RECORD[cFormatRecords]; - memcpy(m_pFormatRecords, pFormatRecords, sizeof(FORMAT_RECORD)*cFormatRecords); - - CloneWaveFormat(pPreferredFormat, wil::out_param(m_pPreferredFormat)); - CloneWaveFormat(pPreferredFormat, wil::out_param(m_pCurrentFormat)); // Use preferred format as current format - } - - ~CHalfApp(void) - { - if (m_pModes) - delete[] m_pModes; - if (m_pFormatRecords) - delete[] m_pFormatRecords; - } - - // Audio endpoint interfaces - HRESULT InitializeEndpoint(); - HRESULT ReleaseEndpoint(); - HRESULT StartEndpoint(); - HRESULT StopEndpoint(); - HRESULT ResetEndpoint(); - - // Sine tone data buffer - HRESULT CreateSineToneDataBuffer(WAVEFORMATEX* pWfx); - HRESULT ReleaseSineToneDataBuffer(); - - - - - // Stream - HRESULT InitializeAndSetBuffer(HNSTIME hnsPeriod, UINT32 u32LatencyCoefficient); - HRESULT InitializeStream(HNSTIME requestedPeriodicity, UINT32 u32LatencyCoefficient); - HRESULT CleanupStream(); - HRESULT StartStream(); - HRESULT StopStream(); - static DWORD CALLBACK RenderStreamRoutine(PVOID lpParameter); - static DWORD CALLBACK CaptureStreamRoutine(PVOID lpParameter); - void SignalAndWaitForThread(); - - - - HRESULT SetTimer(HANDLE timer, HNSTIME timeDuration, bool fireImmediatley); - void CancelTimer(HANDLE timer); - HRESULT GetPosition(UINT64* pu64Position, UINT64* pu64hnsQPCPosition); - - // Multiple pin instances - HRESULT GetCurrentAvailiablePinInstanceCount(UINT32* pAvailablePinInstanceCount); - HRESULT GetSecondHalfApp(AUDIO_SIGNALPROCESSINGMODE secondMode, CHalfApp** ppSecondHalfApp); - - // Only for loopback testcases - HRESULT GetHostHalfApp(CHalfApp** ppHostHalfApp); -}; - -// {571BF784-A9D6-420A-BCA0-C579A8710236} -DEFINE_GUID(IID_IHalfAppContainer, - 0x571bf784, 0xa9d6, 0x420a, 0xbc, 0xa0, 0xc5, 0x79, 0xa8, 071, 0x02, 0x36); - -DECLARE_INTERFACE_IID_( -IHalfAppContainer, -IUnknown, -"571BF784-A9D6-420A-BCA0-C579A8710236") -{ - STDMETHOD(GetHalfApp)(THIS_ CHalfApp ** ppHalfApp) PURE; -}; diff --git a/audio/tests/wavetest/PreComp.h b/audio/tests/wavetest/PreComp.h index 2044d6b1..76210090 100644 --- a/audio/tests/wavetest/PreComp.h +++ b/audio/tests/wavetest/PreComp.h @@ -24,13 +24,11 @@ #include <Audioclient.h> #include <mmdeviceapi.h> #include <mmdeviceapip.h> -#include <devicetopology.h> #include <devicetopologyp.h> #include <propvarutil.h> #include <wil\com.h> -#include <winioctl.h> #include <KsLib.h> #include <Waveutil.h> #include <aecommon.h> @@ -47,108 +45,3 @@ #define WARN(log, ...) BasicLogPrintf( log, XWARN, 1, __VA_ARGS__ ) #define BLOCK(log, ...) BasicLogPrintf( log, XBLOCK, 1, __VA_ARGS__ ) #define ERR(log, ...) BasicLogPrintf( log, XFAIL, 1, __VA_ARGS__ ) - -// ------------------------------------------------------------------------------ -// Data flow -enum STACKWISE_DATAFLOW { render, capture }; - -// ------------------------------------------------------------------------------ -// Structs used to hold results of format records -typedef struct -{ - WAVEFORMATEXTENSIBLE wfxEx; - UINT32 fundamentalPeriodInFrames; - UINT32 defaultPeriodInFrames; - UINT32 minPeriodInFrames; - UINT32 maxPeriodInFrames; - UINT32 maxPeriodInFramesExtended; -} FORMAT_RECORD, *PFORMAT_RECORD; - -// ---------------------------------------------------------- -// Parameters for test sine tone -#define TEST_AMPLITUDE 1.0f -#define TEST_FREQUENCY 200.0f - -// ---------------------------------------------------------- -// Predefined format list for offload streaming -#define COUNT_FORMATS 4 - -static WAVEFORMATEXTENSIBLE ListofFormats[COUNT_FORMATS] = -{ - // 1 - { - // Format - { - WAVE_FORMAT_EXTENSIBLE, // wFormatTag - 2, // nChannels - 44100, // nSamplesPerSec - 176400, // nAvgBytesPerSec - 4, // nBlockAlign - 16, // wBitsPerSample - 22 // cbSize - }, - // Samples - { - 16 // wValidBitsPerSample - }, - KSAUDIO_SPEAKER_STEREO, // dwChannelMask - KSDATAFORMAT_SUBTYPE_PCM // SubFormat - }, - // 2 - { - // Format - { - WAVE_FORMAT_EXTENSIBLE, // wFormatTag - 2, // nChannels - 48000, // nSamplesPerSec - 192000, // nAvgBytesPerSec - 4, // nBlockAlign - 16, // wBitsPerSample - 22 // cbSize - }, - // Samples - { - 16 // wValidBitsPerSample - }, - KSAUDIO_SPEAKER_STEREO, // dwChannelMask - KSDATAFORMAT_SUBTYPE_PCM // SubFormat - }, - // 3 - { - // Format - { - WAVE_FORMAT_EXTENSIBLE, // wFormatTag - 2, // nChannels - 88200, // nSamplesPerSec - 352800, // nAvgBytesPerSec - 4, // nBlockAlign - 16, // wBitsPerSample - 22 // cbSize - }, - // Samples - { - 16 // wValidBitsPerSample - }, - KSAUDIO_SPEAKER_STEREO, // dwChannelMask - KSDATAFORMAT_SUBTYPE_PCM // SubFormat - }, - // 4 - { - // Format - { - WAVE_FORMAT_EXTENSIBLE, // wFormatTag - 2, // nChannels - 96000, // nSamplesPerSec - 384000, // nAvgBytesPerSec - 4, // nBlockAlign - 16, // wBitsPerSample - 22 // cbSize - }, - // Samples - { - 16 // wValidBitsPerSample - }, - KSAUDIO_SPEAKER_STEREO, // dwChannelMask - KSDATAFORMAT_SUBTYPE_PCM // SubFormat - }, -};
\ No newline at end of file diff --git a/audio/tests/wavetest/PropertyHelper.h b/audio/tests/wavetest/PropertyHelper.h deleted file mode 100644 index 9f01a9a7..00000000 --- a/audio/tests/wavetest/PropertyHelper.h +++ /dev/null @@ -1,190 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// Module Name: -// -// PropertyHelpers.h -// -// Abstract: -// -// Header for common helpers/defines of property -// -// ------------------------------------------------------------------------------- -#pragma once - -#define kSystemDefaultPeriod 100000 // 10 milliseconds -#define kSystemMinPeriodForNonRTCapableEndpoints 20000 // 2 milliseconds - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ---------------------------------------------------------------------- -HRESULT GetConnectorId -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - bool* hasConnector, - UINT* pConnectorId -); - -// ---------------------------------------------------------------------- -HRESULT GetEndpointFriendlyName -( - IMMDevice* pDevice, - LPWSTR* ppwszEndpointName -); - -// ---------------------------------------------------------------------- -HRESULT GetAudioFilterAsDevice -( - IMMDevice* pDevice, - IMMDevice** ppAudioFilterAsDevice -); - -// ---------------------------------------------------------------------- -HRESULT GetCachedProcessingModes -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - ULONG *pCount, - AUDIO_SIGNALPROCESSINGMODE **ppModes -); - - - - - - - - - - - - -// ---------------------------------------------------------------------- -HRESULT GetProcessingModes -( - IMMDevice* pDevice, - UINT pinId, - ULONG *pCount, - AUDIO_SIGNALPROCESSINGMODE **ppModes -); - -// ---------------------------------------------------------------------- -HRESULT GetCachedSupportedFormatRecords -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - ULONG *pCount, - FORMAT_RECORD **ppFormatRecords -); - - - - - - - - - - - - - -// ---------------------------------------------------------------------- -HRESULT GetSupportedFormatRecords -( - IMMDevice* pDevice, - UINT pinId, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - STACKWISE_DATAFLOW dataFlow, - ULONG *pCount, - FORMAT_RECORD **ppFormatRecords -); - -// ---------------------------------------------------------------------- -HRESULT IsFormatSupported -( - IMMDevice* pDevice, - UINT pinId, - WAVEFORMATEX *pWfx, - BOOL* pbSupported -); - -// ---------------------------------------------------------------------- -HRESULT DiscoverPeriodicityCharacteristicsForFormat -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - WAVEFORMATEX *pWfx, - STACKWISE_DATAFLOW dataFlow, - UINT32 *pDefaultPeriodicityInFrames, - UINT32 *pFundamentalPeriodicityInFrames, - UINT32 *pMinPeriodicityInFrames, - UINT32 *pMaxPeriodicityInFrames, - UINT32 *pMaxPeriodicityInFramesExtended -); - -// ---------------------------------------------------------------------- -HRESULT CheckConnectorSupportForPeriodicity -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - WAVEFORMATEX *pWfx, - STACKWISE_DATAFLOW dataFlow, - HNSTIME RequestedPeriodicity, - UINT32 *pActualPeriodicityInFrames -); - -// ---------------------------------------------------------------------- -HRESULT GetCachedDefaultFormat -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - WAVEFORMATEX** ppDefaultFormat -); - -// ---------------------------------------------------------------------- -HRESULT GetProposedFormatForProcessingMode -( - IMMDevice* pDevice, - UINT pinId, - AUDIO_SIGNALPROCESSINGMODE mode, - WAVEFORMATEX **ppProposedFormat -); - -// ---------------------------------------------------------------------- -HRESULT GetAvailiablePinInstanceCount -( - IMMDevice* pDevice, - UINT pinId, - UINT32* pAvailablePinInstanceCount -); diff --git a/audio/tests/wavetest/TestResource.h b/audio/tests/wavetest/TestResource.h deleted file mode 100644 index 49dee5a5..00000000 --- a/audio/tests/wavetest/TestResource.h +++ /dev/null @@ -1,66 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// File Name: -// -// TestResource.h -// -// Abstract: -// -// TAEF Test Resource -// -// ------------------------------------------------------------------------------- -#include "WaveTestTaef.h" - -class CPinTestResource : - public WEX::TestExecution::ITestResource, - public IHalfAppContainer -{ -public: - //IUnknown - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - STDMETHODIMP QueryInterface( - __in REFIID riid, - __deref_out VOID **ppvObject - ); - // ITestResouce - STDMETHODIMP GetGuid(GUID* pGuid); - STDMETHODIMP SetGuid(GUID guid); - STDMETHODIMP GetValue(BSTR name, BSTR* pValue); - STDMETHODIMP SetValue(BSTR name, BSTR value); - - //IHalfAppContainer - STDMETHODIMP GetHalfApp(CHalfApp ** ppHalfApp); - - static HRESULT STDMETHODCALLTYPE CreateInstance( - CHalfApp * pHalf, - REFGUID guid, - WEX::TestExecution::ITestResource ** ppOut - ); - -private: - - CPinTestResource(); - ~CPinTestResource(); - - HRESULT STDMETHODCALLTYPE Initialize( - CHalfApp * pHalf, - REFGUID guid - ); - - LPWSTR ModeName(REFGUID guidMode); - LPWSTR PinName(EndpointConnectorType eConnectorType); - - CComBSTR m_szType; - CComBSTR m_szName; - CComBSTR m_szId; - CComBSTR m_szMode; - CComBSTR m_szPin; - - CAutoPtr<CHalfApp> m_spHalfApp; - - ULONG m_cRef; - GUID m_guid; -};
\ No newline at end of file diff --git a/audio/tests/wavetest/TestResourceBuild.cpp b/audio/tests/wavetest/TestResourceBuild.cpp index 51cce52d..5ca2ff0d 100644 --- a/audio/tests/wavetest/TestResourceBuild.cpp +++ b/audio/tests/wavetest/TestResourceBuild.cpp @@ -12,8 +12,8 @@ // // ------------------------------------------------------------------------------ #include "PreComp.h" -#include "TestResource.h" -#include "PropertyHelper.h" +#include <TestResource.h> +#include <PropertyHelper.h> using namespace WEX::Common; using namespace WEX::Logging; @@ -30,32 +30,18 @@ using namespace WEX::TestExecution; HRESULT CreateTestResource ( ResourceList& resourceList, - LPWSTR deviceId, - LPWSTR deviceName, - IMMDevice* pDevice, - STACKWISE_DATAFLOW dataFlow, - EndpointConnectorType eConnectorType, - UINT uConnectorId, - AUDIO_SIGNALPROCESSINGMODE mode, - ULONG cModes, - AUDIO_SIGNALPROCESSINGMODE* pModes, - ULONG cFormatRecords, - PFORMAT_RECORD pFormatRecords, - PWAVEFORMATEX pPreferredFormat, - UINT32 defaultPeriodicityInFrames, - UINT32 fundamentalPeriodicityInFrames, - UINT32 minPeriodicityInFrames, - UINT32 maxPeriodicityInFrames + DeviceDescriptor descriptor ) { HRESULT hr = S_OK; + CComHeapPtr<CHalfApp> spHalfApp; wil::com_ptr_nothrow<ITestResource> spTestResource; GUID ResourceGUID; CComBSTR szResourceName; // Create HalfApp - spHalfApp.Attach(new CHalfApp(pDevice, deviceId, deviceName, dataFlow, eConnectorType, uConnectorId, mode, cModes, pModes, cFormatRecords, pFormatRecords, pPreferredFormat, defaultPeriodicityInFrames, fundamentalPeriodicityInFrames, minPeriodicityInFrames, maxPeriodicityInFrames)); + spHalfApp.Attach(new CHalfApp(descriptor)); if (!VERIFY_IS_NOT_NULL(spHalfApp)) { hr = E_OUTOFMEMORY; return hr; @@ -327,6 +313,7 @@ HRESULT AddTestResourceForConnector UINT32 u32MinPeriodicityInFrames; UINT32 u32MaxPeriodicityInFrames; UINT32 u32MaxPeriodicityInFramesExtended; + bool isAVStream = false; // Get connector id if (!VERIFY_SUCCEEDED(hr = GetConnectorId(pDevice, eConnectorType, &bHasConnector, &uConnectorId))) { @@ -338,6 +325,7 @@ HRESULT AddTestResourceForConnector Log::Comment(String().Format(L"Adding Test Resource for pin [%u]:", (uConnectorId & PARTID_MASK))); + // Get all signal processing modes for connector if (!VERIFY_SUCCEEDED(hr = GetProcessingModesForConnector(pDevice, uConnectorId, eConnectorType, &cModes, &spModes))) { return hr; @@ -345,6 +333,7 @@ HRESULT AddTestResourceForConnector // Loop through each mode, get preferred format and list of formats and create test resource for each mode for (ULONG i = 0; i < cModes; i++) { + mode = spModes[i]; if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(pDevice, uConnectorId, eConnectorType, mode, dataFlow, &cFormatRecords, &spFormatRecords))) { @@ -358,8 +347,32 @@ HRESULT AddTestResourceForConnector if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatPeriodicityCharacteristicsForConnector(pDevice, eConnectorType, mode, dataFlow, pPreferredFormat.get(), cFormatRecords, spFormatRecords, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) { return hr; } - - if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, deviceId, deviceName, pDevice, dataFlow, eConnectorType, uConnectorId, mode, cModes, spModes, cFormatRecords, spFormatRecords, pPreferredFormat.get(), u32DefaultPeriodicityInFrames, u32FundamentalPeriodicityInFrames, u32MinPeriodicityInFrames, u32MaxPeriodicityInFrames))) { + + // Check if audio endpoint is AVStream + if (!VERIFY_SUCCEEDED(hr = IsAVStream(pDevice, &isAVStream))) { + return hr; + } + + DeviceDescriptor descriptor = { 0 }; + descriptor.pDevice = pDevice; + descriptor.pwstrAudioEndpointId = deviceId; + descriptor.pwstrAudioEndpointFriendlyName = deviceName; + descriptor.dataFlow = dataFlow; + descriptor.eConnectorType = eConnectorType; + descriptor.uConnectorId = uConnectorId; + descriptor.mode = mode; + descriptor.cModes = cModes; + descriptor.pModes = spModes; + descriptor.cFormatRecords = cFormatRecords; + descriptor.pFormatRecords = spFormatRecords; + descriptor.pPreferredFormat = pPreferredFormat.get(); + descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; + descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; + descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; + descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsAVStream = isAVStream; + + if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, descriptor))) { return hr; } } diff --git a/audio/tests/wavetest/WaveTestTaef.cpp b/audio/tests/wavetest/WaveTestTaef.cpp index dacf44ab..be5d47f3 100644 --- a/audio/tests/wavetest/WaveTestTaef.cpp +++ b/audio/tests/wavetest/WaveTestTaef.cpp @@ -164,7 +164,7 @@ void WDMAudio::WaveTest::TAEF_StreamMultipleModes() RUN_TEST_CASE(Test_StreamMultipleModes) } -void WDMAudio::WaveTest::TAEF_VerifyPinSupportsPullMode() +void WDMAudio::WaveTest::TAEF_VerifyPinWaveRTConformance() { - RUN_TEST_CASE(Test_VerifyPinSupportsPullMode) -}
\ No newline at end of file + RUN_TEST_CASE(Test_VerifyPinWaveRTConformance) +} diff --git a/audio/tests/wavetest/WaveTestTaef.h b/audio/tests/wavetest/WaveTestTaef.h index a8d38305..8c87a7f8 100644 --- a/audio/tests/wavetest/WaveTestTaef.h +++ b/audio/tests/wavetest/WaveTestTaef.h @@ -13,7 +13,7 @@ // ------------------------------------------------------------------------------- #pragma once -#include "HalfApp.h" +#include <HalfApp.h> extern IBasicLog* g_pBasicLog; @@ -41,6 +41,7 @@ namespace WDMAudio TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Server x64") TEST_CLASS_PROPERTY(L"Kits.MinRelease", L"19H1") TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"Full") + TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"OnecoreUAP") TEST_CLASS_PROPERTY(L"Kits.IsInProc", L"True") TEST_CLASS_PROPERTY(L"Kits.Parameter", L"DeviceID") TEST_CLASS_PROPERTY(L"Kits.Parameter.DeviceID.Description", L"Device id of device under test") @@ -109,7 +110,7 @@ namespace WDMAudio TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*RAW' OR @Id = '*DEFAULT'") END_TEST_METHOD() - BEGIN_TEST_METHOD(TAEF_VerifyPinSupportsPullMode) + BEGIN_TEST_METHOD(TAEF_VerifyPinWaveRTConformance) TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") diff --git a/audio/tests/wavetest/comptest.cpp b/audio/tests/wavetest/comptest.cpp index ef41972c..db823156 100644 --- a/audio/tests/wavetest/comptest.cpp +++ b/audio/tests/wavetest/comptest.cpp @@ -17,8 +17,8 @@ #include "tests.h" // ------------------------------------------------------------------------------ -// Test_VerifyPinSupportsPullMode: Test the requirement that WaveRT drivers must support pull mode audio streaming technology. -void Test_VerifyPinSupportsPullMode(void) +// Test_VerifyPinSupportsPullMode: Test the requirement that new drivers must be WaveRT and WaveRT drivers must support event driven audio streaming technology. +void Test_VerifyPinWaveRTConformance(void) { CHalfApp* pHalfApp = g_pWaveTest->m_pHalf; BOOL bIsRTCapable = false; @@ -28,10 +28,17 @@ void Test_VerifyPinSupportsPullMode(void) VERIFY_SUCCEEDED(pHalfApp->m_pAudioDeviceEndpoint->GetRTCaps(&bIsRTCapable)); - // Only require pull mode for WaveRT drivers + // Check for our new requirement - new drivers must be WaveRT. Here we skip the test on AVStream (UAC1/HFP/A2dp) + // For other old drivers that are not WaveRT, may issue Errata to cover the failure. + if (!pHalfApp->m_bIsAVStream) + { + VERIFY_IS_TRUE(bIsRTCapable); + } + + // Only require event driven for WaveRT drivers if (bIsRTCapable) { VERIFY_SUCCEEDED(pHalfApp->m_pAudioDeviceEndpoint->GetEventDrivenCapable(&bIsEventCapable)); VERIFY_IS_TRUE(bIsEventCapable); } -}
\ No newline at end of file +} diff --git a/audio/tests/wavetest/tests.h b/audio/tests/wavetest/tests.h index 38a6aaa1..e3ae0691 100644 --- a/audio/tests/wavetest/tests.h +++ b/audio/tests/wavetest/tests.h @@ -24,4 +24,4 @@ void Test_LoopbackStreamDifferentFormat(void); void Test_StreamMultipleModes(void); // Compliance test -void Test_VerifyPinSupportsPullMode(void);
\ No newline at end of file +void Test_VerifyPinWaveRTConformance(void);
\ No newline at end of file diff --git a/avstream/avssamp/avssamp.inx b/avstream/avssamp/avssamp.inx Binary files differindex bcb18baa..61d66fa3 100644 --- a/avstream/avssamp/avssamp.inx +++ b/avstream/avssamp/avssamp.inx diff --git a/avstream/avssamp/avssamp.vcxproj b/avstream/avssamp/avssamp.vcxproj index 77567a8f..58d7992d 100644 --- a/avstream/avssamp/avssamp.vcxproj +++ b/avstream/avssamp/avssamp.vcxproj @@ -108,6 +108,9 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> </Midl> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Link> @@ -130,6 +133,9 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> </Midl> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Link> @@ -152,6 +158,9 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> </Midl> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Link> @@ -174,6 +183,9 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_</PreprocessorDefinitions> </Midl> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="audio.cpp" /> diff --git a/avstream/avssamp/avssamp.vcxproj.Filters b/avstream/avssamp/avssamp.vcxproj.Filters index 9b056e4d..8d345831 100644 --- a/avstream/avssamp/avssamp.vcxproj.Filters +++ b/avstream/avssamp/avssamp.vcxproj.Filters @@ -49,4 +49,32 @@ <Filter>Resource Files</Filter> </ResourceCompile> </ItemGroup> + <ItemGroup> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Inf Include="*.inf;*.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/avstream/avssamp/video.cpp b/avstream/avssamp/video.cpp index 1be9b56e..0f3f7c7d 100644 --- a/avstream/avssamp/video.cpp +++ b/avstream/avssamp/video.cpp @@ -1,4 +1,4 @@ -/************************************************************************** +/************************************************************************** AVStream Filter-Centric Sample @@ -23,7 +23,7 @@ /************************************************************************** PAGEABLE CODE - + **************************************************************************/ #ifdef ALLOC_PRAGMA diff --git a/avstream/sampledevicemft/multipinmft.vcxproj b/avstream/sampledevicemft/multipinmft.vcxproj index 1a0716bb..f13ed67d 100644 --- a/avstream/sampledevicemft/multipinmft.vcxproj +++ b/avstream/sampledevicemft/multipinmft.vcxproj @@ -154,17 +154,9 @@ <WppScanConfigurationData>common.h</WppScanConfigurationData> </ClCompile> </ItemGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <TargetName>multipinmft</TargetName> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <TargetName>multipinmft</TargetName> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <TargetName>multipinmft</TargetName> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <PropertyGroup> <TargetName>multipinmft</TargetName> + <IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\wil\include</IncludePath> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> @@ -261,18 +253,17 @@ <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" /> - <None Include="packages.config" /> + <None Include="Source.def" /> </ItemGroup> <ItemGroup> - <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + <ClInclude Include="basepin.h" /> + <ClInclude Include="common.h" /> + <ClInclude Include="contosodevice.h" /> + <ClInclude Include="custompin.h" /> + <ClInclude Include="mftpeventgenerator.h" /> + <ClInclude Include="multipinmft.h" /> + <ClInclude Include="multipinmfthelpers.h" /> + <ClInclude Include="stdafx.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <Import Project="packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> - <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> - <PropertyGroup> - <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> - </PropertyGroup> - <Error Condition="!Exists('packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" /> - </Target> </Project>
\ No newline at end of file diff --git a/avstream/sampledevicemft/multipinmft.vcxproj.Filters b/avstream/sampledevicemft/multipinmft.vcxproj.Filters index 36af8e37..a4692065 100644 --- a/avstream/sampledevicemft/multipinmft.vcxproj.Filters +++ b/avstream/sampledevicemft/multipinmft.vcxproj.Filters @@ -42,6 +42,31 @@ <None Include="Source.def"> <Filter>Source Files</Filter> </None> - <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="basepin.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="common.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="contosodevice.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="custompin.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="mftpeventgenerator.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="multipinmft.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="multipinmfthelpers.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="stdafx.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project>
\ No newline at end of file diff --git a/avstream/sampledevicemft/packages.config b/avstream/sampledevicemft/packages.config deleted file mode 100644 index 09cb1163..00000000 --- a/avstream/sampledevicemft/packages.config +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<packages> - <package id="Microsoft.Windows.ImplementationLibrary" version="1.0.190716.2" targetFramework="native" /> -</packages>
\ No newline at end of file diff --git a/hid/hclient/report.c b/hid/hclient/report.c index f1d9ee55..02fb95a9 100644 --- a/hid/hclient/report.c +++ b/hid/hclient/report.c @@ -513,7 +513,7 @@ Routine Description: ULONG i; ULONG CurrReportID; BOOLEAN result = FALSE; - + PHID_DATA Head = Data; /* // All report buffers that are initially sent need to be zero'd out */ @@ -584,6 +584,7 @@ Routine Description: // having been set. */ + Data = Head; for (i = 0; i < DataLength; i++, Data++) { if (CurrReportID == Data -> ReportID) diff --git a/network/ndis/filter/ndislwf.vcxproj b/network/ndis/filter/ndislwf.vcxproj index f381ba89..fa4f9d39 100644 --- a/network/ndis/filter/ndislwf.vcxproj +++ b/network/ndis/filter/ndislwf.vcxproj @@ -211,6 +211,9 @@ <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Link> @@ -220,6 +223,9 @@ <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Link> @@ -229,6 +235,9 @@ <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Link> @@ -238,6 +247,9 @@ <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>SHA256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="device.c"> diff --git a/network/ndis/filter/ndislwf.vcxproj.Filters b/network/ndis/filter/ndislwf.vcxproj.Filters index 200de09e..fab2d582 100644 --- a/network/ndis/filter/ndislwf.vcxproj.Filters +++ b/network/ndis/filter/ndislwf.vcxproj.Filters @@ -37,4 +37,23 @@ <Filter>Resource Files</Filter> </ResourceCompile> </ItemGroup> + <ItemGroup> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Inf Include="*.inf"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj index 0f930a7b..da4c5c58 100644 --- a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj @@ -154,6 +154,9 @@ <Midl> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..</AdditionalIncludeDirectories> </Midl> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Link> @@ -170,6 +173,9 @@ <Midl> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..</AdditionalIncludeDirectories> </Midl> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Link> @@ -186,6 +192,9 @@ <Midl> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..</AdditionalIncludeDirectories> </Midl> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Link> @@ -202,6 +211,9 @@ <Midl> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..</AdditionalIncludeDirectories> </Midl> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\debug.c"> diff --git a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters index fb3baf30..32ecad3d 100644 --- a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters @@ -46,4 +46,9 @@ <Filter>Resource Files</Filter> </ResourceCompile> </ItemGroup> + <ItemGroup> + <Inf Include="*.inf"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_Macros.h b/network/trans/WFPSampler/syslib/HelperFunctions_Macros.h index 36c2ec00..8af997bb 100644 --- a/network/trans/WFPSampler/syslib/HelperFunctions_Macros.h +++ b/network/trans/WFPSampler/syslib/HelperFunctions_Macros.h @@ -155,7 +155,7 @@ extern "C" /** @macro="HLPR_DELETE" - Purpose: Free memory allocated with ExAllocatePoolWithTag and set the pointer to 0. <br> + Purpose: Free memory allocated with ExAllocatePoolZero and set the pointer to 0. <br> <br> Notes: <br> <br> @@ -172,7 +172,7 @@ extern "C" /** @macro="HLPR_DELETE_ARRAY" - Purpose: Free memory allocated with ExAllocatePoolWithTag and set the pointer to 0. <br> + Purpose: Free memory allocated with ExAllocatePoolZero and set the pointer to 0. <br> <br> Notes: <br> <br> @@ -184,7 +184,7 @@ extern "C" /** @macro="HLPR_NEW" - Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolWithTag and initialize it's + Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolZero and initialize it's contents with 0's. <br> <br> Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE. <br> @@ -196,9 +196,9 @@ extern "C" pPtr == 0; \ ) \ { \ - pPtr = (object*)ExAllocatePoolWithTag(NonPagedPoolNx, \ - sizeof(object), \ - tag); \ + pPtr = (object*)ExAllocatePoolZero(NonPagedPoolNx, \ + sizeof(object), \ + tag); \ if(pPtr) \ RtlSecureZeroMemory(pPtr, \ sizeof(object)); \ @@ -207,7 +207,7 @@ extern "C" /** @macro="HLPR_NEW_ARRAY" - Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolWithTag and initialize it's + Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolZero and initialize it's contents with 0's. <br> <br> Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE_ARRAY. <br> @@ -226,9 +226,9 @@ extern "C" &SAFE_SIZE) == STATUS_SUCCESS && \ SAFE_SIZE >= (sizeof(object) * count)) \ { \ - pPtr = (object*)ExAllocatePoolWithTag(NonPagedPoolNx, \ - SAFE_SIZE, \ - tag); \ + pPtr = (object*)ExAllocatePoolZero(NonPagedPoolNx, \ + SAFE_SIZE, \ + tag); \ if(pPtr) \ RtlZeroMemory(pPtr, \ SAFE_SIZE); \ @@ -243,7 +243,7 @@ extern "C" /** @macro="HLPR_NEW_CASTED_ARRAY" - Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolWithTag and initialize it's + Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolZero and initialize it's contents with 0's. <br> <br> Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE_ARRAY. <br> @@ -262,9 +262,9 @@ extern "C" &SAFE_SIZE) == STATUS_SUCCESS && \ SAFE_SIZE >= (sizeof(object) * count)) \ { \ - pPtr = (CAST_TYPE*)ExAllocatePoolWithTag(NonPagedPoolNx, \ - SAFE_SIZE, \ - tag); \ + pPtr = (CAST_TYPE*)ExAllocatePoolZero(NonPagedPoolNx, \ + SAFE_SIZE, \ + tag); \ if(pPtr) \ RtlZeroMemory(pPtr, \ SAFE_SIZE); \ @@ -279,7 +279,7 @@ extern "C" /** @macro="HLPR_NEW" - Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolWithTag and leave it's + Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolZero and leave it's contents as is. <br> <br> Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE. <br> @@ -291,9 +291,9 @@ extern "C" pPtr == 0; \ ) \ { \ - pPtr = (object*)ExAllocatePoolWithTag(NonPagedPoolNx, \ - sizeof(object), \ - tag); \ + pPtr = (object*)ExAllocatePoolZero(NonPagedPoolNx, \ + sizeof(object), \ + tag); \ } /** diff --git a/network/trans/ddproxy/sys/DD_proxy.c b/network/trans/ddproxy/sys/DD_proxy.c index a75e2cf9..a5901908 100644 --- a/network/trans/ddproxy/sys/DD_proxy.c +++ b/network/trans/ddproxy/sys/DD_proxy.c @@ -833,7 +833,7 @@ DDProxyCloneModifyReinjectInbound( // is contiguous and 2-byte aligned. _Analysis_assume_(udpHeader != NULL); - udpHeader->destPort = + udpHeader->srcPort = packet->belongingFlow->toRemotePort; // This is our new source port -- or // the destination port of the original diff --git a/network/trans/inspect/inspect.sln b/network/trans/inspect/inspect.sln index 40b0cead..3f3ae438 100644 --- a/network/trans/inspect/inspect.sln +++ b/network/trans/inspect/inspect.sln @@ -1,28 +1,31 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 12.0 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inspect", "sys\inspect.vcxproj", "{5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|Win32.ActiveCfg = Debug|Win32 - {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|Win32.Build.0 = Debug|Win32 - {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|Win32.ActiveCfg = Release|Win32 - {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|Win32.Build.0 = Release|Win32 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|Win32.ActiveCfg = Debug|x64 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|Win32.Build.0 = Debug|x64 {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|x64.ActiveCfg = Debug|x64 {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|x64.Build.0 = Debug|x64 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|Win32.ActiveCfg = Release|Win32 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|Win32.Build.0 = Release|Win32 {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|x64.ActiveCfg = Release|x64 {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F084D2B3-2035-4BCE-A4D1-FA6AE812D7A1} + EndGlobalSection EndGlobal diff --git a/network/trans/inspect/sys/inspect.c b/network/trans/inspect/sys/inspect.c index 418daf9a..8dd6b72a 100644 --- a/network/trans/inspect/sys/inspect.c +++ b/network/trans/inspect/sys/inspect.c @@ -1278,7 +1278,7 @@ TLInspectWorker( listEntry ); - NT_ASSERT((packet->direction == FWP_DIRECTION_INBOUND) || + NT_ASSERT((packet->direction != FWP_DIRECTION_INBOUND) || (packet->authConnectDecision == 0)); if (packet->authConnectDecision == 0) diff --git a/network/trans/inspect/sys/inspect.vcxproj b/network/trans/inspect/sys/inspect.vcxproj index 2115972e..c154521d 100644 --- a/network/trans/inspect/sys/inspect.vcxproj +++ b/network/trans/inspect/sys/inspect.vcxproj @@ -23,14 +23,14 @@ <RootNamespace>$(MSBuildProjectName)</RootNamespace> <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> - <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <Platform>Desktop</Platform> <SampleGuid>{910EE323-2C33-42E0-B2DD-3C3BB1F4689E}</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> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> <DriverType>KMDF</DriverType> <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> <ConfigurationType>Driver</ConfigurationType> @@ -38,7 +38,7 @@ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>True</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> <DriverType>KMDF</DriverType> <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> <ConfigurationType>Driver</ConfigurationType> @@ -46,7 +46,7 @@ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>False</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> <DriverType>KMDF</DriverType> <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> <ConfigurationType>Driver</ConfigurationType> @@ -54,7 +54,7 @@ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <TargetVersion>Windows10</TargetVersion> <UseDebugLibraries>True</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> <DriverType>KMDF</DriverType> <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> <ConfigurationType>Driver</ConfigurationType> @@ -75,7 +75,10 @@ <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" /> + <ItemGroup Label="WrappedTaskItems"> + <ClInclude Include="inspect.h" /> + <ClInclude Include="utils.h" /> + </ItemGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <TargetName>inspect</TargetName> </PropertyGroup> @@ -170,7 +173,6 @@ <ClCompile Include="utils.c" /> </ItemGroup> <ItemGroup> - <Inf Exclude="@(Inf)" Include="*.inf" /> <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> </ItemGroup> <ItemGroup> @@ -179,7 +181,7 @@ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> </ItemGroup> <ItemGroup> - <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + <Inf Include="inspect.inf" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> </Project>
\ No newline at end of file diff --git a/network/trans/inspect/sys/inspect.vcxproj.Filters b/network/trans/inspect/sys/inspect.vcxproj.Filters index 96fc4202..f4a5dbe0 100644 --- a/network/trans/inspect/sys/inspect.vcxproj.Filters +++ b/network/trans/inspect/sys/inspect.vcxproj.Filters @@ -29,4 +29,17 @@ <Filter>Source Files</Filter> </ClCompile> </ItemGroup> + <ItemGroup> + <ClInclude Include="inspect.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="utils.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Inf Include="inspect.inf"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/network/trans/msnmntr/sys/msnmntr.c b/network/trans/msnmntr/sys/msnmntr.c index a2c59cf2..214b98be 100644 --- a/network/trans/msnmntr/sys/msnmntr.c +++ b/network/trans/msnmntr/sys/msnmntr.c @@ -257,9 +257,9 @@ MonitorCoAllocFlowContext( *flowContextOut = NULL; - flowContext = ExAllocatePoolWithTag(NonPagedPool, - sizeof(FLOW_DATA), - TAG_NAME_CALLOUT); + flowContext = ExAllocatePoolZero(NonPagedPool, + sizeof(FLOW_DATA), + TAG_NAME_CALLOUT); if (!flowContext) { @@ -271,9 +271,9 @@ MonitorCoAllocFlowContext( sizeof(FLOW_DATA)); - flowContext->processPath = ExAllocatePoolWithTag(NonPagedPool, - processPathSize, - TAG_NAME_CALLOUT); + flowContext->processPath = ExAllocatePoolZero(NonPagedPool, + processPathSize, + TAG_NAME_CALLOUT); if (!flowContext->processPath) { status = STATUS_NO_MEMORY; diff --git a/network/trans/msnmntr/sys/notify.c b/network/trans/msnmntr/sys/notify.c index 22253797..935cd161 100644 --- a/network/trans/msnmntr/sys/notify.c +++ b/network/trans/msnmntr/sys/notify.c @@ -270,9 +270,9 @@ NTSTATUS MonitorNfNotifyMessage( if(streamLength == 0) return status; - stream = ExAllocatePoolWithTag(NonPagedPool, - streamLength, - TAG_NAME_NOTIFY); + stream = ExAllocatePoolZero(NonPagedPool, + streamLength, + TAG_NAME_NOTIFY); if (!stream) return STATUS_INSUFFICIENT_RESOURCES; diff --git a/network/wlan/WDI/HAL/phydm/phydm.h b/network/wlan/WDI/HAL/phydm/phydm.h index 21651973..492a4f2a 100644 --- a/network/wlan/WDI/HAL/phydm/phydm.h +++ b/network/wlan/WDI/HAL/phydm/phydm.h @@ -620,7 +620,7 @@ typedef struct DM_Out_Source_Dynamic_Mechanism_Structure //-----------HOOK BEFORE REG INIT-----------// // ODM Platform info AP/ADSL/CE/MP = 1/2/3/4 u1Byte SupportPlatform; - // ODM Support Ability DIG/RATR/TX_PWR_TRACK/ �K�K = 1/2/3/�K + // ODM Support Ability DIG/RATR/TX_PWR_TRACK/ ¡K¡K = 1/2/3/¡K u4Byte SupportAbility; // ODM PCIE/USB/SDIO = 1/2/3 u1Byte SupportInterface; @@ -695,7 +695,7 @@ typedef struct DM_Out_Source_Dynamic_Mechanism_Structure BOOLEAN *pbGetValueFromOtherMac; PADAPTER *pBuddyAdapter; - BOOLEAN *pbMasterOfDMSP; //MAC0: master, MAC1: slave + BOOLEAN *pbMasterOfDMSP; //MAC0: master, MAC1: subordinate // Common info for Status BOOLEAN *pbScanInProcess; BOOLEAN *pbPowerSaving; diff --git a/network/wlan/WDI/PLATFORM/NDIS6/SDIO/N6SdioPlatformWindows.c b/network/wlan/WDI/PLATFORM/NDIS6/SDIO/N6SdioPlatformWindows.c index d48ee104..86a16e03 100644 --- a/network/wlan/WDI/PLATFORM/NDIS6/SDIO/N6SdioPlatformWindows.c +++ b/network/wlan/WDI/PLATFORM/NDIS6/SDIO/N6SdioPlatformWindows.c @@ -1006,17 +1006,15 @@ PlatformSdioGetProperty( PSDBUS_REQUEST_PACKET psdrp = NULL; NTSTATUS status; - psdrp = (PSDBUS_REQUEST_PACKET)ExAllocatePoolWithTag(NonPagedPool, sizeof(SDBUS_REQUEST_PACKET), '3278'); + psdrp = (PSDBUS_REQUEST_PACKET)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(SDBUS_REQUEST_PACKET), '3278'); if(!psdrp) return STATUS_INSUFFICIENT_RESOURCES; - RtlZeroMemory(psdrp, sizeof(SDBUS_REQUEST_PACKET)); - - psdrp->RequestFunction = SDRF_GET_PROPERTY; - psdrp->Parameters.GetSetProperty.Property = Property; - psdrp->Parameters.GetSetProperty.Buffer = Buffer; - psdrp->Parameters.GetSetProperty.Length = Length; + psdrp->RequestFunction = SDRF_GET_PROPERTY; + psdrp->Parameters.GetSetProperty.Property = Property; + psdrp->Parameters.GetSetProperty.Buffer = Buffer; + psdrp->Parameters.GetSetProperty.Length = Length; NdisAcquireSpinLock( &(pDevice->IrpSpinLock) ); RT_SDIO_INC_CMD_REF(pDevice); @@ -1062,17 +1060,15 @@ PlatformSdioSetProperty( PSDBUS_REQUEST_PACKET psdrp = NULL; NTSTATUS status; - psdrp = (PSDBUS_REQUEST_PACKET)ExAllocatePoolWithTag(NonPagedPool, sizeof(SDBUS_REQUEST_PACKET), '3278'); + psdrp = (PSDBUS_REQUEST_PACKET)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(SDBUS_REQUEST_PACKET), '3278'); if(!psdrp) return STATUS_INSUFFICIENT_RESOURCES; - RtlZeroMemory(psdrp, sizeof(SDBUS_REQUEST_PACKET)); - - psdrp->RequestFunction = SDRF_SET_PROPERTY; - psdrp->Parameters.GetSetProperty.Property = Property; - psdrp->Parameters.GetSetProperty.Buffer = Buffer; - psdrp->Parameters.GetSetProperty.Length = Length; + psdrp->RequestFunction = SDRF_SET_PROPERTY; + psdrp->Parameters.GetSetProperty.Property = Property; + psdrp->Parameters.GetSetProperty.Buffer = Buffer; + psdrp->Parameters.GetSetProperty.Length = Length; NdisAcquireSpinLock( &(pDevice->IrpSpinLock) ); RT_SDIO_INC_CMD_REF(pDevice); @@ -1409,14 +1405,12 @@ PlatformSdioCmd53ReadWriteMDL( // // Now allocate a request packet for the arguments of the command. // - psdrp = ExAllocatePoolWithTag(NonPagedPool, sizeof(SDBUS_REQUEST_PACKET), '3278'); + psdrp = ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(SDBUS_REQUEST_PACKET), '3278'); if(!psdrp) { RT_TRACE(COMP_IO, DBG_SERIOUS, ("PlatformSdioCmd53ReadWriteMDL(): Allocate sdrp fail!!\n")); return rtstatus; } - - RtlZeroMemory(psdrp, sizeof(SDBUS_REQUEST_PACKET)); psdrp->RequestFunction = SDRF_DEVICE_COMMAND; psdrp->Parameters.DeviceCommand.Mdl = pmdl; @@ -2985,7 +2979,7 @@ PrepareSdioAWBs( // // Allocate SD Request Packet // - device->pAsynIoWriteSdrp = (PSDBUS_REQUEST_PACKET)ExAllocatePoolWithTag(NonPagedPool, sizeof(SDBUS_REQUEST_PACKET), '3278'); + device->pAsynIoWriteSdrp = (PSDBUS_REQUEST_PACKET)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(SDBUS_REQUEST_PACKET), '3278'); if(device->pAsynIoWriteSdrp == NULL) goto Error; diff --git a/network/wlan/WDI/PLATFORM/NDIS6/SDIO/N6Sdio_main.c b/network/wlan/WDI/PLATFORM/NDIS6/SDIO/N6Sdio_main.c index fb9160a4..1165eed1 100644 --- a/network/wlan/WDI/PLATFORM/NDIS6/SDIO/N6Sdio_main.c +++ b/network/wlan/WDI/PLATFORM/NDIS6/SDIO/N6Sdio_main.c @@ -1489,10 +1489,9 @@ N6SdioInitTxQueue( pDevice->RtNumTxQueue = SDIO_MAX_TX_QUEUE; // TX_HIQ, TX_MIQ and TX_LOQ // Initialize the contexts in Tx Queue. - pDevice->RtTxQueue = - (PRT_SDIO_TX_QUEUE)ExAllocatePoolWithTag(NonPagedPool, SDIO_MAX_TX_QUEUE*sizeof(RT_SDIO_TX_QUEUE), '3278' ); - PlatformZeroMemory(pDevice->RtTxQueue, SDIO_MAX_TX_QUEUE*sizeof(RT_SDIO_TX_QUEUE)); - + pDevice->RtTxQueue = + (PRT_SDIO_TX_QUEUE)ExAllocatePool2(POOL_FLAG_NON_PAGED, SDIO_MAX_TX_QUEUE*sizeof(RT_SDIO_TX_QUEUE), '3278' ); + if (pDevice->RtTxQueue != NULL) { diff --git a/network/wlan/WDI/PLATFORM/NDIS6/SDIO/sdio.vcxproj b/network/wlan/WDI/PLATFORM/NDIS6/SDIO/sdio.vcxproj index af48c0a4..a8432de0 100644 --- a/network/wlan/WDI/PLATFORM/NDIS6/SDIO/sdio.vcxproj +++ b/network/wlan/WDI/PLATFORM/NDIS6/SDIO/sdio.vcxproj @@ -336,13 +336,9 @@ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Win10 Debug|Win32'"> <Link> <AdditionalDependencies>$(SolutionDir)COMMON\$(Configuration)\common.lib;$(SolutionDir)PLATFORM\NdisComm\$(Configuration)\NdisComm.lib;$(SolutionDir)\LIB\x86\hal.lib;$(SolutionDir)\LIB\x86\rtklibcom.lib;$(WindowsSDK_LibraryPath_x86)km\x86\ndis.lib;$(WindowsSDK_LibraryPath_x86)km\x86\ntoskrnl.lib;$(WindowsSDK_LibraryPath_x86)km\x86\sdbus.lib;$(WindowsSDK_LibraryPath_x86)km\x86\wlan\1.0\TlvGeneratorParser.lib;</AdditionalDependencies> - <Driver> - </Driver> - <TreatLinkerWarningAsErrors> - </TreatLinkerWarningAsErrors> + <!-- warning LNK4099: PDB 'rtklibcom.pdb' was not found with 'rtklibcom.lib(multichannels.obj)' --> + <AdditionalOptions>$(AdditionalOptions) /IGNORE:4099</AdditionalOptions> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <LinkStatus> - </LinkStatus> </Link> <ClCompile> <PreprocessorDefinitions>_X86_;DBG;NDIS60_MINIPORT;USE_KLOCKS=1;BINARY_COMPATIBLE=0;NDIS650_MINIPORT=1;NDIS640_MINIPORT=1;NDIS630_MINIPORT=1;NDIS620_MINIPORT=1;NDIS61_MINIPORT=1;NDIS_MINIPORT_DRIVER;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -425,6 +421,8 @@ <DebugInformationFormat>None</DebugInformationFormat> </ClCompile> <Link> + <!-- warning LNK4099: PDB 'rtklibcom.pdb' was not found with 'rtklibcom.lib(multichannels.obj)' --> + <AdditionalOptions>$(AdditionalOptions) /IGNORE:4099</AdditionalOptions> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> <AdditionalDependencies>$(SolutionDir)COMMON\$(Configuration)\common.lib;$(SolutionDir)PLATFORM\NdisComm\$(Configuration)\NdisComm.lib;$(SolutionDir)\LIB\x86\hal.lib;$(SolutionDir)\LIB\x86\rtklibcom.lib;$(WindowsSDK_LibraryPath_x86)km\x86\ndis.lib;$(WindowsSDK_LibraryPath_x86)km\x86\ntoskrnl.lib;$(WindowsSDK_LibraryPath_x86)km\x86\sdbus.lib;$(WindowsSDK_LibraryPath_x86)km\x86\wlan\1.0\TlvGeneratorParser.lib;</AdditionalDependencies> <GenerateDebugInformation> @@ -626,6 +624,8 @@ </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <!-- warning LNK4099: PDB 'rtklibcom.pdb' was not found with 'rtklibcom.lib(multichannels.obj)' --> + <AdditionalOptions>$(AdditionalOptions) /IGNORE:4099</AdditionalOptions> <AdditionalDependencies>$(SolutionDir)COMMON\$(Configuration)\$(Platform)\common.lib;$(SolutionDir)PLATFORM\NdisComm\$(Configuration)\$(Platform)\NdisComm.lib;$(SolutionDir)\LIB\x64\hal.lib;$(SolutionDir)\LIB\x64\rtklibcom.lib;$(WindowsSDK_LibraryPath_x64)km\x64\ndis.lib;$(WindowsSDK_LibraryPath_x64)km\x64\ntoskrnl.lib;$(WindowsSDK_LibraryPath_x64)km\x64\sdbus.lib;$(WindowsSDK_LibraryPath_x64)km\x64\wlan\1.0\TlvGeneratorParser.lib;</AdditionalDependencies> </Link> <ProjectReference> diff --git a/network/wlan/WDI/PLATFORM/NDIS6/WDI_Cmds.c b/network/wlan/WDI/PLATFORM/NDIS6/WDI_Cmds.c index ec5cddd6..943038dd 100644 --- a/network/wlan/WDI/PLATFORM/NDIS6/WDI_Cmds.c +++ b/network/wlan/WDI/PLATFORM/NDIS6/WDI_Cmds.c @@ -3191,24 +3191,24 @@ Wdi_Set_Add_Pm_Protocol_Offload( //2 DOT11 RSN REKey Offload - if(Params->DOT11RSNREKeyOffload.ProtocolOffloadId) + if(Params->DOT11RSNREKeyOffload.RsnKeyInfo.ProtocolOffloadId) { RT_TRACE(COMP_OID_SET, DBG_LOUD, ("ProtocolOffloadId = %d\n", - Params->DOT11RSNREKeyOffload.ProtocolOffloadId)); + Params->DOT11RSNREKeyOffload.RsnKeyInfo.ProtocolOffloadId)); pMgntInfo->PowerSaveControl.PMProtocolOffloadIDs[eGTKOffloadIdx] - = Params->DOT11RSNREKeyOffload.ProtocolOffloadId; + = Params->DOT11RSNREKeyOffload.RsnKeyInfo.ProtocolOffloadId; //Copy kck, kek PlatformMoveMemory(&(pMgntInfo->PMDot11RSNRekeyPara.KCK), - Params->DOT11RSNREKeyOffload.KCK_CONTENT, 32); + &Params->DOT11RSNREKeyOffload.RsnKeyInfo.KCK_CONTENT, 32); RT_PRINT_DATA( COMP_OID_SET, DBG_LOUD, ("KCK_CONTENT:\n"), pMgntInfo->PMDot11RSNRekeyPara.KCK, 16); RT_PRINT_DATA( COMP_OID_SET, DBG_LOUD, ("KEK_CONTENT:\n"), pMgntInfo->PMDot11RSNRekeyPara.KEK, 16); - pMgntInfo->PMDot11RSNRekeyPara.KeyReplayCounter = Params->DOT11RSNREKeyOffload.ReplayCounter; + pMgntInfo->PMDot11RSNRekeyPara.KeyReplayCounter = Params->DOT11RSNREKeyOffload.RsnKeyInfo.ReplayCounter; if(pPSC->RegGTKOffloadEnable) pPSC->GTKOffloadEnable = TRUE; diff --git a/print/OEM Printer Customization Plug-in Samples/README.md b/print/OEM Printer Customization Plug-in Samples/README.md index 9c3beff5..4a5fcf8d 100644 --- a/print/OEM Printer Customization Plug-in Samples/README.md +++ b/print/OEM Printer Customization Plug-in Samples/README.md @@ -54,7 +54,13 @@ To build a driver solution using Windows 10 driver kit (Windows Driver Kit (WDK) 1. Add all non-binary files (usually located in the \install directory of the sample) to the Package project - 1. In the **Solution Explorer**, right click **Driver Files** + 1. In the **Solution Explorer**, right click **Driver Files**. If you can't find **Driver Files** in this step, please change the "Platform Toolset" like below + + a. In the **Solution Explorer**, right click the Package project which you want to build and select **Properties** + + b. Select **General** under the **Configuration Properties** + + c. Change **Platform Toolset** to **WindowsKernelModeDriver10.0** or **WindowsUserModeDriver10.0** 1. Select **Add**, then click **Existing Item** diff --git a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj index 9242769a..6f345409 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj +++ b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj @@ -138,36 +138,54 @@ <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> <ClCompile> <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> <ClCompile> <ExceptionHandling> </ExceptionHandling> </ClCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj index d68c6610..8bd189b0 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj +++ b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj @@ -92,7 +92,35 @@ <VerifyDrivers /> <VerifyFlags>133563</VerifyFlags> </PropertyGroup> - <ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/sensors/ADXL345Acc/ADXL345Acc.vcxproj b/sensors/ADXL345Acc/ADXL345Acc.vcxproj index 3e04c33b..5e2757dd 100644 --- a/sensors/ADXL345Acc/ADXL345Acc.vcxproj +++ b/sensors/ADXL345Acc/ADXL345Acc.vcxproj @@ -121,6 +121,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>ADXL345Acc.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -143,6 +146,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>ADXL345Acc.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -165,6 +171,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>ADXL345Acc.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -187,6 +196,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>ADXL345Acc.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/sensors/Activity/Activity.vcxproj b/sensors/Activity/Activity.vcxproj index 724803cd..bd83ce53 100644 --- a/sensors/Activity/Activity.vcxproj +++ b/sensors/Activity/Activity.vcxproj @@ -121,6 +121,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>Activity.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -143,6 +146,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>Activity.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -165,6 +171,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>Activity.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -187,6 +196,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>Activity.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/sensors/CustomSensors/CustomSensors.vcxproj b/sensors/CustomSensors/CustomSensors.vcxproj index 113ddd23..7e2d3b5a 100644 --- a/sensors/CustomSensors/CustomSensors.vcxproj +++ b/sensors/CustomSensors/CustomSensors.vcxproj @@ -121,6 +121,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>CustomSensors.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -143,6 +146,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>CustomSensors.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -165,6 +171,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>CustomSensors.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -187,6 +196,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>CustomSensors.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/sensors/Fusion/FusionSensor.vcxproj b/sensors/Fusion/FusionSensor.vcxproj index bdcad389..362e350d 100644 --- a/sensors/Fusion/FusionSensor.vcxproj +++ b/sensors/Fusion/FusionSensor.vcxproj @@ -121,6 +121,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>FusionSensor.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -142,6 +145,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>FusionSensor.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -163,6 +169,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>FusionSensor.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -184,6 +193,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>FusionSensor.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/sensors/Pedometer/Pedometer.vcxproj b/sensors/Pedometer/Pedometer.vcxproj index ee5738d7..3ada5e9d 100644 --- a/sensors/Pedometer/Pedometer.vcxproj +++ b/sensors/Pedometer/Pedometer.vcxproj @@ -121,6 +121,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>Pedometer.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -143,6 +146,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>Pedometer.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -165,6 +171,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>Pedometer.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -187,6 +196,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>Pedometer.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/sensors/SensorsComboDriver/SensorsComboDriver.vcxproj b/sensors/SensorsComboDriver/SensorsComboDriver.vcxproj index 381f5a03..0871cfc0 100644 --- a/sensors/SensorsComboDriver/SensorsComboDriver.vcxproj +++ b/sensors/SensorsComboDriver/SensorsComboDriver.vcxproj @@ -119,6 +119,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -139,6 +142,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -159,6 +165,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -179,6 +188,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj index ee3d87a3..591fe423 100644 --- a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj +++ b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj @@ -121,6 +121,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>SimpleDeviceOrientationSensor.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -143,6 +146,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>SimpleDeviceOrientationSensor.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -165,6 +171,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>SimpleDeviceOrientationSensor.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -187,6 +196,9 @@ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\sensors\1.1\sensorscxstub.lib;$(SDK_LIB_PATH)\sensorsutils.lib</AdditionalDependencies> <ModuleDefinitionFile>SimpleDeviceOrientationSensor.def</ModuleDefinitionFile> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/serial/serial/wdfserial.vcxproj b/serial/serial/wdfserial.vcxproj index 30db6172..fb761163 100644 --- a/serial/serial/wdfserial.vcxproj +++ b/serial/serial/wdfserial.vcxproj @@ -317,6 +317,9 @@ <ResourceCompile> <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions> </ResourceCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -330,6 +333,9 @@ <ResourceCompile> <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions> </ResourceCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -343,6 +349,9 @@ <ResourceCompile> <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions> </ResourceCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -356,6 +365,9 @@ <ResourceCompile> <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions> </ResourceCompile> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="precompsrc.c"> diff --git a/simbatt/func/simbatt.vcxproj b/simbatt/func/simbatt.vcxproj index 3f0fd780..a9ab3b9b 100644 --- a/simbatt/func/simbatt.vcxproj +++ b/simbatt/func/simbatt.vcxproj @@ -108,6 +108,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\battc.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -129,6 +132,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\battc.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -150,6 +156,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\battc.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -171,6 +180,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\battc.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="miniclass.c" /> @@ -190,4 +202,4 @@ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project> +</Project>
\ No newline at end of file diff --git a/simbatt/func/simbatt.vcxproj.Filters b/simbatt/func/simbatt.vcxproj.Filters index d49b9ea2..42b85d1a 100644 --- a/simbatt/func/simbatt.vcxproj.Filters +++ b/simbatt/func/simbatt.vcxproj.Filters @@ -31,4 +31,23 @@ <Filter>Resource Files</Filter> </ResourceCompile> </ItemGroup> + <ItemGroup> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Inf Include="*.inx"> + <Filter>Driver Files</Filter> + </Inf> + <Inf Include="*.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/storage/class/classpnp/src/autorun.c b/storage/class/classpnp/src/autorun.c index f0a88414..5cb5ca3d 100644 --- a/storage/class/classpnp/src/autorun.c +++ b/storage/class/classpnp/src/autorun.c @@ -210,9 +210,9 @@ ClassSendNotification( return; } - notification = ExAllocatePoolWithTag(NonPagedPoolNx, - requiredSize, - 'oNcS'); + notification = ExAllocatePoolZero(NonPagedPoolNx, + requiredSize, + 'oNcS'); // // if none allocated, exit @@ -226,7 +226,6 @@ ClassSendNotification( // Prepare and send the request! // - RtlZeroMemory(notification, requiredSize); notification->Version = 1; notification->Size = (USHORT)(requiredSize); notification->FileObject = NULL; @@ -1838,13 +1837,11 @@ ClasspInitializePolling( return STATUS_SUCCESS; } - info = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(MEDIA_CHANGE_DETECTION_INFO), - CLASS_TAG_MEDIA_CHANGE_DETECTION); + info = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(MEDIA_CHANGE_DETECTION_INFO), + CLASS_TAG_MEDIA_CHANGE_DETECTION); if (info != NULL) { - RtlZeroMemory(info, sizeof(MEDIA_CHANGE_DETECTION_INFO)); - FdoExtension->KernelModeMcnContext.FileObject = (PVOID)-1; FdoExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1; FdoExtension->KernelModeMcnContext.LockCount = 0; @@ -1862,10 +1859,10 @@ ClasspInitializePolling( PVOID buffer; BOOLEAN GesnSupported = FALSE; - buffer = ExAllocatePoolWithTag( - NonPagedPoolNxCacheAligned, - SENSE_BUFFER_SIZE_EX, - CLASS_TAG_MEDIA_CHANGE_DETECTION); + buffer = ExAllocatePoolZero( + NonPagedPoolNxCacheAligned, + SENSE_BUFFER_SIZE_EX, + CLASS_TAG_MEDIA_CHANGE_DETECTION); if (buffer != NULL) { @@ -2032,9 +2029,10 @@ ClasspInitializeGesn( // if (Info->Gesn.Buffer == NULL) { - Info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, - GESN_BUFFER_SIZE, - '??cS'); + Info->Gesn.Buffer = ExAllocatePoolZero( + NonPagedPoolNxCacheAligned, + GESN_BUFFER_SIZE, + '??cS'); } if (Info->Gesn.Buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; @@ -2816,10 +2814,9 @@ ClasspIsMediaChangeDisabledDueToHardwareLimitation( deviceString.Length = (USHORT)( length ); deviceString.MaximumLength = deviceString.Length + 1; - deviceString.Buffer = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolNx, - deviceString.MaximumLength, - CLASS_TAG_AUTORUN_DISABLE - ); + deviceString.Buffer = (PCHAR)ExAllocatePoolZero( NonPagedPoolNx, + deviceString.MaximumLength, + CLASS_TAG_AUTORUN_DISABLE ); if (deviceString.Buffer == NULL) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassMediaChangeDisabledForHardware: Unable to alloc " @@ -4277,9 +4274,9 @@ ClassSetFailurePredictionPoll( if (FailurePredictionMethod != FailurePredictionNone) { - info = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(FAILURE_PREDICTION_INFO), - CLASS_TAG_FAILURE_PREDICT); + info = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(FAILURE_PREDICTION_INFO), + CLASS_TAG_FAILURE_PREDICT); if (info == NULL) { diff --git a/storage/class/classpnp/src/class.c b/storage/class/classpnp/src/class.c index 5e7f00f1..58d1835a 100644 --- a/storage/class/classpnp/src/class.c +++ b/storage/class/classpnp/src/class.c @@ -222,6 +222,11 @@ ClassInitialize( PAGED_CODE(); // + // Must be called before calling pool allocation functions. + // + ExInitializeDriverRuntime(0); + + // // Initialize the security cookie if needed. // if (InitSecurityCookie == FALSE) { @@ -348,9 +353,9 @@ ClassInitialize( driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength; driverExtension->RegistryPath.Buffer = - ExAllocatePoolWithTag(PagedPool, - RegistryPath->MaximumLength, - '1CcS'); + ExAllocatePoolZero(PagedPool, + RegistryPath->MaximumLength, + '1CcS'); if(driverExtension->RegistryPath.Buffer == NULL) { @@ -557,10 +562,9 @@ ClassInitializeEx( } else { - info = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(CLASS_WORKING_SET), - CLASS_TAG_WORKING_SET - ); + info = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(CLASS_WORKING_SET), + CLASS_TAG_WORKING_SET); if (info == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; @@ -626,10 +630,9 @@ ClassInitializeEx( } else { - info = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(CLASS_INTERPRET_SENSE_INFO2), - CLASS_TAG_SENSE2 - ); + info = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(CLASS_INTERPRET_SENSE_INFO2), + CLASS_TAG_SENSE2); if (info == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; @@ -960,15 +963,12 @@ ClassDispatchPnp( status = STATUS_INSUFFICIENT_RESOURCES; - deviceRelations = ExAllocatePoolWithTag(PagedPool, - sizeof(DEVICE_RELATIONS), - '2CcS'); + deviceRelations = ExAllocatePoolZero(PagedPool, + sizeof(DEVICE_RELATIONS), + '2CcS'); if(deviceRelations != NULL) { - RtlZeroMemory(deviceRelations, - sizeof(DEVICE_RELATIONS)); - Irp->IoStatus.Information = (ULONG_PTR) deviceRelations; deviceRelations->Count = 1; @@ -1836,10 +1836,10 @@ NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject) // if (fdoExtension->PrivateFdoData == NULL) { - fdoExtension->PrivateFdoData = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(CLASS_PRIVATE_FDO_DATA), - CLASS_TAG_PRIVATE_DATA - ); + fdoExtension->PrivateFdoData = ExAllocatePoolZero( + NonPagedPoolNx, + sizeof(CLASS_PRIVATE_FDO_DATA), + CLASS_TAG_PRIVATE_DATA); } if (fdoExtension->PrivateFdoData == NULL) { @@ -1856,10 +1856,10 @@ NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject) // This structure's memory is managed by classpnp, so it is more extensible. // if (fdoExtension->AdditionalFdoData == NULL) { - fdoExtension->AdditionalFdoData = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(ADDITIONAL_FDO_DATA), - CLASSPNP_POOL_TAG_ADDITIONAL_DATA - ); + fdoExtension->AdditionalFdoData = ExAllocatePoolZero( + NonPagedPoolNx, + sizeof(ADDITIONAL_FDO_DATA), + CLASSPNP_POOL_TAG_ADDITIONAL_DATA); } if (fdoExtension->AdditionalFdoData == NULL) { @@ -1885,10 +1885,11 @@ NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject) // if (fdoExtension->FunctionSupportInfo == NULL) { - fdoExtension->FunctionSupportInfo = (PCLASS_FUNCTION_SUPPORT_INFO)ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(CLASS_FUNCTION_SUPPORT_INFO), - '3BcS' - ); + fdoExtension->FunctionSupportInfo = + (PCLASS_FUNCTION_SUPPORT_INFO)ExAllocatePoolZero( + NonPagedPoolNx, + sizeof(CLASS_FUNCTION_SUPPORT_INFO), + '3BcS'); } if (fdoExtension->FunctionSupportInfo == NULL) { @@ -3051,9 +3052,9 @@ ClassSendStartUnit( // Allocate Srb from nonpaged pool. // - context = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(COMPLETION_CONTEXT), - '6CcS'); + context = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(COMPLETION_CONTEXT), + '6CcS'); if (context == NULL) { @@ -4068,9 +4069,9 @@ ClassSendSrbSynchronous( #endif - senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, - senseInfoBufferLength, - '7CcS'); + senseInfoBuffer = ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + senseInfoBufferLength, + '7CcS'); if (senseInfoBuffer == NULL) { @@ -7443,7 +7444,7 @@ ClassDeviceControl( break; } - valueName = ExAllocatePoolWithTag( + valueName = ExAllocatePoolZero( PagedPool, commonExtension->DeviceName.Length + sizeof(WCHAR), '8CcS'); @@ -7561,10 +7562,9 @@ ClassDeviceControl( sizeNeeded = sizeof(SCSI_REQUEST_BLOCK); } - srb = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeNeeded + - (sizeof(ULONG_PTR) * 2), - '9CcS'); + srb = ExAllocatePoolZero(NonPagedPoolNx, + sizeNeeded + (sizeof(ULONG_PTR) * 2), + '9CcS'); if (srb == NULL) { @@ -7593,7 +7593,6 @@ ClassDeviceControl( goto SetStatusAndReturn; } } else { - RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); srb->Length = sizeof(SCSI_REQUEST_BLOCK); srb->Function = SRB_FUNCTION_EXECUTE_SCSI; function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1)); @@ -9431,16 +9430,16 @@ ClassQueryTimeOutRegistryValue( return 0; } - parameters = ExAllocatePoolWithTag(NonPagedPoolNx, - sizeof(RTL_QUERY_REGISTRY_TABLE)*2, - '1BcS'); + parameters = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(RTL_QUERY_REGISTRY_TABLE) * 2, + '1BcS'); if (!parameters) { return 0; } size = registryPath->MaximumLength + sizeof(WCHAR); - path = ExAllocatePoolWithTag(NonPagedPoolNx, size, '2BcS'); + path = ExAllocatePoolZero(NonPagedPoolNx, size, '2BcS'); if (!path) { FREE_POOL(parameters); @@ -9455,9 +9454,6 @@ ClassQueryTimeOutRegistryValue( // Check for the Timeout value. // - RtlZeroMemory(parameters, - (sizeof(RTL_QUERY_REGISTRY_TABLE)*2)); - parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK; parameters[0].Name = L"TimeOutValue"; parameters[0].EntryContext = &timeOut; @@ -9625,7 +9621,7 @@ ClassGetDescriptor( if(!NT_SUCCESS(ioStatus.Status)) { - TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: error %lx trying to " + TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: error %lx trying to " "query properties #1\n", ioStatus.Status)); return ioStatus.Status; } @@ -9651,7 +9647,7 @@ ClassGetDescriptor( NT_ASSERT(length >= sizeof(STORAGE_PROPERTY_QUERY)); length = max(length, sizeof(STORAGE_PROPERTY_QUERY)); - descriptor = ExAllocatePoolWithTag(NonPagedPoolNx, length, '4BcS'); + descriptor = ExAllocatePoolZero(NonPagedPoolNx, length, '4BcS'); if(descriptor == NULL) { @@ -9672,8 +9668,6 @@ ClassGetDescriptor( // copy the input to the new outputbuffer // - RtlZeroMemory(descriptor, length); - RtlCopyMemory(descriptor, &query, sizeof(STORAGE_PROPERTY_QUERY) @@ -9989,7 +9983,7 @@ ClassRetrieveDeviceRelations( relationsSize = (sizeof(DEVICE_RELATIONS) + (count * sizeof(PDEVICE_OBJECT))); - deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS'); + deviceRelations = ExAllocatePoolZero(PagedPool, relationsSize, '5BcS'); if (deviceRelations == NULL) { @@ -10001,8 +9995,6 @@ ClassRetrieveDeviceRelations( return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(deviceRelations, relationsSize); - if (oldRelations) { // @@ -11056,9 +11048,11 @@ ClassSendDeviceIoControlSynchronous( { if ((InputBufferLength != 0) || (OutputBufferLength != 0)) { - irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, - max(InputBufferLength, OutputBufferLength), - CLASS_TAG_DEVICE_CONTROL); + irp->AssociatedIrp.SystemBuffer = + ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + max(InputBufferLength, OutputBufferLength), + CLASS_TAG_DEVICE_CONTROL); + if (irp->AssociatedIrp.SystemBuffer == NULL) { IoFreeIrp(irp); @@ -11543,10 +11537,9 @@ ClasspAllocateReleaseQueueIrp( NT_ASSERT(!(FdoExtension->ReleaseQueueInProgress)); FdoExtension->PrivateFdoData->ReleaseQueueIrp = - ExAllocatePoolWithTag(NonPagedPoolNx, - IoSizeOfIrp(lowerStackSize), - CLASS_TAG_RELEASE_QUEUE - ); + ExAllocatePoolZero(NonPagedPoolNx, + IoSizeOfIrp(lowerStackSize), + CLASS_TAG_RELEASE_QUEUE); if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate for " @@ -11590,10 +11583,10 @@ ClasspAllocatePowerProcessIrp( stackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1; - FdoExtension->PrivateFdoData->PowerProcessIrp = ExAllocatePoolWithTag(NonPagedPoolNx, - IoSizeOfIrp(stackSize), - CLASS_TAG_POWER - ); + FdoExtension->PrivateFdoData->PowerProcessIrp = + ExAllocatePoolZero(NonPagedPoolNx, + IoSizeOfIrp(stackSize), + CLASS_TAG_POWER); if (FdoExtension->PrivateFdoData->PowerProcessIrp == NULL) { @@ -12824,17 +12817,15 @@ ClasspGetInquiryVpdSupportInfo( // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64 // based platforms. We are taking the conservative approach here. // - allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment()); - supportedPages = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, - allocationBufferLength, - '3CcS' - ); + allocationBufferLength = ALIGN_UP_BY(allocationBufferLength , KeGetRecommendedSharedDataAlignment()); + supportedPages = ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + allocationBufferLength, + '3CcS'); #else - supportedPages = ExAllocatePoolWithTag(NonPagedPoolNx, - bufferLength, - '3CcS' - ); + supportedPages = ExAllocatePoolZero(NonPagedPoolNx, + bufferLength, + '3CcS'); #endif if (supportedPages == NULL) { @@ -12842,8 +12833,6 @@ ClasspGetInquiryVpdSupportInfo( return; } - RtlZeroMemory(supportedPages, allocationBufferLength); - // prepare the Srb if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) { @@ -13006,7 +12995,7 @@ ClasspGetLBProvisioningInfo( srbSize = sizeof(SCSI_REQUEST_BLOCK); } - srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, '0DcS'); + srb = ExAllocatePoolZero(NonPagedPoolNx, srbSize, '0DcS'); if (srb == NULL) { return STATUS_INSUFFICIENT_RESOURCES; @@ -13162,7 +13151,7 @@ Return Value: srbSize = sizeof(SCSI_REQUEST_BLOCK); } - srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, CLASSPNP_POOL_TAG_SRB); + srb = ExAllocatePoolZero(NonPagedPoolNx, srbSize, CLASSPNP_POOL_TAG_SRB); if (!srb) { @@ -13181,9 +13170,13 @@ Return Value: // based platforms. We are taking the conservative approach here. // allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment()); - dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, CLASSPNP_POOL_TAG_VPD); + dataBuffer = ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + allocationBufferLength, + CLASSPNP_POOL_TAG_VPD); #else - dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CLASSPNP_POOL_TAG_VPD); + dataBuffer = ExAllocatePoolZero(NonPagedPoolNx, + bufferLength, + CLASSPNP_POOL_TAG_VPD); #endif if (!dataBuffer) { @@ -13199,8 +13192,6 @@ Return Value: operatingParameters = (PVPD_THIRD_PARTY_COPY_PAGE)dataBuffer; - RtlZeroMemory(dataBuffer, allocationBufferLength); - if ((fdoExtension->AdapterDescriptor != NULL) && (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) { status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srb, @@ -13230,7 +13221,6 @@ Return Value: } } else { - RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); srb->Length = sizeof(SCSI_REQUEST_BLOCK); srb->Function = SRB_FUNCTION_EXECUTE_SCSI; } @@ -13816,10 +13806,9 @@ Return Value: allocationSize = sizeof(OFFLOAD_READ_CONTEXT) + bufferLength; - offloadReadContext = ExAllocatePoolWithTag( - NonPagedPoolNx, - allocationSize, - CLASSPNP_POOL_TAG_TOKEN_OPERATION); + offloadReadContext = ExAllocatePoolZero(NonPagedPoolNx, + allocationSize, + CLASSPNP_POOL_TAG_TOKEN_OPERATION); if (!offloadReadContext) { @@ -13832,8 +13821,6 @@ Return Value: goto __ClasspServicePopulateTokenTransferRequest_ErrorExit; } - RtlZeroMemory(offloadReadContext, allocationSize); - offloadReadContext->Fdo = Fdo; offloadReadContext->OffloadReadDsmIrp = Irp; @@ -14885,10 +14872,9 @@ Return Value: allocationSize = sizeof(OFFLOAD_WRITE_CONTEXT) + bufferLength; - offloadWriteContext = ExAllocatePoolWithTag( - NonPagedPoolNx, - allocationSize, - CLASSPNP_POOL_TAG_TOKEN_OPERATION); + offloadWriteContext = ExAllocatePoolZero(NonPagedPoolNx, + allocationSize, + CLASSPNP_POOL_TAG_TOKEN_OPERATION); if (!offloadWriteContext) { @@ -14901,12 +14887,6 @@ Return Value: goto __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit; } - // - // Only zero the context portion here. The buffer portion is zeroed for - // each sub-request. - // - RtlZeroMemory(offloadWriteContext, sizeof(OFFLOAD_WRITE_CONTEXT)); - offloadWriteContext->Fdo = Fdo; offloadWriteContext->OffloadWriteDsmIrp = Irp; offloadWriteContext->OperationStartTime = KeQueryInterruptTime(); @@ -16343,7 +16323,7 @@ Return Value: srbSize = sizeof(SCSI_REQUEST_BLOCK); } - srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, '1DcS'); + srb = ExAllocatePoolZero(NonPagedPoolNx, srbSize, '1DcS'); if (srb == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } diff --git a/storage/class/classpnp/src/classp.h b/storage/class/classpnp/src/classp.h index d7d57e9d..1e2331c2 100644 --- a/storage/class/classpnp/src/classp.h +++ b/storage/class/classpnp/src/classp.h @@ -26,6 +26,7 @@ Revision History: --*/ #define RTL_USE_AVL_TABLES 0 +#define POOL_ZERO_DOWN_LEVEL_SUPPORT #include <stddef.h> #include <stdarg.h> diff --git a/storage/class/classpnp/src/classwmi.c b/storage/class/classpnp/src/classwmi.c index 1b1eeeee..e21ec356 100644 --- a/storage/class/classpnp/src/classwmi.c +++ b/storage/class/classpnp/src/classwmi.c @@ -1175,10 +1175,9 @@ ClassWmiFireEvent( sizeNeeded = sizeof(WNODE_SINGLE_INSTANCE) + EventDataSize; - event = ExAllocatePoolWithTag(NonPagedPoolNx, sizeNeeded, CLASS_TAG_WMI); + event = ExAllocatePoolZero(NonPagedPoolNx, sizeNeeded, CLASS_TAG_WMI); if (event != NULL) { - RtlZeroMemory(event, sizeNeeded); event->WnodeHeader.Guid = *Guid; event->WnodeHeader.ProviderId = IoWMIDeviceObjectToProviderId(DeviceObject); event->WnodeHeader.BufferSize = sizeNeeded; diff --git a/storage/class/classpnp/src/debug.c b/storage/class/classpnp/src/debug.c index cc288ec6..b811c6d0 100644 --- a/storage/class/classpnp/src/debug.c +++ b/storage/class/classpnp/src/debug.c @@ -165,8 +165,9 @@ Revision History: "ClassDebugPrint: Allocating %x bytes for " "classdebugprint buffer\n", (ULONG)bufferSize); ClasspnpGlobals.Index = (ULONG)-1; - ClasspnpGlobals.Buffer = - ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, 'bDcS'); + ClasspnpGlobals.Buffer = ExAllocatePoolZero(NonPagedPoolNx, + bufferSize, + 'bDcS'); DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL, "ClassDebugPrint: Allocated buffer at %p\n", ClasspnpGlobals.Buffer); diff --git a/storage/class/classpnp/src/dictlib.c b/storage/class/classpnp/src/dictlib.c index 90a67117..73a7e5ca 100644 --- a/storage/class/classpnp/src/dictlib.c +++ b/storage/class/classpnp/src/dictlib.c @@ -78,15 +78,14 @@ AllocateDictionaryEntry( *Entry = NULL; - header = ExAllocatePoolWithTag(NonPagedPoolNx, - Size + sizeof(DICTIONARY_HEADER), - Tag); + header = ExAllocatePoolZero(NonPagedPoolNx, + Size + sizeof(DICTIONARY_HEADER), + Tag); if(header == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(header, sizeof(DICTIONARY_HEADER) + Size); header->Key = Key; // diff --git a/storage/class/classpnp/src/lock.c b/storage/class/classpnp/src/lock.c index 1b5a8862..1d0d240c 100644 --- a/storage/class/classpnp/src/lock.c +++ b/storage/class/classpnp/src/lock.c @@ -472,7 +472,9 @@ RemoveTrackingAllocateRoutine( { UNREFERENCED_PARAMETER(Table); - return ExAllocatePoolWithTag(NonPagedPoolNx, ByteSize, CLASS_TAG_LOCK_TRACKING); + return ExAllocatePoolZero(NonPagedPoolNx, + ByteSize, + CLASS_TAG_LOCK_TRACKING); } VOID @@ -496,7 +498,10 @@ ClasspInitializeRemoveTracking( #if DBG KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock); - commonExtension->RemoveTrackingList = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(RTL_GENERIC_TABLE), CLASS_TAG_LOCK_TRACKING); + commonExtension->RemoveTrackingList = + ExAllocatePoolZero(NonPagedPoolNx, + sizeof(RTL_GENERIC_TABLE), + CLASS_TAG_LOCK_TRACKING); if (commonExtension->RemoveTrackingList != NULL) { diff --git a/storage/class/classpnp/src/power.c b/storage/class/classpnp/src/power.c index 790dba4a..75a6c05d 100644 --- a/storage/class/classpnp/src/power.c +++ b/storage/class/classpnp/src/power.c @@ -2615,7 +2615,9 @@ Return Value: // // Put this FDO on the list of devices that are idle power managed. // - fdoEntry = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(IDLE_POWER_FDO_LIST_ENTRY), CLASS_TAG_POWER); + fdoEntry = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(IDLE_POWER_FDO_LIST_ENTRY), + CLASS_TAG_POWER); if (fdoEntry) { fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled = TRUE; diff --git a/storage/class/classpnp/src/srblib.c b/storage/class/classpnp/src/srblib.c index 64a7ca88..f56ae419 100644 --- a/storage/class/classpnp/src/srblib.c +++ b/storage/class/classpnp/src/srblib.c @@ -44,7 +44,7 @@ Return Value: --*/ { - return ExAllocatePoolWithTag(NonPagedPoolNx, ByteSize, '+brs'); + return ExAllocatePoolZero(NonPagedPoolNx, ByteSize, '+brs'); } diff --git a/storage/class/classpnp/src/utils.c b/storage/class/classpnp/src/utils.c index 323d7449..120031eb 100644 --- a/storage/class/classpnp/src/utils.c +++ b/storage/class/classpnp/src/utils.c @@ -1296,9 +1296,9 @@ NTSTATUS ClasspWriteCacheProperty( status = STATUS_SUCCESS; } - modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, - MODE_PAGE_DATA_SIZE, - CLASS_TAG_MODE_DATA); + modeData = ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + MODE_PAGE_DATA_SIZE, + CLASS_TAG_MODE_DATA); if (modeData == NULL) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Unable to allocate mode data buffer\n")); @@ -1306,8 +1306,6 @@ NTSTATUS ClasspWriteCacheProperty( goto WriteCacheExit; } - RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE); - length = ClassModeSense(DeviceObject, (PCHAR) modeData, MODE_PAGE_DATA_SIZE, @@ -1598,10 +1596,16 @@ ClassReadCapacity16 ( // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64 // based platforms. We are taking the conservative approach here. // - allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment()); - dataBuffer = (PREAD_CAPACITY16_DATA)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, '4CcS'); + allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment()); + dataBuffer = (PREAD_CAPACITY16_DATA) + ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + allocationBufferLength, + '4CcS'); #else - dataBuffer = (PREAD_CAPACITY16_DATA)ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, '4CcS'); + dataBuffer = (PREAD_CAPACITY16_DATA) + ExAllocatePoolZero(NonPagedPoolNx, + bufferLength, + '4CcS'); #endif if (dataBuffer == NULL) { @@ -1610,8 +1614,6 @@ ClassReadCapacity16 ( return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(dataBuffer, allocationBufferLength); - // // Initialize the SRB. // @@ -2097,25 +2099,23 @@ Return Value: // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64 // based platforms. We are taking the conservative approach here. // - allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment()); - dataBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, - allocationBufferLength, - '5CcS' - ); + allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment()); + dataBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE) + ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + allocationBufferLength, + '5CcS'); #else - dataBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)ExAllocatePoolWithTag(NonPagedPoolNx, - bufferLength, - '5CcS' - ); + dataBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE) + ExAllocatePoolZero(NonPagedPoolNx, + bufferLength, + '5CcS'); #endif if (dataBuffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } - RtlZeroMemory(dataBuffer, allocationBufferLength); - // prepare the Srb SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue); SrbSetRequestTag(Srb, SP_UNTAGGED); @@ -2384,10 +2384,14 @@ NTSTATUS ClasspDeviceGetLBProvisioningVPDPage( // based platforms. We are taking the conservative approach here. // // - allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment()); - dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength,'0CcS'); + allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment()); + dataBuffer = ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + allocationBufferLength, + '0CcS'); #else - dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength,'0CcS'); + dataBuffer = ExAllocatePoolZero(NonPagedPoolNx, + bufferLength, + '0CcS'); #endif if (dataBuffer == NULL) { // return without updating FdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus @@ -2398,8 +2402,6 @@ NTSTATUS ClasspDeviceGetLBProvisioningVPDPage( lbProvisioning = (PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE)dataBuffer; - RtlZeroMemory(dataBuffer, allocationBufferLength); - if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) { status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb, STORAGE_ADDRESS_TYPE_BTL8, @@ -2559,9 +2561,13 @@ NTSTATUS ClasspDeviceGetBlockLimitsVPDPage( // based platforms. We are taking the conservative approach here. // allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment()); - dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, '0CcS'); + dataBuffer = ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + allocationBufferLength, + '0CcS'); #else - dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, '0CcS'); + dataBuffer = ExAllocatePoolZero(NonPagedPoolNx, + bufferLength, + '0CcS'); #endif if (dataBuffer == NULL) { @@ -2573,8 +2579,6 @@ NTSTATUS ClasspDeviceGetBlockLimitsVPDPage( blockLimits = (PVPD_BLOCK_LIMITS_PAGE)dataBuffer; - RtlZeroMemory(dataBuffer, allocationBufferLength); - if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) { #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded") @@ -3305,9 +3309,15 @@ Return Value: // based platforms. We are taking the conservative approach here. // bufferLength = ALIGN_UP_BY(bufferLength,KeGetRecommendedSharedDataAlignment()); - buffer = (PUNMAP_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, bufferLength, CLASS_TAG_LB_PROVISIONING); + buffer = (PUNMAP_LIST_HEADER) + ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + bufferLength, + CLASS_TAG_LB_PROVISIONING); #else - buffer = (PUNMAP_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CLASS_TAG_LB_PROVISIONING); + buffer = (PUNMAP_LIST_HEADER) + ExAllocatePoolZero(NonPagedPoolNx, + bufferLength, + CLASS_TAG_LB_PROVISIONING); #endif if (buffer == NULL) { @@ -3315,8 +3325,6 @@ Return Value: goto Exit; } - RtlZeroMemory(buffer, bufferLength); - blockDescrPointer = &buffer->Descriptors[0]; allDataSetRangeFullyConverted = FALSE; @@ -4373,9 +4381,15 @@ Return Value: // based platforms. We are taking the conservative approach here. // lbaStatusSize = ALIGN_UP_BY(lbaStatusSize,KeGetRecommendedSharedDataAlignment()); - lbaStatusListHeader = (PLBA_STATUS_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, lbaStatusSize, CLASS_TAG_LB_PROVISIONING); + lbaStatusListHeader = (PLBA_STATUS_LIST_HEADER) + ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + lbaStatusSize, + CLASS_TAG_LB_PROVISIONING); #else - lbaStatusListHeader = (PLBA_STATUS_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNx, lbaStatusSize, CLASS_TAG_LB_PROVISIONING); + lbaStatusListHeader = (PLBA_STATUS_LIST_HEADER) + ExAllocatePoolZero(NonPagedPoolNx, + lbaStatusSize, + CLASS_TAG_LB_PROVISIONING); #endif if (lbaStatusListHeader == NULL) @@ -5032,9 +5046,15 @@ Return Value: // based platforms. We are taking the conservative approach here. // logPageSize = ALIGN_UP_BY(logPageSize, KeGetRecommendedSharedDataAlignment()); - logPage = (PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, logPageSize, CLASS_TAG_LB_PROVISIONING); + logPage = (PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING) + ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + logPageSize, + CLASS_TAG_LB_PROVISIONING); #else - logPage = (PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING)ExAllocatePoolWithTag(NonPagedPoolNx, logPageSize, CLASS_TAG_LB_PROVISIONING); + logPage = (PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING) + ExAllocatePoolZero(NonPagedPoolNx, + logPageSize, + CLASS_TAG_LB_PROVISIONING); #endif if (logPage != NULL) { @@ -5166,9 +5186,9 @@ Arguments: srbSize = sizeof(SCSI_REQUEST_BLOCK); } - srb = ExAllocatePoolWithTag(NonPagedPoolNx, - srbSize, - 'ACcS'); + srb = ExAllocatePoolZero(NonPagedPoolNx, + srbSize, + 'ACcS'); if (srb != NULL) { // @@ -6046,7 +6066,9 @@ Arguments: } if (SenseBufferSize) { - senseData = ExAllocatePoolWithTag(NonPagedPoolNx, SenseBufferSize, CLASSPNP_POOL_TAG_LOG_MESSAGE); + senseData = ExAllocatePoolZero(NonPagedPoolNx, + SenseBufferSize, + CLASSPNP_POOL_TAG_LOG_MESSAGE); if (senseData) { senseBufferSize = SenseBufferSize; } @@ -6070,7 +6092,11 @@ Arguments: PIO_RETRIED_LOG_MESSAGE_CONTEXT ioLogMessageContext = NULL; - ioLogMessageContext = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(IO_RETRIED_LOG_MESSAGE_CONTEXT), CLASSPNP_POOL_TAG_LOG_MESSAGE); + ioLogMessageContext = + ExAllocatePoolZero(NonPagedPoolNx, + sizeof(IO_RETRIED_LOG_MESSAGE_CONTEXT), + CLASSPNP_POOL_TAG_LOG_MESSAGE); + if (!ioLogMessageContext) { goto __ClasspQueueLogIOEventWithContextWorker_ExitWithMessage; } @@ -7756,7 +7782,9 @@ Return Value: // // Allocate the buffer. // - buffer = ExAllocatePoolWithTag(NonPagedPoolNx, actualLength, CLASSPNP_POOL_TAG_TOKEN_OPERATION); + buffer = ExAllocatePoolZero(NonPagedPoolNx, + actualLength, + CLASSPNP_POOL_TAG_TOKEN_OPERATION); if (!buffer) { TracePrint((TRACE_LEVEL_ERROR, @@ -7768,8 +7796,6 @@ Return Value: goto __ClasspBinaryToAscii_Exit; } - RtlZeroMemory(buffer, actualLength); - for (i = 0, j = 0; i < Length; i++) { // @@ -7941,17 +7967,15 @@ Return Value: PMODE_CONTROL_PAGE pageData = NULL; ULONG size = 0; - modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, - MODE_PAGE_DATA_SIZE, - CLASS_TAG_MODE_DATA); + modeData = ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + MODE_PAGE_DATA_SIZE, + CLASS_TAG_MODE_DATA); if (modeData == NULL) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_SCSI, "ClasspZeroQERR: Unable to allocate mode data buffer\n")); goto ClasspZeroQERR_Exit; } - RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE); - size = ClassModeSense(DeviceObject, (PCHAR) modeData, MODE_PAGE_DATA_SIZE, @@ -8192,15 +8216,15 @@ ClasspGetHwFirmwareInfo( // retry: - firmwareInfo = ExAllocatePoolWithTag(NonPagedPoolNx, dataLength, CLASSPNP_POOL_TAG_FIRMWARE); + firmwareInfo = ExAllocatePoolZero(NonPagedPoolNx, + dataLength, + CLASSPNP_POOL_TAG_FIRMWARE); if (firmwareInfo == NULL) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClasspGetHwFirmwareInfo: cannot allocate memory to hold data. \n")); return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(firmwareInfo, dataLength); - // // Set up query data, making sure the "Flags" field indicating the request is for device itself. // @@ -8705,15 +8729,15 @@ ClassDeviceHwFirmwareDownloadProcess( lockHeld = FALSE; #pragma prefast(suppress:6014, "The allocated memory that firmwareImageBuffer points to will be freed in ClassHwFirmwareDownloadComplete().") - firmwareImageBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, CLASSPNP_POOL_TAG_FIRMWARE); + firmwareImageBuffer = ExAllocatePoolZero(NonPagedPoolNx, + bufferSize, + CLASSPNP_POOL_TAG_FIRMWARE); if (firmwareImageBuffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Exit_Firmware_Download; } - RtlZeroMemory(firmwareImageBuffer, bufferSize); - RtlCopyMemory(firmwareImageBuffer, firmwareDownload->ImageBuffer, (ULONG)firmwareDownload->BufferSize); } else { diff --git a/storage/class/classpnp/src/xferpkt.c b/storage/class/classpnp/src/xferpkt.c index 29019140..75300c9a 100644 --- a/storage/class/classpnp/src/xferpkt.c +++ b/storage/class/classpnp/src/xferpkt.c @@ -77,9 +77,9 @@ NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo) // arraySize = KeQueryHighestNodeNumber() + 1; fdoData->FreeTransferPacketsLists = - ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, - sizeof(PNL_SLIST_HEADER) * arraySize, - CLASS_TAG_PRIVATE_DATA); + ExAllocatePoolZero(NonPagedPoolNxCacheAligned, + sizeof(PNL_SLIST_HEADER) * arraySize, + CLASS_TAG_PRIVATE_DATA); if (fdoData->FreeTransferPacketsLists == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; @@ -264,11 +264,12 @@ NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo) NT_ASSERT(FALSE); } } else { - fdoData->SrbTemplate = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK), '-brs'); + fdoData->SrbTemplate = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(SCSI_REQUEST_BLOCK), + '-brs'); if (fdoData->SrbTemplate == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } else { - RtlZeroMemory(fdoData->SrbTemplate, sizeof(SCSI_REQUEST_BLOCK)); fdoData->SrbTemplate->Length = sizeof(SCSI_REQUEST_BLOCK); fdoData->SrbTemplate->Function = SRB_FUNCTION_EXECUTE_SCSI; } @@ -342,47 +343,51 @@ PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo) * Allocate the actual packet. */ if (NT_SUCCESS(status)) { - newPkt = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(TRANSFER_PACKET), 'pnPC'); + newPkt = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(TRANSFER_PACKET), + 'pnPC'); if (newPkt == NULL) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate transfer packet.")); status = STATUS_INSUFFICIENT_RESOURCES; } else { - RtlZeroMemory(newPkt, sizeof(TRANSFER_PACKET)); newPkt->AllocateNode = KeGetCurrentNodeNumber(); if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) { #if (NTDDI_VERSION >= NTDDI_WINBLUE) if ((fdoExt->MiniportDescriptor != NULL) && (fdoExt->MiniportDescriptor->Size >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_MINIPORT_DESCRIPTOR, ExtraIoInfoSupported)) && (fdoExt->MiniportDescriptor->ExtraIoInfoSupported == TRUE)) { - status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb, - fdoExt->AdapterDescriptor->AddressType, - DefaultStorageRequestBlockAllocateRoutine, - NULL, - 2, - SrbExDataTypeScsiCdb16, - SrbExDataTypeIoInfo - ); + + status = CreateStorageRequestBlock( + (PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb, + fdoExt->AdapterDescriptor->AddressType, + DefaultStorageRequestBlockAllocateRoutine, + NULL, + 2, + SrbExDataTypeScsiCdb16, + SrbExDataTypeIoInfo); } else { - status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb, - fdoExt->AdapterDescriptor->AddressType, - DefaultStorageRequestBlockAllocateRoutine, - NULL, - 1, - SrbExDataTypeScsiCdb16 - ); + status = CreateStorageRequestBlock( + (PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb, + fdoExt->AdapterDescriptor->AddressType, + DefaultStorageRequestBlockAllocateRoutine, + NULL, + 1, + SrbExDataTypeScsiCdb16); } #else - status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb, - fdoExt->AdapterDescriptor->AddressType, - DefaultStorageRequestBlockAllocateRoutine, - NULL, - 1, - SrbExDataTypeScsiCdb16 - ); + status = CreateStorageRequestBlock( + (PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb, + fdoExt->AdapterDescriptor->AddressType, + DefaultStorageRequestBlockAllocateRoutine, + NULL, + 1, + SrbExDataTypeScsiCdb16); #endif } else { #pragma prefast(suppress:6014, "The allocated memory that Pkt->Srb points to will be freed in DestroyTransferPacket().") - newPkt->Srb = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK), '-brs'); + newPkt->Srb = ExAllocatePoolZero(NonPagedPoolNx, + sizeof(SCSI_REQUEST_BLOCK), + '-brs'); if (newPkt->Srb == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } @@ -447,7 +452,9 @@ PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo) historyByteCount = sizeof(SRB_HISTORY_ITEM) * fdoData->InterpretSenseInfo->HistoryCount; historyByteCount += sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM); - newPkt->RetryHistory = (PSRB_HISTORY)ExAllocatePoolWithTag(NonPagedPoolNx, historyByteCount, 'hrPC'); + newPkt->RetryHistory = (PSRB_HISTORY)ExAllocatePoolZero(NonPagedPoolNx, + historyByteCount, + 'hrPC'); if (newPkt->RetryHistory == NULL) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate MDL for transfer packet.")); diff --git a/storage/miniports/storahci/src/ahci.h b/storage/miniports/storahci/src/ahci.h index 29e29f65..b11c24fe 100644 --- a/storage/miniports/storahci/src/ahci.h +++ b/storage/miniports/storahci/src/ahci.h @@ -322,9 +322,9 @@ typedef struct _AHCI_COMMAND_TABLE { UCHAR CFIS_Padding[44]; UCHAR ACMD[16]; //AHCI1.0 section 4.2.3.2 ATAPI Command (ACMD): This is a software constructed region of 12 or 16 bytes in length that contains the ATAPI command to transmit if the ?A? bit is set in the command header. The ATAPI command must be either 12 or 16 bytes in length. The length transmitted by the HBA is determined by the PIO setup FIS that is sent by the device requesting the ATAPI command. UCHAR Reserved1[48]; - AHCI_PRDT PRDT[33]; //elements in a sglist are 4K (except for maybe the first and last one). STORAHCI limits transfer size cap to be 128K, therefore no more than 33 PRD entries. - //if the PRD Table had 32 entries this structure would come out to 640 which has nice 128 byte alignment (required for all instances of these structures), - UCHAR Reserved2[112];//but since it has 33 entries it comes out to 656. another 112 bytes at the end is needed to restore 128 byte alignment and make the structure 768 bytes + AHCI_PRDT PRDT[257]; //elements in a sglist are 4K (except for maybe the first and last one). STORAHCI limits transfer size cap default to be 128K, max to 1M (configure registry MaxTransferSize to 1024), therefore no more than 257 PRD entries. + //if the PRD Table had 256 entries this structure would come out to 4224 which has nice 128 byte alignment (required for all instances of these structures), + UCHAR Reserved2[112];//but since it has 257 entries it comes out to 4240. another 112 bytes at the end is needed to restore 128 byte alignment and make the structure 4352 bytes } AHCI_COMMAND_TABLE, *PAHCI_COMMAND_TABLE; @@ -439,7 +439,7 @@ typedef union _AHCI_INTERRUPT_STATUS { ULONG UFS :1; //Unknown FIS Interrupt: When set to '1 , indicates that an unknown FIS was received and has been copied into system memory. This bit is cleared to ?0 by software clearing the PxSERR.DIAG.F bit to ?0 . Note that this bit does not directly reflect the PxSERR.DIAG.F bit. PxSERR.DIAG.F is set immediately when an unknown FIS is detected, whereas this bit is set when that FIS is posted to memory. Software should wait to act on an unknown FIS until this bit is set to ?1 or the two bits may become out of sync. ULONG DPS :1; //Descriptor Processed: A PRD with the 'I' bit set has transferred all of its data. Refer to section 5.3.2. ULONG PCS :1; //Port Connect Change Status: 1=Change in Current Connect Status. 0=No change in Current Connect Status. This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared when PxSERR.DIAG.X is cleared. - ULONG DMPS :1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device. This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to �1�. + ULONG DMPS :1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device. This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to '1'. ULONG DW4_Reserved :14; ULONG PRCS :1; //PhyRdy Change Status: When set to '1 indicates the internal PhyRdy signal changed state. This bit reflects the state of P0SERR.DIAG.N. To clear this bit, software must clear P0SERR.DIAG.N to ?0 . ULONG IPMS :1; //Incorrect Port Multiplier Status: Indicates that the HBA received a FIS from a device whose Port Multiplier field did not match what was expected. The IPMS bit may be set during enumeration of devices on a Port Multiplier due to the normal Port Multiplier enumeration process. It is recommended that IPMS only be used after enumeration is complete on the Port Multiplier. @@ -470,7 +470,7 @@ typedef union _AHCI_INTERRUPT_ENABLE { ULONG UFE :1; // Unknown FIS Interrupt Enable: When set, GHC.IE is set, and P0IS.UFS is set to ?1 , the HBA shall generate an interrupt. ULONG DPE :1; // Descriptor Processed Interrupt Enable: When set, GHC.IE is set, and P0IS.DPS is set, the HBA shall generate an interrupt. ULONG PCE :1; // Port Change Interrupt Enable: When set, GHC.IE is set, and P0IS.PCS is set, the HBA shall generate an interrupt. - ULONG DMPE :1; //Device Mechanical Presence Enable (DMPE): When set, and GHC.IE is set to �1�, and P0IS.DMPS is set, the HBA shall generate an interrupt. For systems that do not support a mechanical presence switch, this bit shall be a read-only �0�. + ULONG DMPE :1; //Device Mechanical Presence Enable (DMPE): When set, and GHC.IE is set to '1', and P0IS.DMPS is set, the HBA shall generate an interrupt. For systems that do not support a mechanical presence switch, this bit shall be a read-only '0'. ULONG DW5_Reserved :14; // ULONG PRCE :1; // PhyRdy Change Interrupt Enable: When set to ?1 , and GHC.IE is set to ?1 , and P0IS.PRCS is set to ?1 , the HBA shall generate an interrupt. ULONG IPME :1; // Incorrect Port Multiplier Enable: When set, and GHC.IE and P0IS.IPMS are set, the HBA shall generate an interrupt. @@ -494,8 +494,8 @@ typedef union _AHCI_COMMAND { struct { //LSB - ULONG ST :1; // Start: When set, the HBA may process the command list. When cleared, the HBA may not process the command list. Whenever this bit is changed from a ?0 to a, the HBA starts processing the command list at entry ?0 . Whenever this bit is changed from a ?1 to a ?0 , the PxCI register is cleared by the HBA upon the HBA putting the controller into an idle state. This bit shall only be set to �1� by software after PxCMD.FRE has been set to �1�. Refer to section 10.3.1 for important restrictions on when ST can be set to ?1 . - ULONG SUD :1; // Spin-Up Device: This bit is read/write for HBAs that support staggered spin-up via CAP.SSS. This bit is read only ?1 for HBAs that do not support staggered spin-up. On an edge detect from ?0 to ?1 , the HBA shall start a COMRESET initialization sequence to the device. Clearing this bit to �0� does not cause any OOB signal to be sent on the interface. When this bit is cleared to �0� and PxSCTL.DET=0h, the HBA will enter listen mode as detailed in section 10.9.1. + ULONG ST :1; // Start: When set, the HBA may process the command list. When cleared, the HBA may not process the command list. Whenever this bit is changed from a ?0 to a, the HBA starts processing the command list at entry ?0 . Whenever this bit is changed from a ?1 to a ?0 , the PxCI register is cleared by the HBA upon the HBA putting the controller into an idle state. This bit shall only be set to '1' by software after PxCMD.FRE has been set to '1'. Refer to section 10.3.1 for important restrictions on when ST can be set to ?1 . + ULONG SUD :1; // Spin-Up Device: This bit is read/write for HBAs that support staggered spin-up via CAP.SSS. This bit is read only ?1 for HBAs that do not support staggered spin-up. On an edge detect from ?0 to ?1 , the HBA shall start a COMRESET initialization sequence to the device. Clearing this bit to '0' does not cause any OOB signal to be sent on the interface. When this bit is cleared to '0' and PxSCTL.DET=0h, the HBA will enter listen mode as detailed in section 10.9.1. ULONG POD :1; // Power On Device: This bit is read/write for HBAs that support cold presence detection on this port as indicated by PxCMD.CPD set to ?1 . This bit is read only ?1 for HBAs that do not support cold presence detect. When set, the HBA sets the state of a pin on the HBA to ?1 so that it may be used to provide power to a cold-presence detectable port. ULONG CLO :1; // Command List Override: Setting this bit to ?1 causes PxTFD.STS.BSY and PxTFD.STS.DRQ to be cleared to ?0 . This allows a software reset to be transmitted to the device regardless of whether the BSY and DRQ bits are still set in the PxTFD.STS register. The HBA sets this bit to ?0 when PxTFD.STS.BSY and PxTFD.STS.DRQ have been cleared to ?0 . A write to this register with a value of ?0 shall have no effect. This bit shall only be set to ?1 immediately prior to setting the PxCMD.ST bit to ?1 from previous value of ?0 . Setting this bit to ?1 at any other time is not supported and will result in indeterminate behavior. ULONG FRE :1; // FIS Receive Enable: When set, the HBA may post received FISes into the FIS receive area pointed to by PxFB (and for 64-bit HBAs, PxFBU). When cleared, received FISes are not accepted by the HBA, except for the first D2H register FIS after the initialization sequence, and no FISes are posted to the FIS receive area. System software must not set this bit until PxFB (PxFBU) have been programmed with valid pointer to the FIS receive area, and if software wishes to move the base, this bit must first be cleared, and software must wait for the FR bit in this register to be cleared. Refer to section 10.3.2 for important restrictions on when FRE can be set and cleared. @@ -508,17 +508,17 @@ typedef union _AHCI_COMMAND { ULONG CPS :1; // Cold Presence State: The CPS bit reports whether a device is currently detected on this port. If CPS is set to ?1 , then the HBA detects via cold presence that a device is attached to this port. If CPS is cleared to ?0 , then the HBA detects via cold presence that there is no device attached to this port. ULONG PMA :1; // Port Multiplier Attached: This bit is read/write for HBAs that support a Port Multiplier (CAP.SPM = ?1 ). This bit is read-only for HBAs that do not support a port Multiplier (CAP.SPM = ?0 ). When set to ?1 by software, a Port Multiplier is attached to the HBA for this port. When cleared to ?0 by software, a Port Multiplier is not attached to the HBA for this port. Software is responsible for detecting whether a Port Multiplier is present; hardware does not auto-detect the presence of a Port Multiplier. - ULONG HPCP :1; // Hot Plug Capable Port: When set to �1�, indicates that this port�s signal and power connectors are externally accessible via a joint signal and power connector for blindmate device hot plug. When cleared to �0�, indicates that this port�s signal and power connectors are not externally accessible via a joint signal and power connector. + ULONG HPCP :1; // Hot Plug Capable Port: When set to '1', indicates that this port's signal and power connectors are externally accessible via a joint signal and power connector for blindmate device hot plug. When cleared to '0', indicates that this port's signal and power connectors are not externally accessible via a joint signal and power connector. ULONG MPSP :1; // Mechanical Presence Switch Attached to Port : If set to ?1 , the platform supports an interlocked switch attached to this port. If cleared to ?0 , the platform does not support an interlocked switch attached to this port. When this bit is set to ?1 , P0CMD.HPCP should also be set to ?1 . ULONG CPD :1; // Cold Presence Detection: If set to ?1 , the platform supports cold presence detection on this port. If cleared to ?0 , the platform does not support cold presence detection on this port. When this bit is set to ?1 , P0CMD.HPCP should also be set to ?1 . - ULONG ESP :1; //AHCI 1.1 External SATA Port: When set to '1', indicates that this port�s signal connector is externally accessible on a signal only connector. When set to '1', CAP.SXS shall be set to '1'. When cleared to �0�, indicates that this port�s signal connector is not externally accessible on a signal only connector. ESP is mutually exclusive with the HPCP bit in this register. - ULONG FBSCP :1; // FIS-based Switching Capable Port (FBSCP): When set to �1�, indicates that this port supports Port Multiplier FIS-based switching. When cleared to �0�, indicates that this port does not support FIS-based switching. This bit may only be set to �1� if both CAP.SPM and CAP.FBSS are set to �1�. - ULONG APSTE :1; // Automatic Partial to Slumber Transitions Enabled (APSTE): When set to �1�, the HBA may perform Automatic Partial to Slumber Transitions. When cleared to �0� the port shall not perform Automatic Partial to Slumber Transitions. Software shall only set this bit to �1� if CAP2.APST is set to �1�; if CAP2.APST is cleared to �0� software shall treat this bit as reserved. + ULONG ESP :1; //AHCI 1.1 External SATA Port: When set to '1', indicates that this port's signal connector is externally accessible on a signal only connector. When set to '1', CAP.SXS shall be set to '1'. When cleared to '0', indicates that this port's signal connector is not externally accessible on a signal only connector. ESP is mutually exclusive with the HPCP bit in this register. + ULONG FBSCP :1; // FIS-based Switching Capable Port (FBSCP): When set to '1', indicates that this port supports Port Multiplier FIS-based switching. When cleared to '0', indicates that this port does not support FIS-based switching. This bit may only be set to '1' if both CAP.SPM and CAP.FBSS are set to '1'. + ULONG APSTE :1; // Automatic Partial to Slumber Transitions Enabled (APSTE): When set to '1', the HBA may perform Automatic Partial to Slumber Transitions. When cleared to '0' the port shall not perform Automatic Partial to Slumber Transitions. Software shall only set this bit to '1' if CAP2.APST is set to '1'; if CAP2.APST is cleared to '0' software shall treat this bit as reserved. ULONG ATAPI :1; // Device is ATAPI: When set, the connected device is an ATAPI device. This bit is used by the HBA to control whether or not to generate the desktop LED when commands are active. See section 10.10 for details on the activity LED. ULONG DLAE :1; // Drive LED on ATAPI Enable: When set, the HBA shall drive the LED pin active for commands regardless of the state of P0CMD.ATAPI. When cleared, the HBA shall only drive the LED pin active for commands if P0CMD.ATAPI set to ?0 . See section 10.10 for details on the activity LED. - ULONG ALPE :1; // Aggressive Link Power Management Enable: When set to '1', the HBA shall aggressively enter a lower link power state (Partial or Slumber) based upon the setting of the ASP bit. Software shall only set this bit to �1� if CAP.SALP is set to �1�; if CAP.SALP is cleared to �0� software shall treat this bit as reserved. See section 8.3.1.3 for details. - ULONG ASP :1; // Aggressive Slumber / Partial: When set to '1', and ALPE is set, the HBA shall aggressively enter the Slumber state when it clears the PxCI register and the PxSACT register is cleared or when it clears the PxSACT register and PxCI is cleared. When cleared, and ALPE is set, the HBA shall aggressively enter the Partial state when it clears the PxCI register and the PxSACT register is cleared or when it clears the PxSACT register and PxCI is cleared. If CAP.SALP is cleared to �0� software shall treat this bit as reserved. See section 8.3.1.3 for details. + ULONG ALPE :1; // Aggressive Link Power Management Enable: When set to '1', the HBA shall aggressively enter a lower link power state (Partial or Slumber) based upon the setting of the ASP bit. Software shall only set this bit to '1' if CAP.SALP is set to '1'; if CAP.SALP is cleared to '0' software shall treat this bit as reserved. See section 8.3.1.3 for details. + ULONG ASP :1; // Aggressive Slumber / Partial: When set to '1', and ALPE is set, the HBA shall aggressively enter the Slumber state when it clears the PxCI register and the PxSACT register is cleared or when it clears the PxSACT register and PxCI is cleared. When cleared, and ALPE is set, the HBA shall aggressively enter the Partial state when it clears the PxCI register and the PxSACT register is cleared or when it clears the PxSACT register and PxCI is cleared. If CAP.SALP is cleared to '0' software shall treat this bit as reserved. See section 8.3.1.3 for details. ULONG ICC :4; // Interface Communication Control: This field is used to control power management states of the interface. If the Link layer is currently in the L_IDLE state, writes to this field shall cause the HBA to initiate a transition to the interface power management state requested. If the Link layer is not currently in the L_IDLE state, writes to this field shall have no effect. //MSB }; @@ -532,12 +532,12 @@ typedef union _AHCI_DEVICE_SLEEP { struct { //LSB - ULONG ADSE :1; // Aggressive Device Sleep Enable (ADSE): When this bit is set to �1�, the HBA shall assert the DEVSLP signal after the port has been idle (PxCI = 0h and PxSACT = 0h) for the amount of time specified by the PxDEVSLP.DITO register. When this bit is set to �1� and CAP2.DESO is set to �1�, the HBA shall assert the DEVSLP signal after the port has been idle (PxCI = 0h and PxSACT = 0h) for the amount of time specified by PxDEVSLP.DITO and the interface is in Slumber (PxSSTS.IPM = 6h).When this bit is cleared to �0�, the HBA does not enter DevSleep unless software directed via PxCMD.ICC. This bit shall only be set to �1� if PxDEVSLP.DSP is set to �1�. - ULONG DSP :1; // Device Sleep Present (DSP): If set to �1�, the platform supports Device Sleep on this port. If cleared to �0�, the platform does not support Device Sleep on this port. This bit may only be set to �1� if CAP2.SDS is set to �1�. DSP is mutually exclusive with the PxCMD.HPCP bit and PxCMD.ESP bit. + ULONG ADSE :1; // Aggressive Device Sleep Enable (ADSE): When this bit is set to '1', the HBA shall assert the DEVSLP signal after the port has been idle (PxCI = 0h and PxSACT = 0h) for the amount of time specified by the PxDEVSLP.DITO register. When this bit is set to '1' and CAP2.DESO is set to '1', the HBA shall assert the DEVSLP signal after the port has been idle (PxCI = 0h and PxSACT = 0h) for the amount of time specified by PxDEVSLP.DITO and the interface is in Slumber (PxSSTS.IPM = 6h).When this bit is cleared to '0', the HBA does not enter DevSleep unless software directed via PxCMD.ICC. This bit shall only be set to '1' if PxDEVSLP.DSP is set to '1'. + ULONG DSP :1; // Device Sleep Present (DSP): If set to '1', the platform supports Device Sleep on this port. If cleared to '0', the platform does not support Device Sleep on this port. This bit may only be set to '1' if CAP2.SDS is set to '1'. DSP is mutually exclusive with the PxCMD.HPCP bit and PxCMD.ESP bit. ULONG DETO :8; // Device Sleep Exit Timeout (DETO): This field specifies the maximum duration (in approximate 1ms granularity) from DEVSLP de-assertion until the device is ready to accept OOB. The nominal value is 20ms while the max value is 255ms depending on device identification information. ULONG MDAT :5; // Minimum Device Sleep Assertion Time (MDAT): This field specifies the minimum amount of time (in 1ms granularity) that the HBA must assert the DEVSLP signal before it may be de-asserted. The nominal value is 10ms and the minimum is 1ms depending on device identification information. - ULONG DITO :10; // Device Sleep Idle Timeout (DITO): This field specifies the amount of the time (in approximate 1ms granularity) that the HBA shall wait before driving the DEVSLP signal. Refer to section 8.5.1.1.1 for details. Hardware reloads its port specific Device Sleep timer with this value each time the port transitions out of the DEVSLP state. For example: from DevSleep to active or PxDEVSLP.ADSE transitions from �0� to a �1�. - ULONG DM :4; // DITO Multiplier (DM): 0�s based value that specifies the DITO multiplier that the HBA applies to the specified DITO value, effectively extending the range of DITO from 1ms to 16368ms. A value of 0h indicates a multiplier of 1. A maximum multiplier of 16 may be applied. The HBA computes the total idle timeout as a product of DM and DITO (i.e. DITOactual = DITO * (DM+1)). + ULONG DITO :10; // Device Sleep Idle Timeout (DITO): This field specifies the amount of the time (in approximate 1ms granularity) that the HBA shall wait before driving the DEVSLP signal. Refer to section 8.5.1.1.1 for details. Hardware reloads its port specific Device Sleep timer with this value each time the port transitions out of the DEVSLP state. For example: from DevSleep to active or PxDEVSLP.ADSE transitions from '0' to a '1'. + ULONG DM :4; // DITO Multiplier (DM): 0's based value that specifies the DITO multiplier that the HBA applies to the specified DITO value, effectively extending the range of DITO from 1ms to 16368ms. A value of 0h indicates a multiplier of 1. A maximum multiplier of 16 may be applied. The HBA computes the total idle timeout as a product of DM and DITO (i.e. DITOactual = DITO * (DM+1)). ULONG Reserved :3; //MSB @@ -733,10 +733,10 @@ typedef struct _AHCI_PORT { AHCI_SERIAL_ATA_ERROR SERR; //Serial ATA Active as defined in AHCI1.0 section 3.3.13 - ULONG SACT; //Device Status: Device Status (DS): This field is bit significant. Each bit corresponds to the TAG and command slot of a native queued command, where bit 0 corresponds to TAG 0 and command slot 0. This field is set by software prior to issuing a native queued command for a particular command slot. Prior to writing PxCI[TAG] to �1�, software will set DS[TAG] to �1� to indicate that a command with that TAG is outstanding. The device clears bits in this field by sending a Set Device Bits FIS to the host. The HBA clears bits in this field that are set to �1� in the SActive field of the Set Device Bits FIS. The HBA only clears bits that correspond to native queued commands that have completed successfully. Software should only write this field when PxCMD.ST is set to �1�. This field is cleared when PxCMD.ST is written from a �1� to a �0� by software. This field is not cleared by a COMRESET or a software reset. + ULONG SACT; //Device Status: Device Status (DS): This field is bit significant. Each bit corresponds to the TAG and command slot of a native queued command, where bit 0 corresponds to TAG 0 and command slot 0. This field is set by software prior to issuing a native queued command for a particular command slot. Prior to writing PxCI[TAG] to '1', software will set DS[TAG] to '1' to indicate that a command with that TAG is outstanding. The device clears bits in this field by sending a Set Device Bits FIS to the host. The HBA clears bits in this field that are set to '1' in the SActive field of the Set Device Bits FIS. The HBA only clears bits that correspond to native queued commands that have completed successfully. Software should only write this field when PxCMD.ST is set to '1'. This field is cleared when PxCMD.ST is written from a '1' to a '0' by software. This field is not cleared by a COMRESET or a software reset. //Command Issue as defined in AHCI1.0 section 3.3.14 - ULONG CI; //Commands Issued: This field is bit significant. Each bit corresponds to a command slot, where bit 0 corresponds to command slot 0. This field is set by software to indicate to the HBA that a command has been built in system memory for a command slot and may be sent to the device. When the HBA receives a FIS which clears the BSY, DRQ, and ERR bits for the command, it clears the corresponding bit in this register for that command slot. Bits in this field shall only be set to �1� by software when PxCMD.ST is set to �1�. This field is also cleared when PxCMD.ST is written from a �1� to a �0� by software. + ULONG CI; //Commands Issued: This field is bit significant. Each bit corresponds to a command slot, where bit 0 corresponds to command slot 0. This field is set by software to indicate to the HBA that a command has been built in system memory for a command slot and may be sent to the device. When the HBA receives a FIS which clears the BSY, DRQ, and ERR bits for the command, it clears the corresponding bit in this register for that command slot. Bits in this field shall only be set to '1' by software when PxCMD.ST is set to '1'. This field is also cleared when PxCMD.ST is written from a '1' to a '0' by software. AHCI_SNOTIFICATION SNTF; @@ -757,24 +757,24 @@ typedef union _AHCI_HBA_CAPABILITIES { struct { //LSB ULONG NP :5; //Number of Ports: 0 s based value indicating the maximum number of ports supported by the HBA silicon. A maximum of 32 ports can be supported. A value of ?0h?, indicating one port, is the minimum requirement. Note that the number of ports indicated in this field may be more than the number of ports indicated in the GHC.PI register. - ULONG SXS :1; //AHCI 1.1 Supports External SATA: When set to �1�, indicates that the HBA has one or more Serial ATA ports that has a signal only connector that is externally accessible. If this bit is set, software may refer to the PxCMD.ESP bit to determine whether a specific port has its signal connector externally accessible as a signal only connector (i.e. power is not part of that connector). When the bit is cleared to �0�, indicates that the HBA has no Serial ATA ports that have a signal only connector externally accessible. - ULONG EMS :1; //AHCI 1.1 Enclosure Management Supported: When set to �1�, indicates that the HBA supports enclosure management as defined in section 12. When enclosure management is supported, the HBA has implemented the EM_LOC and EM_CTL global HBA registers. When cleared to �0�, indicates that the HBA does not support enclosure management and the EM_LOC and EM_CTL global HBA registers are not implemented. - ULONG CCCS :1; //AHCI 1.1 Command Completion Coalescing Supported: When set to �1�, indicates that the HBA supports command completion coalescing as defined in section 11. When command completion coalescing is supported, the HBA has implemented the CCC_CTL and the CCC_PORTS global HBA registers. When cleared to �0�, indicates that the HBA does not support command completion coalescing and the CCC_CTL and CCC_PORTS global HBA registers are not implemented. + ULONG SXS :1; //AHCI 1.1 Supports External SATA: When set to '1', indicates that the HBA has one or more Serial ATA ports that has a signal only connector that is externally accessible. If this bit is set, software may refer to the PxCMD.ESP bit to determine whether a specific port has its signal connector externally accessible as a signal only connector (i.e. power is not part of that connector). When the bit is cleared to '0', indicates that the HBA has no Serial ATA ports that have a signal only connector externally accessible. + ULONG EMS :1; //AHCI 1.1 Enclosure Management Supported: When set to '1', indicates that the HBA supports enclosure management as defined in section 12. When enclosure management is supported, the HBA has implemented the EM_LOC and EM_CTL global HBA registers. When cleared to '0', indicates that the HBA does not support enclosure management and the EM_LOC and EM_CTL global HBA registers are not implemented. + ULONG CCCS :1; //AHCI 1.1 Command Completion Coalescing Supported: When set to '1', indicates that the HBA supports command completion coalescing as defined in section 11. When command completion coalescing is supported, the HBA has implemented the CCC_CTL and the CCC_PORTS global HBA registers. When cleared to '0', indicates that the HBA does not support command completion coalescing and the CCC_CTL and CCC_PORTS global HBA registers are not implemented. ULONG NCS :5; //Number of Command Slots: 0 s based value indicating the number of command slots supported by this HBA. A minimum of 1 and maximum of 32 slots can be supported. ULONG PSC :1; //Partial State Capable: Indicates whether the HBA can support transitions to the Partial state. When cleared to ?0 , software must not allow the HBA to initiate transitions to the Partial state via aggressive link power management nor the PxCMD.ICC field in each port, and the PxSCTL.IPM field in each port must be programmed to disallow device initiated Partial requests. When set to ?1 , HBA and device initiated Partial requests can be supported. ULONG SSC :1; //Slumber State Capable: Indicates whether the HBA can support transitions to the Slumber state. When cleared to ?0 , software must not allow the HBA to initiate transitions to the Slumber state via aggressive link power management nor the PxCMD.ICC field in each port, and the PxSCTL.IPM field in each port must be programmed to disallow device initiated Slumber requests. When set to ?1 , HBA and device initiated Slumber requests can be supported. - ULONG PMD :1; //PIO Multiple DRQ Block: If set to �1 , the HBA supports multiple DRQ block data transfers for the PIO command protocol. If cleared to ?0 the HBA only supports single DRQ block data transfers for the PIO command protocol. - ULONG FBSS :1; //AHCI 1.1 FIS-based Switching Supported: When set to �1�, indicates that the HBA supports Port Multiplier FIS-based switching. When cleared to �0�, indicates that the HBA does not support FIS-based switching. AHCI 1.0 and 1.1 HBAs shall have this bit cleared to �0�. + ULONG PMD :1; //PIO Multiple DRQ Block: If set to '1 , the HBA supports multiple DRQ block data transfers for the PIO command protocol. If cleared to ?0 the HBA only supports single DRQ block data transfers for the PIO command protocol. + ULONG FBSS :1; //AHCI 1.1 FIS-based Switching Supported: When set to '1', indicates that the HBA supports Port Multiplier FIS-based switching. When cleared to '0', indicates that the HBA does not support FIS-based switching. AHCI 1.0 and 1.1 HBAs shall have this bit cleared to '0'. ULONG SPM :1; //Supports Port Multiplier: Indicates whether the HBA can support a Port Multiplier. When set, a Port Multiplier using command-based switching is supported. When cleared to ?0 , a Port Multiplier is not supported, and a Port Multiplier may not be attached to this HBA. ULONG SAM :1; //Supports AHCI mode only: The SATA controller may optionally support AHCI access mechanisms only. A value of '0' indicates that in addition to the native AHCI mechanism (via ABAR), the SATA controller implements a legacy, task-file based register interface such as SFF-8038i. A value of '1' indicates that the SATA controller does not implement a legacy, task-file based register interface. - ULONG SNZO :1; //Supports Non-Zero DMA Offsets: When set to �1 , indicates that the HBA can support non-zero DMA offsets for DMA Setup FISes. This bit is reserved for future AHCI enhancements. AHCI 1.0 HBAs must have this bit cleared to ?0 . + ULONG SNZO :1; //Supports Non-Zero DMA Offsets: When set to '1 , indicates that the HBA can support non-zero DMA offsets for DMA Setup FISes. This bit is reserved for future AHCI enhancements. AHCI 1.0 HBAs must have this bit cleared to ?0 . ULONG ISS :4; //Interface Speed Support: Indicates the maximum speed the HBA can support on its ports. These encodings match the PxSCTL.DET.SPD field, which is programmable by system software. Values are: - ULONG SCLO :1; //Supports Command List Override: When set to �1 , indicates that the HBA supports the PxCMD.CLO bit and its associated function. When cleared to ?0 , the HBA is not capable of clearing the BSY and DRQ bits in the Status register in order to issue a software reset if these bits are still set from a previous operation. - ULONG SAL :1; //Supports Activity LED: When set to �1 , indicates that the HBA supports a single output pin which indicates activity. This pin can be connected to an LED on the platform to indicate device activity on any drive. See section 10.10 for more information. + ULONG SCLO :1; //Supports Command List Override: When set to '1 , indicates that the HBA supports the PxCMD.CLO bit and its associated function. When cleared to ?0 , the HBA is not capable of clearing the BSY and DRQ bits in the Status register in order to issue a software reset if these bits are still set from a previous operation. + ULONG SAL :1; //Supports Activity LED: When set to '1 , indicates that the HBA supports a single output pin which indicates activity. This pin can be connected to an LED on the platform to indicate device activity on any drive. See section 10.10 for more information. ULONG SALP :1; //Supports Aggressive Link Power Management: When set to ?1 , indicates that the HBA can support auto-generating link requests to the Partial or Slumber states when there are no commands to process. Refer to section 8.3.1.3. - ULONG SSS :1; //Supports Staggered Spin-up: When set to �1 , indicates that the HBA supports staggered spin-up on its ports, for use in balancing power spikes. This value is loaded by the BIOS prior to OS initialization. - ULONG SMPS :1; //Supports Mechanical Presence Switch: ): When set to �1�, the HBA supports mechanical presence switches on its ports for use in hot plug operations. When cleared to �0�, this function is not supported. This value is loaded by the BIOS prior to OS initialization. - ULONG SSNTF :1; //AHCI 1.1 Supports SNotification Register: When set to �1�, indicates that the HBA supports the PxSNTF (SNotification) register and its associated functionality. When cleared to �0�, the HBA does not support the PxSNTF (SNotification) register and its associated functionality. Refer to section 10.10.1. + ULONG SSS :1; //Supports Staggered Spin-up: When set to '1 , indicates that the HBA supports staggered spin-up on its ports, for use in balancing power spikes. This value is loaded by the BIOS prior to OS initialization. + ULONG SMPS :1; //Supports Mechanical Presence Switch: ): When set to '1', the HBA supports mechanical presence switches on its ports for use in hot plug operations. When cleared to '0', this function is not supported. This value is loaded by the BIOS prior to OS initialization. + ULONG SSNTF :1; //AHCI 1.1 Supports SNotification Register: When set to '1', indicates that the HBA supports the PxSNTF (SNotification) register and its associated functionality. When cleared to '0', the HBA does not support the PxSNTF (SNotification) register and its associated functionality. Refer to section 10.10.1. ULONG SNCQ :1; //Supports Native Command Queuing: Indicates whether the HBA supports Serial ATA native command queuing. If set to ?1 , an HBA shall handle DMA Setup FISes natively, and shall handle the auto-activate optimization through that FIS. If cleared to ?0 , native command queuing is not supported and software should not issue any native command queuing commands. ULONG S64A :1; //Supports 64-bit Addressing: Indicates whether the HBA can access 64-bit data structures. If true, the HBA shall make the 32-bit upper bits of the port DMA Descriptor, the PRD Base, and each PRD entry read/write. If cleared, these are read- only and treated as ?0 by the HBA. //MSB @@ -791,7 +791,7 @@ typedef union _AHCI_Global_HBA_CONTROL { //LSB ULONG HR :1; //HBA Reset: When set by SW, this bit causes an internal reset of the HBA. All state machines that relate to data transfers and queuing shall return to an idle condition, and all ports shall be re-initialized via COMRESET (if staggered spin-up is not supported). If staggered spin-up is supported, then it is the responsibility of software to spin-up each port after the reset has completed. When the HBA has performed the reset action, it shall reset this bit to ?0 . A software write of ?0 shall have no effect. For a description on which bits are reset when this bit is set, see section 10.4.3. ULONG IE :1; //Interrupt Enable: This global bit enables interrupts from the HBA. When cleared (reset default), all interrupt sources from all ports are disabled. When set, interrupts are enabled. - ULONG MRSM :1; //MSI Revert to Single Message: When set to �1� by hardware, indicates that the HBA requested more than one MSI vector but has reverted to using the first vector only. When this bit is cleared to �0�, the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode, software has allocated the number of messages requested, or hardware is sharing interrupt vectors if MC.MME < MC.MMC). + ULONG MRSM :1; //MSI Revert to Single Message: When set to '1' by hardware, indicates that the HBA requested more than one MSI vector but has reverted to using the first vector only. When this bit is cleared to '0', the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode, software has allocated the number of messages requested, or hardware is sharing interrupt vectors if MC.MME < MC.MMC). ULONG Reserved :28; //3-30 ULONG AE :1; //AHCI Enable: When set, indicates that communication to the HBA shall be via AHCI mechanisms. This can be used by an HBA that supports both legacy mechanisms (such as SFF-8038i) and AHCI to know when the HBA is running under an AHCI driver. When set, software shall only communicate with the HBA using AHCI. When cleared, software shall only communicate with the HBA using legacy mechanisms. When cleared FISes are not posted to memory and no commands are sent via AHCI mechanisms. Software shall set this bit to ?1 before accessing other AHCI registers. The implementation of this bit is dependent upon the value of the CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and shall have a reset value of '0'. If CAP.SAM is '1', then AE shall be read-only and shall have a reset value of '1'. //MSB @@ -820,11 +820,11 @@ typedef union _AHCI_COMMAND_COMPLETION_COALESCING_CONTROL { struct { //LSB - ULONG EN: 1; //Enable: When cleared to �0�, the command completion coalescing feature is disabled and no CCC interrupts are generated. When set to �1�, the command completion coalescing feature is enabled and CCC interrupts may be generated based on timeout or command completion conditions. Software shall only change the contents of the TV and CC fields when EN is cleared to �0�. On transition of this bit from �0� to �1�, any updated values for the TV and CC fields shall take effect. + ULONG EN: 1; //Enable: When cleared to '0', the command completion coalescing feature is disabled and no CCC interrupts are generated. When set to '1', the command completion coalescing feature is enabled and CCC interrupts may be generated based on timeout or command completion conditions. Software shall only change the contents of the TV and CC fields when EN is cleared to '0'. On transition of this bit from '0' to '1', any updated values for the TV and CC fields shall take effect. ULONG Reserved: 2; - ULONG INT: 5; //Interrupt: Specifies the interrupt used by the CCC feature. This interrupt must be marked as unused in the Ports Implemented (PI) register by the corresponding bit being set to �0�. Thus, the CCC interrupt corresponds to the interrupt for an unimplemented port on the controller. When a CCC interrupt occurs, the IS.IPS[INT] bit shall be asserted to �1�. This field also specifies the interrupt vector used for MSI. - ULONG CC: 8; //Command Completions: Specifies the number of command completions that are necessary to cause a CCC interrupt. The HBA has an internal command completion counter, hCccComplete. hCccComplete is incremented by one each time a selected port has a command completion. When hCccComplete is equal to the command completions value, a CCC interrupt is signaled. The internal command completion counter is reset to �0� on the assertion of each CCC interrupt. A value of �0� for this field shall disable CCC interrupts being generated based on the number of commands completed, i.e. CCC interrupts are only generated based on the timer in this case. - ULONG TV: 16; //Timeout Value: The timeout value is specified in 1 millisecond intervals. The timer accuracy shall be within 5%. hCccTimer is loaded with this timeout value. hCccTimer is only decremented when commands are outstanding on selected ports, as defined in section 11.2. The HBA will signal a CCC interrupt when hCccTimer has decremented to �0�. hCccTimer is reset to the timeout value on the assertion of each CCC interrupt. A timeout value of �0� is reserved. + ULONG INT: 5; //Interrupt: Specifies the interrupt used by the CCC feature. This interrupt must be marked as unused in the Ports Implemented (PI) register by the corresponding bit being set to '0'. Thus, the CCC interrupt corresponds to the interrupt for an unimplemented port on the controller. When a CCC interrupt occurs, the IS.IPS[INT] bit shall be asserted to '1'. This field also specifies the interrupt vector used for MSI. + ULONG CC: 8; //Command Completions: Specifies the number of command completions that are necessary to cause a CCC interrupt. The HBA has an internal command completion counter, hCccComplete. hCccComplete is incremented by one each time a selected port has a command completion. When hCccComplete is equal to the command completions value, a CCC interrupt is signaled. The internal command completion counter is reset to '0' on the assertion of each CCC interrupt. A value of '0' for this field shall disable CCC interrupts being generated based on the number of commands completed, i.e. CCC interrupts are only generated based on the timer in this case. + ULONG TV: 16; //Timeout Value: The timeout value is specified in 1 millisecond intervals. The timer accuracy shall be within 5%. hCccTimer is loaded with this timeout value. hCccTimer is only decremented when commands are outstanding on selected ports, as defined in section 11.2. The HBA will signal a CCC interrupt when hCccTimer has decremented to '0'. hCccTimer is reset to the timeout value on the assertion of each CCC interrupt. A timeout value of '0' is reserved. //MSB }; @@ -837,7 +837,7 @@ typedef union _AHCI_ENCLOSURE_MANAGEMENT_LOCATION { struct { //LSB - ULONG SZ :16; //Buffer Size: Specifies the size of the transmit message buffer area in Dwords. If both transmit and receive buffers are supported, then the transmit buffer begins at ABAR[EM_LOC.OFST*4] and the receive buffer directly follows it. If both transmit and receive buffers are supported, both buffers are of the size indicated in the Buffer Size field. A value of �0� is invalid. + ULONG SZ :16; //Buffer Size: Specifies the size of the transmit message buffer area in Dwords. If both transmit and receive buffers are supported, then the transmit buffer begins at ABAR[EM_LOC.OFST*4] and the receive buffer directly follows it. If both transmit and receive buffers are supported, both buffers are of the size indicated in the Buffer Size field. A value of '0' is invalid. ULONG OFST :16; //Offset: The offset of the message buffer in Dwords from the beginning of the ABAR. //MSB }; @@ -851,20 +851,20 @@ typedef union _AHCI_ENCLOSURE_MANAGEMENT_CONTROL { struct { //LSB - ULONG STS_MR :1; //Message Received: The HBA sets this bit to a �1� when a message is completely received into the message buffer. When software detects this bit is a �1�, software should read the message and perform any actions necessary. When software is finished reading the message in the buffer, software writes a �1� to this bit in order to clear it. A write of �0� to this bit by software shall have no effect. + ULONG STS_MR :1; //Message Received: The HBA sets this bit to a '1' when a message is completely received into the message buffer. When software detects this bit is a '1', software should read the message and perform any actions necessary. When software is finished reading the message in the buffer, software writes a '1' to this bit in order to clear it. A write of '0' to this bit by software shall have no effect. ULONG Reserved0 :7; - ULONG CTL_TM :1; //Transmit Message: When set to �1� by software, the HBA shall transmit the message contained in the message buffer. When the message is completely sent, the HBA shall clear this bit to �0�. A write of �0� to this bit by software shall have no effect. Software shall not change the contents of the message buffer while CTL.TM is set to �1�. - ULONG CTL_RST :1; //Reset: When set to �1� by software, the HBA shall reset all enclosure management message logic and the attached enclosure processor (if applicable) and take all appropriate reset actions to ensure messages can be transmitted/received after the reset. After the HBA completes the reset operation, the HBA shall set the value to �0�. A write of �0� by software to this field shall have no effect. + ULONG CTL_TM :1; //Transmit Message: When set to '1' by software, the HBA shall transmit the message contained in the message buffer. When the message is completely sent, the HBA shall clear this bit to '0'. A write of '0' to this bit by software shall have no effect. Software shall not change the contents of the message buffer while CTL.TM is set to '1'. + ULONG CTL_RST :1; //Reset: When set to '1' by software, the HBA shall reset all enclosure management message logic and the attached enclosure processor (if applicable) and take all appropriate reset actions to ensure messages can be transmitted/received after the reset. After the HBA completes the reset operation, the HBA shall set the value to '0'. A write of '0' by software to this field shall have no effect. ULONG Reserved1 :6; - ULONG SUPP_LED :1; //LED Message Types: If set to �1�, the HBA supports the LED message type defined in section 12.2.1. - ULONG SUPP_SAFTE :1; //SAF-TE Enclosure Management Messages: If set to �1�, the HBA supports the SAF-TE message type. - ULONG SUPP_SES2 :1; //SES-2 Enclosure Management Messages: If set to �1�, the HBA supports the SES-2 message type. - ULONG SUPP_SGPIO :1; //SGPIO Enclosure Management Messages: If set to �1�, the HBA supports the SGPIO register interface message type. + ULONG SUPP_LED :1; //LED Message Types: If set to '1', the HBA supports the LED message type defined in section 12.2.1. + ULONG SUPP_SAFTE :1; //SAF-TE Enclosure Management Messages: If set to '1', the HBA supports the SAF-TE message type. + ULONG SUPP_SES2 :1; //SES-2 Enclosure Management Messages: If set to '1', the HBA supports the SES-2 message type. + ULONG SUPP_SGPIO :1; //SGPIO Enclosure Management Messages: If set to '1', the HBA supports the SGPIO register interface message type. ULONG Reserved2 :4; - ULONG ATTR_SMB :1; //Single Message Buffer: If set to �1�, the HBA has one message buffer that is shared for messages to transmit and messages received. In this case, unsolicited receive messages are not supported and it is software�s responsibility to manage access to this buffer. If cleared to �0�, there are separate receive and transmit buffers such that unsolicited messages could be supported. - ULONG ATTR_XMT :1; //Transmit Only: If set to �1�, the HBA only supports transmitting messages and does not support receiving messages. If cleared to �0�, the HBA supports transmitting and receiving messages. - ULONG ATTR_ALHD :1; //Activity LED Hardware Driven: If set to �1�, the HBA drives the activity LED for the LED message type in hardware and does not utilize software settings for this LED. The HBA does not begin transmitting the hardware based activity signal until after software has written CTL.TM=1 after a reset condition. - ULONG ATTR_PM :1; //Port Multiplier Support: If set to �1�, the HBA supports enclosure management messages for devices attached via a Port Multiplier. If cleared to �0�, the HBA does not support enclosure management messages for devices attached via a Port Multiplier. When cleared to �0�, software should use the Serial ATA enclosure management bridge that is built into many Port Multipliers for enclosure services with these devices. For more information on Serial ATA enclosure management bridges, refer to the Serial ATA Revision 2.5 specification. + ULONG ATTR_SMB :1; //Single Message Buffer: If set to '1', the HBA has one message buffer that is shared for messages to transmit and messages received. In this case, unsolicited receive messages are not supported and it is software's responsibility to manage access to this buffer. If cleared to '0', there are separate receive and transmit buffers such that unsolicited messages could be supported. + ULONG ATTR_XMT :1; //Transmit Only: If set to '1', the HBA only supports transmitting messages and does not support receiving messages. If cleared to '0', the HBA supports transmitting and receiving messages. + ULONG ATTR_ALHD :1; //Activity LED Hardware Driven: If set to '1', the HBA drives the activity LED for the LED message type in hardware and does not utilize software settings for this LED. The HBA does not begin transmitting the hardware based activity signal until after software has written CTL.TM=1 after a reset condition. + ULONG ATTR_PM :1; //Port Multiplier Support: If set to '1', the HBA supports enclosure management messages for devices attached via a Port Multiplier. If cleared to '0', the HBA does not support enclosure management messages for devices attached via a Port Multiplier. When cleared to '0', software should use the Serial ATA enclosure management bridge that is built into many Port Multipliers for enclosure services with these devices. For more information on Serial ATA enclosure management bridges, refer to the Serial ATA Revision 2.5 specification. ULONG Reserved3 :4; //MSB }; @@ -878,12 +878,12 @@ typedef union _AHCI_HBA_CAPABILITIES2 { struct { //LSB - ULONG BOH :1; //RO -- BIOS/OS Handoff: When set to �1�, the HBA supports the BIOS/OS handoff mechanism defined in section TBD. When cleared to �0�, the HBA does not support the BIOS/OS handoff mechanism. - ULONG NVMP :1; //RO -- NVMHCI Present: When set to '1', the HBA includes support for NVMHCI and the registers at offset 60h-9Fh are valid. When cleared to �0�, the HBA does not support NVMHCI. - ULONG APST :1; //RO -- Automatic Partial to Slumber Transitions (APST): When set to �1�, the HBA supports Automatic Partial to Slumber Transitions. When cleared to �0�, Automatic Partial to Slumber Transitions are not supported. - ULONG SDS :1; //RO -- Supports Device Sleep (SDS): When set to �1�, the HBA supports the Device Sleep feature. When cleared to �0�, DEVSLP is not supported and software shall not set PxCMD.ICC to �8h� - ULONG SADM :1; //RO -- Supports Aggressive Device Sleep Management (SADM): When set to �1�, the HBA supports hardware assertion of the DEVSLP signal after the idle timeout expires. When cleared to �0�, this function is not supported and software shall treat the PxDEVSLP.ADSE field as reserved. Refer to section 8.5.1. - ULONG DESO :1; //RO -- DevSleep Entrance from Slumber Only (DESO): This field specifies that the HBA shall only assert the DEVSLP signal if the interface is in Slumber. When this bit is set to �1�, the HBA shall ignore software directed entrance to DevSleep via PxCMD.ICC unless PxSSTS.IPM = 6h. When this bit is cleared to �0�, the HBA may enter DevSleep from any link state (active, Partial, or Slumber). + ULONG BOH :1; //RO -- BIOS/OS Handoff: When set to '1', the HBA supports the BIOS/OS handoff mechanism defined in section TBD. When cleared to '0', the HBA does not support the BIOS/OS handoff mechanism. + ULONG NVMP :1; //RO -- NVMHCI Present: When set to '1', the HBA includes support for NVMHCI and the registers at offset 60h-9Fh are valid. When cleared to '0', the HBA does not support NVMHCI. + ULONG APST :1; //RO -- Automatic Partial to Slumber Transitions (APST): When set to '1', the HBA supports Automatic Partial to Slumber Transitions. When cleared to '0', Automatic Partial to Slumber Transitions are not supported. + ULONG SDS :1; //RO -- Supports Device Sleep (SDS): When set to '1', the HBA supports the Device Sleep feature. When cleared to '0', DEVSLP is not supported and software shall not set PxCMD.ICC to '8h' + ULONG SADM :1; //RO -- Supports Aggressive Device Sleep Management (SADM): When set to '1', the HBA supports hardware assertion of the DEVSLP signal after the idle timeout expires. When cleared to '0', this function is not supported and software shall treat the PxDEVSLP.ADSE field as reserved. Refer to section 8.5.1. + ULONG DESO :1; //RO -- DevSleep Entrance from Slumber Only (DESO): This field specifies that the HBA shall only assert the DEVSLP signal if the interface is in Slumber. When this bit is set to '1', the HBA shall ignore software directed entrance to DevSleep via PxCMD.ICC unless PxSSTS.IPM = 6h. When this bit is cleared to '0', the HBA may enter DevSleep from any link state (active, Partial, or Slumber). ULONG Reserved :26; //MSB @@ -918,4 +918,3 @@ typedef struct _AHCI_MEMORY_REGISTERS{ #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/common.c b/storage/miniports/storahci/src/common.c index 2c1dd991..b8aa8667 100644 --- a/storage/miniports/storahci/src/common.c +++ b/storage/miniports/storahci/src/common.c @@ -91,10 +91,58 @@ AtaAlwaysSuccessRequestCompletion ( _In_ PSTORAGE_REQUEST_BLOCK Srb ); +__inline +BOOLEAN +IsExceptionCommand( + _In_ PCDB Cdb + ) +/* + Return true for enumeration and power transition related command. +*/ +{ + return ((Cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS) || + (Cdb->CDB10.OperationCode == SCSIOP_INQUIRY) || + (Cdb->CDB10.OperationCode == SCSIOP_START_STOP_UNIT) || + (Cdb->CDB10.OperationCode == SCSIOP_SECURITY_PROTOCOL_OUT) || + (Cdb->CDB10.OperationCode == SCSIOP_SECURITY_PROTOCOL_IN) || + (Cdb->CDB10.OperationCode == SCSIOP_SYNCHRONIZE_CACHE)); +} + +__inline +BOOLEAN +RetryCommand( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PCDB Cdb + ) +/* + If initialization triggered by power up in progress, return false; + else if port start/init/restore preserved settings command outstanding, and + command(e.g. enumeration and power transition related) is not in exception + list, return true; + Otherwise, return false. +*/ +{ + // + // If power up in progress, do not retry anything. + // + if (ChannelExtension->StateFlags.PowerUpInitializationInProgress) { + return FALSE; + } + + if ((ChannelExtension->StateFlags.ReservedSlotInUse || + ((ChannelExtension->StartState.ChannelNextStartState != StartComplete) && + (ChannelExtension->StartState.ChannelNextStartState != StartFailed))) && + (!IsExceptionCommand(Cdb))) { + return TRUE; + } + + return FALSE; +} + ULONG SCSItoATA( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, - _In_ PSTORAGE_REQUEST_BLOCK Srb + _In_ PSTORAGE_REQUEST_BLOCK Srb ) /* Note: If there is a need to send a command to device, @@ -103,9 +151,9 @@ SCSItoATA( Not setting this field can cause the Srb being completed earlier than expected. */ { - ULONG status; - ULONG cdbLength = 0; - PCDB cdb = RequestGetSrbScsiData(Srb, &cdbLength, NULL, NULL, NULL); + ULONG status; + ULONG cdbLength = 0; + PCDB cdb = RequestGetSrbScsiData(Srb, &cdbLength, NULL, NULL, NULL); if (cdb != NULL) { if (IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { @@ -114,8 +162,17 @@ SCSItoATA( } else if (IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters) || (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS) || (cdb->CDB10.OperationCode == SCSIOP_INQUIRY) ) { - // Ata command, or device enumeration commands. - status = SrbConvertToATACommand(ChannelExtension, Srb, cdb, cdbLength); + // + // Fail SRB with busy status to storport if port start is in progress or init/restore + // preserved settings command outstanding. Storport will retry these IOs later. + // + if (RetryCommand(ChannelExtension, cdb)) { + Srb->SrbStatus = SRB_STATUS_BUSY; + status = STOR_STATUS_BUSY; + } else { + // Ata command, or device enumeration commands. + status = SrbConvertToATACommand(ChannelExtension, Srb, cdb, cdbLength); + } } else { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; status = STOR_STATUS_INVALID_DEVICE_REQUEST; @@ -306,7 +363,7 @@ Return value: if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { if (srbExtension->DataBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); srbExtension->DataBuffer = NULL; } return; @@ -317,7 +374,7 @@ Return value: (srbExtension->DataTransferLength < sizeof(MODE_PARAMETER_HEADER)) ) { // free the memory and mark the Srb with error status. if (srbExtension->DataBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); srbExtension->DataBuffer = NULL; } @@ -370,7 +427,7 @@ Return value: } if (srbExtension->DataBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); srbExtension->DataBuffer = NULL; } @@ -388,9 +445,8 @@ AtapiModeSenseRequest ( { ULONG status; ULONG modeSenseBufferSize; - ULONG tempLength; PMODE_PARAMETER_HEADER10 modeSenseBuffer; - STOR_PHYSICAL_ADDRESS modeSensePhysialAddress; + STOR_PHYSICAL_ADDRESS modeSensePhysicalAddress = {0}; PUCHAR origBuffer; UCHAR bytesAdjust; @@ -432,7 +488,7 @@ AtapiModeSenseRequest ( } // We need to allocate a new data buffer since the size is different - status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, (PVOID*)&modeSenseBuffer); + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, (PVOID*)&modeSenseBuffer, &modeSensePhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (modeSenseBuffer == NULL) ) { // memory allocation failed @@ -442,7 +498,6 @@ AtapiModeSenseRequest ( } AhciZeroMemory((PCHAR)modeSenseBuffer, modeSenseBufferSize); - modeSensePhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)modeSenseBuffer, &tempLength); // fill information in the new MODE_SENSE10 header allocationLength = Cdb->MODE_SENSE.AllocationLength; @@ -472,13 +527,14 @@ AtapiModeSenseRequest ( srbExtension->Flags |= ATA_FLAGS_NEW_CDB; srbExtension->Flags |= ATA_FLAGS_DATA_IN; srbExtension->DataBuffer = modeSenseBuffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = modeSensePhysicalAddress.QuadPart; srbExtension->DataTransferLength = modeSenseBufferSize; srbExtension->CompletionRoutine = AtapiModeCommandRequestCompletion; srbExtension->CompletionContext = (PVOID)modeSenseBufferSize; // preserve the buffer size, it's needed for freeing the memory srbExtension->LocalSgl.NumberOfElements = 1; - srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSensePhysialAddress.LowPart; - srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSensePhysialAddress.HighPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSensePhysicalAddress.LowPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSensePhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = modeSenseBufferSize; srbExtension->Sgl = &srbExtension->LocalSgl; @@ -486,7 +542,7 @@ Done: if (status != STOR_STATUS_SUCCESS) { if (modeSenseBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, modeSenseBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, modeSenseBuffer, modeSensePhysicalAddress); } srbExtension->DataBuffer = NULL; } @@ -508,8 +564,7 @@ AtapiModeSelectRequest ( ULONG status; PVOID modeSelectBuffer; ULONG modeSelectBufferSize; - ULONG tempLength; - STOR_PHYSICAL_ADDRESS modeSelectPhysialAddress; + STOR_PHYSICAL_ADDRESS modeSelectPhysicalAddress = {0}; UCHAR bytesToSkip; PUCHAR origBuffer; UCHAR bytesAdjust; @@ -556,7 +611,7 @@ AtapiModeSelectRequest ( modeSelectBufferSize = srbDataBufferLength + bytesAdjust - header->BlockDescriptorLength; // allocate buffer for the new cdb - status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, (PVOID*)&modeSelectBuffer); + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, (PVOID*)&modeSelectBuffer, &modeSelectPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (modeSelectBuffer == NULL) ) { @@ -567,7 +622,6 @@ AtapiModeSelectRequest ( } AhciZeroMemory((PCHAR)modeSelectBuffer, modeSelectBufferSize); - modeSelectPhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)modeSelectBuffer, &tempLength); header10 = (PMODE_PARAMETER_HEADER10)modeSelectBuffer; @@ -610,6 +664,7 @@ AtapiModeSelectRequest ( srbExtension->Flags |= ATA_FLAGS_NEW_CDB; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = modeSelectBuffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = modeSelectPhysicalAddress.QuadPart; srbExtension->DataTransferLength = modeSelectBufferSize; srbExtension->CompletionRoutine = AtapiModeCommandRequestCompletion; srbExtension->CompletionContext = (PVOID)modeSelectBufferSize; // preserve the buffer size, it's needed for freeing the memory @@ -626,8 +681,8 @@ AtapiModeSelectRequest ( cdb->MODE_SELECT10.Control = Cdb->MODE_SELECT.Control; srbExtension->LocalSgl.NumberOfElements = 1; - srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSelectPhysialAddress.LowPart; - srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSelectPhysialAddress.HighPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSelectPhysicalAddress.LowPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSelectPhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = modeSelectBufferSize; // preserve the buffer size, it's needed for freeing the memory srbExtension->Sgl = &srbExtension->LocalSgl; @@ -635,7 +690,7 @@ Done: if (status != STOR_STATUS_SUCCESS) { if (modeSelectBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, modeSelectBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, modeSelectBuffer, modeSelectPhysicalAddress); } srbExtension->DataBuffer = NULL; } @@ -660,7 +715,7 @@ SrbConvertToATACommand( Not setting this field can cause the Srb being completed earlier than expected. */ { - ULONG status; + ULONG status = STOR_STATUS_INVALID_DEVICE_REQUEST; // translate the cdb to task file register values switch (Cdb->CDB10.OperationCode) { @@ -693,11 +748,29 @@ SrbConvertToATACommand( break; case SCSIOP_READ_CAPACITY: - case SCSIOP_READ_CAPACITY16: status = AtaReadCapacityRequest(ChannelExtension, Srb, Cdb, CdbLength); break; + case SCSIOP_READ_CAPACITY16: + + // + // Get physical element status & Remove element and truncate command share the same operation code 0x9e + // with read capacity 16 command. + // + + if (Cdb->READ_CAPACITY16.ServiceAction == SERVICE_ACTION_READ_CAPACITY16) { + status = AtaReadCapacityRequest(ChannelExtension, Srb, Cdb, CdbLength); + } else if (Cdb->GET_PHYSICAL_ELEMENT_STATUS.ServiceAction == SERVICE_ACTION_GET_PHYSICAL_ELEMENT_STATUS) { + status = AtaGetPhysicalElementStatusRequest(ChannelExtension, Srb, Cdb); + } else if (Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ServiceAction == SERVICE_ACTION_REMOVE_ELEMENT_AND_TRUNCATE) { + status = AtaRemoveElementAndTruncateRequest(ChannelExtension, Srb, Cdb); + } else { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + } + + break; + case SCSIOP_INQUIRY: status = AtaInquiryRequest(ChannelExtension, Srb, Cdb); @@ -746,14 +819,62 @@ SrbConvertToATACommand( status = AtaWriteBufferRequest(ChannelExtension, Srb, Cdb); break; + case SCSIOP_READ_DATA_BUFF16: + + if (Cdb->READ_BUFFER_16.Mode == READ_BUFFER_MODE_ERROR_HISTORY) { + if (Cdb->READ_BUFFER_16.BufferId == BUFFER_ID_RETURN_ERROR_HISTORY_DIRECTORY) { + status = AtaGetDeviceCurrentInternalStatusDataHeader(ChannelExtension, Srb, Cdb); + } else if((Cdb->READ_BUFFER_16.BufferId >= BUFFER_ID_RETURN_ERROR_HISTORY_MINIMUM_THRESHOLD) && + (Cdb->READ_BUFFER_16.BufferId <= BUFFER_ID_RETURN_ERROR_HISTORY_MAXIMUM_THRESHOLD)) { + status = AtaGetDeviceCurrentInternalStatusData(ChannelExtension, Srb, Cdb); + } else { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + } + } else { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + } + + break; default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; - status = STOR_STATUS_INVALID_DEVICE_REQUEST; break; } + if (status != STOR_STATUS_SUCCESS) { + + } else { + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + AHCI_H2D_REGISTER_FIS cfis = srbExtension->Cfis; // Size is 20 bytes (shares first 16 with TaskFile) + // Convert the Cfis to an array of ULONGs to log the struct in pieces + PULONG cfisAsUlong = (PULONG)&cfis; // Length is 5 ULONGs + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventBuildIO, + L"SrbConvertToATACommand finished", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"AtaFunction", + srbExtension->AtaFunction, + L"Cfis 0-7", + ((ULONGLONG)cfisAsUlong[0] << 32) + cfisAsUlong[1], + L"Cfis 8-15", + ((ULONGLONG)cfisAsUlong[2] << 32) + cfisAsUlong[3], + L"Cfis 16-20", + ((ULONGLONG)cfisAsUlong[4] << 32), + L"CDB 0-7", + ((ULONGLONG)Cdb->AsUlong[0] << 32) + Cdb->AsUlong[1], + L"CDB 8-15", + ((ULONGLONG)Cdb->AsUlong[2] << 32) + Cdb->AsUlong[3], + NULL, + 0, + NULL, + 0); + } + return status; } @@ -852,7 +973,7 @@ HybridWriteThroughEvictCompletion( // // Free DMA buffer that allocated for EVICT command. // - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); return; } @@ -880,7 +1001,6 @@ Return Value: --*/ { ULONG status; - ULONG i; PATA_LBA_RANGE buffer = NULL; // DMA buffer allocated for EVICT command STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; PAHCI_SRB_EXTENSION srbExtension; @@ -897,7 +1017,7 @@ Return Value: // // allocate DMA buffer, this buffer will be used to store the ATA LBA Range for EVICT command // - status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, (PVOID*)&buffer); + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, (PVOID*)&buffer, &bufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_ERROR; @@ -925,9 +1045,9 @@ Return Value: srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = bufferPhysicalAddress.QuadPart; srbExtension->DataTransferLength = ATA_BLOCK_SIZE; - bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &i); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; @@ -1164,7 +1284,6 @@ Notes: } } - return; } @@ -1741,9 +1860,9 @@ AtaReadCapacityCompletion ( if (srbDataBuffer != NULL) { if (cdbLength == 0x10) { - // 16 byte CDB + // 16 byte CDB, set return data length default to sizeof(READ_CAPACITY_DATA_EX). PREAD_CAPACITY16_DATA readCap16 = (PREAD_CAPACITY16_DATA)srbDataBuffer; - ULONG returnDataLength = 12; //default to sizeof(READ_CAPACITY_DATA_EX) + ULONG returnDataLength = sizeof(READ_CAPACITY_DATA_EX); if (srbDataBufferLength < sizeof(READ_CAPACITY_DATA_EX)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -1780,7 +1899,11 @@ AtaReadCapacityCompletion ( readCap16->LBPRZ = 0; } - returnDataLength = FIELD_OFFSET(READ_CAPACITY16_DATA, Reserved3); + if (srbDataBufferLength >= sizeof(READ_CAPACITY16_DATA)) { + returnDataLength = sizeof(READ_CAPACITY16_DATA); + } else { + returnDataLength = (ULONG)FIELD_OFFSET(READ_CAPACITY16_DATA, Reserved3); + } } SrbSetDataTransferLength(Srb, returnDataLength); @@ -1939,7 +2062,7 @@ AtaGenerateInquiryData ( AhciZeroMemory((PCHAR)InquiryData, ATA_INQUIRYDATA_SIZE); - InquiryData->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; + InquiryData->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; InquiryData->RemovableMedia = IsRemovableMedia(&ChannelExtension->DeviceExtension->DeviceParameters) ? 1 : 0; InquiryData->ResponseDataFormat = 0x2; //data format is defined in SPC standard InquiryData->CommandQueue = 1; //support NCQ for AHCI controller @@ -2143,7 +2266,11 @@ AtaInquiryRequest( _In_ PCDB Cdb ) /* - NOTE: the command should be completed after calling this function as no real command will be sent to device. + NOTE: + In most cases the command should be completed after calling this function as + no real command will be sent to device. The exceptions being if we're on the + dump stack or if a firmware activate was just performed and the identify data + needs to be refreshed. */ { ULONG status = STOR_STATUS_SUCCESS; @@ -2157,18 +2284,17 @@ AtaInquiryRequest( status = STOR_STATUS_INVALID_PARAMETER; } else if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0) { - if (IsDumpMode(ChannelExtension->AdapterExtension) && !DeviceIdentificationComplete(ChannelExtension->AdapterExtension)) { + if (IsDumpMode(ChannelExtension->AdapterExtension) && !DeviceIdentificationCompletedSuccess(ChannelExtension->AdapterExtension)) { // the enumeration command from dump stack. IssueIdentifyCommand(ChannelExtension, Srb); - } else if (ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData == 1) { - ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData = 0; + } else if (ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData == 1) { IssueIdentifyCommand(ChannelExtension, Srb); } else { status = InquiryComplete(ChannelExtension, Srb); } } else { - PVOID srbDataBuffer = SrbGetDataBuffer(Srb); - ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + PVOID srbDataBuffer = SrbGetDataBuffer(Srb); + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); if (srbDataBuffer == NULL) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -2184,7 +2310,12 @@ AtaInquiryRequest( // // Input buffer should be at least the size of page header plus count of supported pages, each page needs one byte. // - ULONG requiredBufferSize = sizeof(VPD_SUPPORTED_PAGES_PAGE) + 6; + + ULONG requiredBufferSize = 0; + UCHAR supportedPageCount = 8; + ULONG i = 0; + + requiredBufferSize = sizeof(VPD_SUPPORTED_PAGES_PAGE) + supportedPageCount; if (srbDataBufferLength < requiredBufferSize) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -2192,16 +2323,26 @@ AtaInquiryRequest( } else { PVPD_SUPPORTED_PAGES_PAGE outputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)srbDataBuffer; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + // + // Report supported page in ascending order beginning with page code 00h. + // + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_SUPPORTED_PAGES; - outputBuffer->PageLength = 0x06; // supports 6 VPD pages - outputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES; - outputBuffer->SupportedPageList[1] = VPD_SERIAL_NUMBER; - outputBuffer->SupportedPageList[2] = VPD_ATA_INFORMATION; - outputBuffer->SupportedPageList[3] = VPD_BLOCK_LIMITS; - outputBuffer->SupportedPageList[4] = VPD_BLOCK_DEVICE_CHARACTERISTICS; - outputBuffer->SupportedPageList[5] = VPD_LOGICAL_BLOCK_PROVISIONING; + outputBuffer->PageLength = supportedPageCount; + outputBuffer->SupportedPageList[i++] = VPD_SUPPORTED_PAGES; + outputBuffer->SupportedPageList[i++] = VPD_SERIAL_NUMBER; + // Support device identification page(VPD: 0x83) to resolve PCS test failure. + outputBuffer->SupportedPageList[i++] = VPD_DEVICE_IDENTIFIERS; + // According to SAT4, if the SATL supports the Read Buffer command with mode field set to 0x1C(Error History mode), + // then the SATL shall support the Extended Inquiry Data VPD page. + outputBuffer->SupportedPageList[i++] = VPD_EXTENDED_INQUIRY_DATA; + outputBuffer->SupportedPageList[i++] = VPD_ATA_INFORMATION; + outputBuffer->SupportedPageList[i++] = VPD_BLOCK_LIMITS; + outputBuffer->SupportedPageList[i++] = VPD_BLOCK_DEVICE_CHARACTERISTICS; + outputBuffer->SupportedPageList[i++] = VPD_LOGICAL_BLOCK_PROVISIONING; + + NT_ASSERT(supportedPageCount == i); SrbSetDataTransferLength(Srb, requiredBufferSize); @@ -2211,13 +2352,19 @@ AtaInquiryRequest( break; } case VPD_BLOCK_LIMITS: { - if (srbDataBufferLength < 0x14) { - Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; - status = STOR_STATUS_INVALID_PARAMETER; + if (srbDataBufferLength < sizeof(VPD_BLOCK_LIMITS_PAGE)) { + SrbSetSrbStatus(Srb, SRB_STATUS_DATA_OVERRUN); + SrbSetDataTransferLength(Srb, 0); + status = STOR_STATUS_BUFFER_TOO_SMALL; } else { PVPD_BLOCK_LIMITS_PAGE outputBuffer = (PVPD_BLOCK_LIMITS_PAGE)srbDataBuffer; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + // + // Resetting the VPD page buffer + // + AhciZeroMemory((PCHAR)outputBuffer, sizeof(VPD_BLOCK_LIMITS_PAGE)); + + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_BLOCK_LIMITS; @@ -2225,9 +2372,8 @@ AtaInquiryRequest( // leave outputBuffer->Descriptors[0 : 15] as '0' indicating 'not supported' for those fields. // - if ( (srbDataBufferLength >= 0x24) && - IsDeviceSupportsTrim(ChannelExtension) ) { - // not worry about multiply overflow as max of DsmCapBlockCount is min(AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE, 0xFFFF) + if ( IsDeviceSupportsTrim(ChannelExtension) ) { + // not worry about multiply overflow as max of DsmCapBlockCount is min(AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE, 0xFFFF) // calculate how many LBA ranges can be associated with one DSM - Trim command ULONG maxLbaRangeEntryCountPerCmd = ChannelExtension->DeviceExtension[0].DeviceParameters.DsmCapBlockCount * (ATA_BLOCK_SIZE / sizeof(ATA_LBA_RANGE)); // calculate how many LBA can be associated with one DSM - Trim command @@ -2250,8 +2396,9 @@ AtaInquiryRequest( // keep original 'Srb->DataTransferLength' value. } else { - outputBuffer->PageLength[1] = 0x10; - SrbSetDataTransferLength(Srb, 0x14); + outputBuffer->PageLength[1] = 0x3C; + + // keep original 'Srb->DataTransferLength' value. } Srb->SrbStatus = SRB_STATUS_SUCCESS; @@ -2266,7 +2413,7 @@ AtaInquiryRequest( } else { PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE outputBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)srbDataBuffer; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_BLOCK_DEVICE_CHARACTERISTICS; outputBuffer->PageLength = 0x3C; // must be 0x3C per spec @@ -2286,7 +2433,7 @@ AtaInquiryRequest( } else { PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE outputBuffer = (PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE)srbDataBuffer; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_LOGICAL_BLOCK_PROVISIONING; outputBuffer->PageLength[1] = 0x04; // 8 bytes data in total @@ -2319,7 +2466,7 @@ AtaInquiryRequest( PVPD_SERIAL_NUMBER_PAGE outputBuffer = (PVPD_SERIAL_NUMBER_PAGE)srbDataBuffer; UCHAR i; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_SERIAL_NUMBER; outputBuffer->PageLength = 20; // 24 bytes data in total @@ -2347,7 +2494,7 @@ AtaInquiryRequest( AhciZeroMemory((PCHAR)outputBuffer, sizeof(VPD_ATA_INFORMATION_PAGE)); - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_ATA_INFORMATION; outputBuffer->PageLength[0] = 0x02; @@ -2400,6 +2547,40 @@ AtaInquiryRequest( break; } + case VPD_EXTENDED_INQUIRY_DATA: { + if (srbDataBufferLength < RTL_SIZEOF_THROUGH_FIELD(VPD_EXTENDED_INQUIRY_DATA_PAGE, MaxSupportedSenseDataLength)) { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + status = STOR_STATUS_INVALID_PARAMETER; + } else { + PVPD_EXTENDED_INQUIRY_DATA_PAGE outputBuffer = (PVPD_EXTENDED_INQUIRY_DATA_PAGE)srbDataBuffer; + + AhciZeroMemory((PCHAR)outputBuffer, srbDataBufferLength); + + // Below fields are required for Read Buffer command(Error history mode) support in SAT-4. + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; + outputBuffer->DeviceTypeQualifier = 0; + outputBuffer->PageCode = VPD_EXTENDED_INQUIRY_DATA; + outputBuffer->PageLength[0] = 0; + outputBuffer->PageLength[1] = 0x3C; // must be 0x3C per spec. + outputBuffer->HssRelef = 1; + + // Populate below fields according to SAT-2. + if (ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheCapabilities.NVCachePowerModeEnabled == 1 || + ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.NonVolatileWriteCache == 1) { + outputBuffer->NvSup = 1; + } + if (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetActive.WriteCache || + ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetActive.LookAhead) { + outputBuffer->VSup = 1; + } + + Srb->SrbStatus = SRB_STATUS_SUCCESS; + status = STOR_STATUS_SUCCESS; + } + + break; + } + default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; @@ -2905,7 +3086,7 @@ Return Value: } else { if (buffer != NULL) { - AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer); + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer, srbExtension->DataBufferPhysicalAddress); } if (trimContext != NULL) { @@ -2938,6 +3119,7 @@ AtaUnmapRequest ( PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + STOR_PHYSICAL_ADDRESS bufferPhysicalAddress = {0}; unmapList = (PUNMAP_LIST_HEADER)srbDataBuffer; @@ -2957,8 +3139,6 @@ AtaUnmapRequest ( } else { // some preparation work before actually starting to process the request ULONG i = 0; - ULONG length = 0; - STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; status = StorPortAllocatePool(ChannelExtension->AdapterExtension, sizeof(ATA_TRIM_CONTEXT), AHCI_POOL_TAG, (PVOID*)&trimContext); if ( (status != STOR_STATUS_SUCCESS) || (trimContext == NULL) ) { @@ -3007,7 +3187,7 @@ AtaUnmapRequest ( } // 1.4 allocate buffer, this buffer will be used to store ATA LBA Ranges for DSM command - status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, (PVOID*)&buffer); + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, (PVOID*)&buffer, &bufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -3021,10 +3201,10 @@ AtaUnmapRequest ( srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = bufferPhysicalAddress.QuadPart; srbExtension->DataTransferLength = trimContext->AllocatedBufferLength; srbExtension->CompletionContext = (PVOID)trimContext; - bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &length); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; @@ -3039,7 +3219,7 @@ Exit: // the process failed before DSM command can be sent. Free allocated resources. if (status != STOR_STATUS_SUCCESS) { if (buffer != NULL) { - AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer); + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer, bufferPhysicalAddress); } if (trimContext != NULL) { @@ -3121,6 +3301,111 @@ AtaSecurityProtocolRequest ( } +VOID UpdateFirmwareIoctlReturnCode( + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PSRB_IO_CONTROL srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); + + if (srbControl == NULL) { + return; + } + + if (srbExtension->AtaError & IDE_ERROR_CRC_ERROR) { + + // bit 7: Interface CRC error. + srbControl->ReturnCode = FIRMWARE_STATUS_INTERFACE_CRC_ERROR; + + } else if (srbExtension->AtaError & IDE_ERROR_DATA_ERROR) { + + // bit 6: Uncorrectable Error. + srbControl->ReturnCode = FIRMWARE_STATUS_DEVICE_ERROR; + + } else if (srbExtension->AtaError & IDE_ERROR_MEDIA_CHANGE) { + + // bit 5: Media Changed (legacy). + srbControl->ReturnCode = FIRMWARE_STATUS_MEDIA_CHANGE; + + } else if (srbExtension->AtaError & IDE_ERROR_ID_NOT_FOUND) { + + // bit 4: ID Not Found. + srbControl->ReturnCode = FIRMWARE_STATUS_ID_NOT_FOUND; + + } else if (srbExtension->AtaError & IDE_ERROR_MEDIA_CHANGE_REQ) { + + // bit 3: Media Change Request (legacy). + srbControl->ReturnCode = FIRMWARE_STATUS_MEDIA_CHANGE_REQUEST; + + } else if (srbExtension->AtaError & IDE_ERROR_COMMAND_ABORTED) { + + // bit 2: Command Aborted. + srbControl->ReturnCode = FIRMWARE_STATUS_COMMAND_ABORT; + + } else if (srbExtension->AtaError & IDE_ERROR_END_OF_MEDIA) { + + // bit 1: End of Media (legacy). + srbControl->ReturnCode = FIRMWARE_STATUS_END_OF_MEDIA; + + } else if (srbExtension->AtaError & IDE_ERROR_ILLEGAL_LENGTH) { + + // bit 0: Media Error (legacy). + srbControl->ReturnCode = FIRMWARE_STATUS_ILLEGAL_LENGTH; + + } else { + + // Should not be here, need investigation if get here. + NT_ASSERT(FALSE); + srbControl->ReturnCode = FIRMWARE_STATUS_ERROR; + } + + return; +} + + +VOID +AtaFirmwareDownloadCompletion ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +{ + if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { + + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PSRB_IO_CONTROL srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); + + UpdateFirmwareIoctlReturnCode(Srb); + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareDownloadComplete, + L"Firmware Download Completion", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ReturnCode", + (srbControl != NULL) ? (srbControl->ReturnCode) : (0), + L"FeaturesReg", + srbExtension->TaskFile.Current.bFeaturesReg, + L"SectorCountReg", + srbExtension->TaskFile.Current.bSectorCountReg, + L"DriveHeadReg", + srbExtension->TaskFile.Current.bDriveHeadReg, + L"CommandReg", + srbExtension->TaskFile.Current.bCommandReg, + NULL, + 0, + NULL, + 0); + + } + + return; +} + VOID AtaWriteBufferFirmwareDownload ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, @@ -3128,11 +3413,14 @@ AtaWriteBufferFirmwareDownload ( _In_ PCDB Cdb ) { - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PSRB_IO_CONTROL srbControl = NULL; ULONG bufferOffset = 0; ULONG dataLength = 0; - UCHAR commandReg; + UCHAR commandReg = 0; + BOOLEAN invalidCommand = FALSE; + + srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); bufferOffset = (ULONG)Cdb->WRITE_BUFFER.BufferOffset[0] << 16 | (ULONG)Cdb->WRITE_BUFFER.BufferOffset[1] << 8 | @@ -3146,6 +3434,8 @@ AtaWriteBufferFirmwareDownload ( ((dataLength % ATA_BLOCK_SIZE) != 0)) { // Report invalid command. AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; + invalidCommand = TRUE; goto exit; } @@ -3157,6 +3447,8 @@ AtaWriteBufferFirmwareDownload ( (dataLength < ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks)) { // Report invalid command. AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; + invalidCommand = TRUE; goto exit; } @@ -3165,6 +3457,8 @@ AtaWriteBufferFirmwareDownload ( ((Cdb->WRITE_BUFFER.ModeSpecific & SCSI_WRITE_BUFFER_MODE_0D_MODE_SPECIFIC_HR_ACT) != 0)) { // Report invalid command. AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; + invalidCommand = TRUE; goto exit; } } @@ -3183,6 +3477,8 @@ AtaWriteBufferFirmwareDownload ( // Set up taskfile in SrbExtension. srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; + srbExtension->CompletionRoutine = AtaFirmwareDownloadCompletion; + SetCommandReg((&srbExtension->TaskFile.Current), commandReg); SetFeaturesReg((&srbExtension->TaskFile.Current), 0x0E); @@ -3201,6 +3497,37 @@ exit: // If the ATA command completes without error and the ATA device returns a COUNT field set to 03h, then the // SATL shall remember that the new microcode shall be activated by whichever event in table 48 occurs first. + if (invalidCommand) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareDownload, + L"Firmware Download", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"BufferOffset", + bufferOffset, + L"DataLength", + dataLength, + L"MaxTransBlocks", + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks, + L"MinTransBlocks", + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks, + L"Mode", + Cdb->WRITE_BUFFER.Mode, + L"ModeSpecific", + Cdb->WRITE_BUFFER.ModeSpecific, + L"CommandReg", + commandReg, + L"ReturnCode", + srbControl->ReturnCode); + + } + + + return; } @@ -3216,6 +3543,38 @@ AtaFirmwareActivateCompletion ( // if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData = 1; + } else { + + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PSRB_IO_CONTROL srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); + + UpdateFirmwareIoctlReturnCode(Srb); + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareActivateComplete, + L"Firmware Activate Completion", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ReturnCode", + (srbControl != NULL) ? (srbControl->ReturnCode) : (0), + L"FeaturesReg", + srbExtension->TaskFile.Current.bFeaturesReg, + L"SectorCountReg", + srbExtension->TaskFile.Current.bSectorCountReg, + L"DriveHeadReg", + srbExtension->TaskFile.Current.bDriveHeadReg, + L"CommandReg", + srbExtension->TaskFile.Current.bCommandReg, + NULL, + 0, + NULL, + 0); + } return; @@ -3231,6 +3590,9 @@ AtaWriteBufferFirmwareActivate ( { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); UCHAR commandReg; + PSRB_IO_CONTROL srbControl = NULL; + + srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); if ((Cdb->WRITE_BUFFER.ModeSpecific != 0) || (Cdb->WRITE_BUFFER.BufferID != 0) || @@ -3243,6 +3605,33 @@ AtaWriteBufferFirmwareActivate ( // Report invalid command. AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareActivate, + L"Firmware Activate", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"Operation Code", + Cdb->WRITE_BUFFER.ModeSpecific, + L"Buffer ID", + Cdb->WRITE_BUFFER.BufferID, + L"Buffer Offset0", + Cdb->WRITE_BUFFER.BufferOffset[0], + L"Buffer Offset1", + Cdb->WRITE_BUFFER.BufferOffset[1], + L"Buffer Offset2", + Cdb->WRITE_BUFFER.BufferOffset[2], + L"ParaList Len0", + Cdb->WRITE_BUFFER.ParameterListLength[0], + L"ParaList Len1", + Cdb->WRITE_BUFFER.ParameterListLength[1], + L"ParaList Len2", + Cdb->WRITE_BUFFER.ParameterListLength[2]); + } else { NT_ASSERT((SrbGetSrbFlags(Srb) & SRB_FLAGS_UNSPECIFIED_DIRECTION) == 0); @@ -3292,6 +3681,716 @@ AtaWriteBufferRequest ( return STOR_STATUS_SUCCESS; } +VOID +AtaGetPhysicalElementStatusCompletion ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +/*++ + This is the completion point of Get Physical Element Status command for ATA devices. + + This routine translates an ATA output of Get Physical Element Status command into + SCSI format if the command is successful. + +Arguments: + + ChannelExtension + Srb + +Return Value: + None + +--*/ +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + ULONG transferLength = RequestGetDataTransferLength(Srb); + ULONG tempScsiBufferLength = 0; + ULONG tempAtaBufferLength = 0; + + PATA_GET_PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA ataDataBuffer = (PATA_GET_PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA)srbExtension->DataBuffer; + PPHYSICAL_ELEMENT_STATUS_PARAMETER_DATA scsiDataBuffer = (PPHYSICAL_ELEMENT_STATUS_PARAMETER_DATA)SrbGetDataBuffer(Srb); + + NT_ASSERT((srbExtension->DataBuffer != NULL) && (transferLength >= sizeof(ATA_GET_PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA))); + + // + // Translate ATA get physical element status data to SCSI format. + // + tempScsiBufferLength = sizeof(PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA); + tempAtaBufferLength = sizeof(ATA_GET_PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA); + + REVERSE_BYTES(scsiDataBuffer->DescriptorCount, &ataDataBuffer->NumberOfDescriptors); + REVERSE_BYTES(scsiDataBuffer->ReturnedDescriptorCount, &ataDataBuffer->NumberOfDescriptorsReturned); + REVERSE_BYTES(scsiDataBuffer->ElementIdentifierBeingDepoped, &ataDataBuffer->ElementIdentifierBeingDepoped); + + for (ULONG i = 0; i < ataDataBuffer->NumberOfDescriptorsReturned; ++i) { + + if ((tempScsiBufferLength > srbDataBufferLength) || (tempAtaBufferLength > transferLength)) { + goto Exit; + } + + scsiDataBuffer->Descriptors[i].PhysicalElementType = ataDataBuffer->Descriptors[i].PhysicalElementType; + scsiDataBuffer->Descriptors[i].PhysicalElementHealth = ataDataBuffer->Descriptors[i].PhysicalElementHealth; + REVERSE_BYTES(scsiDataBuffer->Descriptors[i].ElementIdentifier, &ataDataBuffer->Descriptors[i].ElementIdentifier); + REVERSE_BYTES_QUAD(scsiDataBuffer->Descriptors[i].AssociatedCapacity, &ataDataBuffer->Descriptors[i].AssociatedCapacity); + + tempScsiBufferLength += sizeof(PHYSICAL_ELEMENT_STATUS_DATA_DESCRIPTOR); + tempAtaBufferLength += sizeof(ATA_PHYSICAL_ELEMENT_STATUS_DESCRIPTOR); + } + } + +Exit: + + if (srbExtension->DataBuffer != NULL) { + + // srbExtension->CompletionContext contains the allocated buffer size. + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); + srbExtension->DataBuffer = NULL; + } + + if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitGetPhysicalElementStatusComplete, + L"Get Physical Element status", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ErrorReg", + ChannelExtension->ReceivedFIS->D2hRegisterFis.Error, + L"StatusReg", + ChannelExtension->ReceivedFIS->D2hRegisterFis.Status, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0 + ); + } + + return; +} + +ULONG +AtaGetPhysicalElementStatusRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +/*++ + This routine translates a SCSI - Get Physical Element Status command into + ATA - Get Physical Element Status command. + +Arguments: + + ChannelExtension + Srb + Cdb + +Return Value: + ULONG - status + +--*/ +{ + ULONG status = STOR_STATUS_SUCCESS; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PVOID srbDataBuffer = SrbGetDataBuffer(Srb); + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + PVOID physicalElementStatusBuffer = NULL; + ULONG physicalElementStatusBufferSize = 0; + STOR_PHYSICAL_ADDRESS physicalElementStatusPhysicalAddress = {0}; + PAHCI_H2D_REGISTER_FIS cfis = &srbExtension->Cfis; + ULONG tempUlong = 0; + + if ((srbDataBuffer == NULL) || + (srbDataBufferLength < sizeof(PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA))) { + + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + status = STOR_STATUS_INVALID_PARAMETER; + goto Done; + } + + srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; + srbExtension->Flags |= (ATA_FLAGS_48BIT_COMMAND | ATA_FLAGS_DATA_IN | ATA_FLAGS_USE_DMA); + srbExtension->CompletionRoutine = AtaGetPhysicalElementStatusCompletion; + + // + // Set up ATA command. + // + cfis->Command = IDE_COMMAND_GET_PHYSICAL_ELEMENT_STATUS; + + cfis->Feature7_0 = 0; + cfis->Feature15_8 = (Cdb->GET_PHYSICAL_ELEMENT_STATUS.Filter << 6) | (Cdb->GET_PHYSICAL_ELEMENT_STATUS.ReportType); + + REVERSE_BYTES(&tempUlong, Cdb->GET_PHYSICAL_ELEMENT_STATUS.AllocationLength); + + tempUlong = (tempUlong + (ATA_BLOCK_SIZE - 1)) / ATA_BLOCK_SIZE; + physicalElementStatusBufferSize = tempUlong * ATA_BLOCK_SIZE; + + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, physicalElementStatusBufferSize, (PVOID*)&physicalElementStatusBuffer, &physicalElementStatusPhysicalAddress); + + if ((status != STOR_STATUS_SUCCESS) || (physicalElementStatusBuffer == NULL) ) { + + Srb->SrbStatus = SRB_STATUS_ERROR; + status = STOR_STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + AhciZeroMemory((PCHAR)physicalElementStatusBuffer, physicalElementStatusBufferSize); + + srbExtension->DataBuffer = physicalElementStatusBuffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = physicalElementStatusPhysicalAddress.QuadPart; + srbExtension->DataTransferLength = physicalElementStatusBufferSize; + srbExtension->CompletionContext = (PVOID)physicalElementStatusBufferSize; // preserve the buffer size, it's needed for freeing the memory + + srbExtension->LocalSgl.NumberOfElements = 1; + srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = physicalElementStatusPhysicalAddress.LowPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = physicalElementStatusPhysicalAddress.HighPart; + srbExtension->LocalSgl.List[0].Length = physicalElementStatusBufferSize; // preserve the buffer size, it's needed for freeing the memory + srbExtension->Sgl = &srbExtension->LocalSgl; + + cfis->Count7_0 = (UCHAR)tempUlong; + cfis->Count15_8 = (UCHAR)(tempUlong >> 8); + + REVERSE_BYTES(&tempUlong, Cdb->GET_PHYSICAL_ELEMENT_STATUS.StartingElement); + cfis->LBA7_0 = (UCHAR)((tempUlong & 0x000000ff) >> 0); + cfis->LBA15_8 = (UCHAR)((tempUlong & 0x0000ff00) >> 8); + cfis->LBA23_16 = (UCHAR)((tempUlong & 0x00ff0000) >> 16); + cfis->LBA31_24 = (UCHAR)((tempUlong & 0xff000000) >> 24); + +Done: + + if (status != STOR_STATUS_SUCCESS) { + if (physicalElementStatusBuffer != NULL) { + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, physicalElementStatusBufferSize, physicalElementStatusBuffer, physicalElementStatusPhysicalAddress); + } + srbExtension->DataBuffer = NULL; + } + + return status; +} + +VOID +AtaRemoveElementAndTruncateCompletion ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +/*++ + This is the completion point of Remove Element And Truncate command for ATA devices. + +Arguments: + + ChannelExtension + Srb + +Return Value: + None + +--*/ +{ + if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitRemoveElementAndTruncateComplete, + L"Remove Element And Truncate", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ErrorReg", + ChannelExtension->ReceivedFIS->D2hRegisterFis.Error, + L"StatusReg", + ChannelExtension->ReceivedFIS->D2hRegisterFis.Status, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0 + ); + } + + return; +} + +ULONG +AtaRemoveElementAndTruncateRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +/*++ + This routine translates a SCSI - Remove Element and Truncate command into + ATA - Remove Element and Truncate command. + +Arguments: + + ChannelExtension + Srb + Cdb + +Return Value: + ULONG - status + +--*/ +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PAHCI_H2D_REGISTER_FIS cfis = &srbExtension->Cfis; + ULONGLONG tempUlonglong = 0; + + UNREFERENCED_PARAMETER(ChannelExtension); + + // + // According to SAT-5 Repurposing Depopulation Translation: + // - If requested capacity is 0, SATL shall set the ATA REQUESTED MAX field to 0; + // - If requested capacity is 1, remove element and truncate command shall be terminated with a checked condition status + // with the sense key set to illegal request and additional sense code set to INVALID FIELD IN CDB; + // - If requested capacity is greater than 1, SATL shall set ATA REQUESTED MAX field in the ATA REMOVE ELEMENT AND + // TRUNCATE command to the value of the REQUESTED CAPACITY field minus 1. + // + REVERSE_BYTES_QUAD(&tempUlonglong, Cdb->REMOVE_ELEMENT_AND_TRUNCATE.RequestedCapacity); + if (tempUlonglong == 1) { + + AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + return STOR_STATUS_INVALID_DEVICE_REQUEST; + } + + tempUlonglong = (tempUlonglong == 0) ? (0) : (tempUlonglong - 1); + cfis->LBA7_0 = (UCHAR)((tempUlonglong & 0x00000000000000ff) >> 0); + cfis->LBA15_8 = (UCHAR)((tempUlonglong & 0x000000000000ff00) >> 8); + cfis->LBA23_16 = (UCHAR)((tempUlonglong & 0x0000000000ff0000) >> 16); + cfis->LBA31_24 = (UCHAR)((tempUlonglong & 0x00000000ff000000) >> 24); + cfis->LBA39_32 = (UCHAR)((tempUlonglong & 0x000000ff00000000) >> 32); + cfis->LBA47_40 = (UCHAR)((tempUlonglong & 0x0000ff0000000000) >> 40); + + srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; + srbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND; + srbExtension->CompletionRoutine = AtaRemoveElementAndTruncateCompletion; + + // + // Set up ATA command. + // + cfis->Command = IDE_COMMAND_REMOVE_ELEMENT_AND_TRUNCATE; + + cfis->Feature7_0 = Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ElementIdentifier[1]; + cfis->Feature15_8 = Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ElementIdentifier[0]; + cfis->Count7_0 = Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ElementIdentifier[3]; + cfis->Count15_8 = Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ElementIdentifier[2]; + + return STOR_STATUS_SUCCESS; +} + +VOID +AtaGetDeviceCurrentInternalStatusDataHeaderCompletion( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +/*++ + This is the completion point of Read Log Ext command that is used to read device + internal data header for ATA devices. + +Arguments: + + ChannelExtension - Channel Extension. + + Srb - Supplies a pointer to the srb request. + +Return Value: + + None. + +--*/ +{ + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + + PCURRENT_DEVICE_INTERNAL_STATUS_LOG ataCurrentDeviceInternalStatusDataHeader = NULL; + PERROR_HISTORY_DIRECTORY scsiErrorHistoryDirectory = (PERROR_HISTORY_DIRECTORY)SrbGetDataBuffer(Srb); + + ULONG transferLength = RequestGetDataTransferLength(Srb); + UCHAR tmpT10VendorId[] = "ATA "; + + NT_ASSERT((scsiErrorHistoryDirectory != NULL) && + (transferLength >= sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) && + (SrbGetDataTransferLength(Srb) >= sizeof(ERROR_HISTORY_DIRECTORY))); + + AhciZeroMemory((PVOID)scsiErrorHistoryDirectory, sizeof(ERROR_HISTORY_DIRECTORY)); + + if (transferLength < sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) { + NT_ASSERT(FALSE); + return; + } + + // + // Parse current device internal status data header(page 0) content. + // + ataCurrentDeviceInternalStatusDataHeader = (PCURRENT_DEVICE_INTERNAL_STATUS_LOG)ChannelExtension->DeviceExtension->ReadLogExtPageData; + NT_ASSERT(ataCurrentDeviceInternalStatusDataHeader->LogAddress == CURRENT_DEVICE_INTERNAL_STATUS_DATA_LOG_ADDRESS); + + NT_ASSERT(sizeof(tmpT10VendorId) >= SCSI_VENDOR_ID_LENGTH); + StorPortCopyMemory(scsiErrorHistoryDirectory->T10VendorId, tmpT10VendorId, SCSI_VENDOR_ID_LENGTH); + + if (ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage > 0) { + + USHORT errorhistoryDirectoryEntryLength = (USHORT)sizeof(ERROR_HISTORY_DIRECTORY_ENTRY); + ULONG maxAvailableLength = (ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage + 1UL) * IDE_GP_LOG_SECTOR_SIZE; + REVERSE_BYTES_SHORT(scsiErrorHistoryDirectory->DirectoryLength, &errorhistoryDirectoryEntryLength); + scsiErrorHistoryDirectory->ErrorHistoryDirectoryList[0].SupportedBufferId = BUFFER_ID_RETURN_ERROR_HISTORY_MINIMUM_THRESHOLD; + scsiErrorHistoryDirectory->ErrorHistoryDirectoryList[0].BufferFormat = BUFFER_FORMAT_CURRENT_INTERNAL_STATUS_DATA; + scsiErrorHistoryDirectory->ErrorHistoryDirectoryList[0].BufferSource = BUFFER_SOURCE_CREATED_DUE_TO_CURRENT_COMMAND; + REVERSE_BYTES(scsiErrorHistoryDirectory->ErrorHistoryDirectoryList[0].MaxAvailableLength, &maxAvailableLength); + } + + SrbSetDataTransferLength(Srb, sizeof(ERROR_HISTORY_DIRECTORY)); + + } else { + + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitGetInternalStatusDataHeaderComplete, + L"Internal Status Data Header", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ErrorReg", + srbExtension->TaskFile.Current.bFeaturesReg, + L"StatusReg", + srbExtension->TaskFile.Current.bCommandReg, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0 + ); + } + + return; +} + +ULONG +AtaGetDeviceCurrentInternalStatusDataHeader( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +/*++ + This routine translates a SCSI - Read Buffer command(Mode: 0x1c Error History Directory) + into ATA - Read Log Ext command to read device current internal status data header. + +Arguments: + + ChannelExtension - Channel Extension. + + Srb - Supplies a pointer to the srb request. + + Cdb - SCSI Command Descriptor Block carried by Srb. + +Return Value: + + ULONG - status. + +--*/ +{ + PVOID srbDataBuffer = SrbGetDataBuffer(Srb); + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + + UNREFERENCED_PARAMETER(Cdb); + + if ((srbDataBuffer == NULL) || (srbDataBufferLength < sizeof(ERROR_HISTORY_DIRECTORY))) { + + Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; + return STOR_STATUS_INVALID_PARAMETER; + } + + IssueReadLogExtCommand(ChannelExtension, + Srb, + IDE_GP_LOG_CURRENT_DEVICE_INTERNAL_STATUS, + 0, // Starting page number + 1, // Number of sectors + 1, // Features field set to 1 + &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress, + (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData, + AtaGetDeviceCurrentInternalStatusDataHeaderCompletion + ); + + return STOR_STATUS_SUCCESS; +} + +VOID +AtaGetDeviceCurrentInternalStatusDataCompletion( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +/*++ + This is the completion point of Read Log Ext command that is used to read device + internal data for ATA devices. + +Arguments: + + ChannelExtension - Channel Extension. + + Srb - Supplies a pointer to the srb request. + +Return Value: + + None. + +--*/ +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + ULONG_PTR currentLogPageOffset = (ULONG_PTR)srbExtension->CompletionContext; + BOOLEAN requestFailed = FALSE; + + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + + PCURRENT_DEVICE_INTERNAL_STATUS_LOG ataCurrentDeviceInternalStatusDataHeader = NULL; + PCURRENT_INTERNAL_STATUS_PARAMETER_DATA scsiCurrentInternalStatusData = (PCURRENT_INTERNAL_STATUS_PARAMETER_DATA)SrbGetDataBuffer(Srb); + + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + ULONG transferLength = RequestGetDataTransferLength(Srb); + + NT_ASSERT((srbExtension->DataBuffer != NULL) && + (srbExtension->DataTransferLength >= sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) && + (scsiCurrentInternalStatusData != NULL) && + (srbDataBufferLength >= transferLength)); + + NT_ASSERT((transferLength % IDE_GP_LOG_SECTOR_SIZE) == 0); + + if (!IsDumpMode(ChannelExtension->AdapterExtension)) { + AhciZeroMemory((PVOID)scsiCurrentInternalStatusData, srbDataBufferLength); + } + + // + // Parse current device internal status data header(page 0) and/or copy other pages back to SRB databuffer. + // + ataCurrentDeviceInternalStatusDataHeader = (PCURRENT_DEVICE_INTERNAL_STATUS_LOG)srbExtension->DataBuffer; + + if (currentLogPageOffset == 0) { + + if (transferLength >= sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) { + + NT_ASSERT(ataCurrentDeviceInternalStatusDataHeader->LogAddress == CURRENT_DEVICE_INTERNAL_STATUS_DATA_LOG_ADDRESS); + NT_ASSERT((ataCurrentDeviceInternalStatusDataHeader->Area2LastLogPage >= ataCurrentDeviceInternalStatusDataHeader->Area1LastLogPage) && + (ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage >= ataCurrentDeviceInternalStatusDataHeader->Area2LastLogPage)); + + if (!IsDumpMode(ChannelExtension->AdapterExtension)) { + + REVERSE_BYTES(scsiCurrentInternalStatusData->IEEECompanyId, &ataCurrentDeviceInternalStatusDataHeader->OrganizationID); + REVERSE_BYTES_SHORT(scsiCurrentInternalStatusData->CurrentInternalStatusDataSetOneLength, &ataCurrentDeviceInternalStatusDataHeader->Area1LastLogPage); + REVERSE_BYTES_SHORT(scsiCurrentInternalStatusData->CurrentInternalStatusDataSetTwoLength, &ataCurrentDeviceInternalStatusDataHeader->Area2LastLogPage); + REVERSE_BYTES_SHORT(scsiCurrentInternalStatusData->CurrentInternalStatusDataSetThreeLength, &ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage); + scsiCurrentInternalStatusData->NewSavedDataAvailable = ataCurrentDeviceInternalStatusDataHeader->SavedDataAvailable; + scsiCurrentInternalStatusData->SavedDataGenerationNumber = ataCurrentDeviceInternalStatusDataHeader->SavedDataGenerationNumber; + StorPortCopyMemory(scsiCurrentInternalStatusData->CurrentReasonIdentifier, + ataCurrentDeviceInternalStatusDataHeader->ReasonIdentifier, + sizeof(scsiCurrentInternalStatusData->CurrentReasonIdentifier) + ); + } else { + + // + // Reorder in place if this is dump mode. + // + REVERSE_LONG(&ataCurrentDeviceInternalStatusDataHeader->OrganizationID); + REVERSE_SHORT(&ataCurrentDeviceInternalStatusDataHeader->Area1LastLogPage); + REVERSE_SHORT(&ataCurrentDeviceInternalStatusDataHeader->Area2LastLogPage); + REVERSE_SHORT(&ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage); + } + } else { + + NT_ASSERT(FALSE); + requestFailed = TRUE; + goto Exit; + } + + // + // Assume that size of this log hadn't changed since we read the directory. + // + NT_ASSERT(transferLength <= + (sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG) + (ULONG)(ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage) * IDE_GP_LOG_SECTOR_SIZE)); + + if ((transferLength > sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) && + (srbDataBufferLength >= transferLength) && + (!IsDumpMode(ChannelExtension->AdapterExtension))) { + StorPortCopyMemory(scsiCurrentInternalStatusData->CurrentInternalStatusData, + (PUCHAR)(ataCurrentDeviceInternalStatusDataHeader + 1), + (transferLength - IDE_GP_LOG_SECTOR_SIZE)); + } + } else { + if ((transferLength > sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) && + (srbDataBufferLength >= transferLength) && + (!IsDumpMode(ChannelExtension->AdapterExtension))) { + StorPortCopyMemory((PUCHAR)scsiCurrentInternalStatusData, + (PUCHAR)ataCurrentDeviceInternalStatusDataHeader, + transferLength); + } + } + + SrbSetDataTransferLength(Srb, transferLength); + } + +Exit: + + if (requestFailed) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitGetInternalStatusDataComplete, + L"Internal Status Data", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ErrorReg", + srbExtension->TaskFile.Current.bFeaturesReg, + L"StatusReg", + srbExtension->TaskFile.Current.bCommandReg, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0 + ); + } + + if ((srbExtension->DataBuffer != NULL) && !IsDumpMode(ChannelExtension->AdapterExtension)) { + + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->DataTransferLength, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); + srbExtension->DataBuffer = NULL; + } + + return; +} + +ULONG +AtaGetDeviceCurrentInternalStatusData( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +/*++ + This routine translates a SCSI - Read Buffer command(Mode: 0x1c Error History) + into ATA - Read Log Ext command to read device current internal status data. + +Arguments: + + ChannelExtension - Channel Extension. + + Srb - Supplies a pointer to the srb request. + + Cdb - SCSI Command Descriptor Block carried by Srb. + +Return Value: + + ULONG - status. + +--*/ +{ + ULONG status = STOR_STATUS_SUCCESS; + PVOID srbDataBuffer = SrbGetDataBuffer(Srb); + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + PVOID internalStatusBuffer = NULL; + STOR_PHYSICAL_ADDRESS internalStatusBufferPhysicalAddress = {0}; + ULONG bufferSize = 0; + ULONG bufferSizeDumpMode = 0; + LONGLONG bufferOffset = 0; + ULONG tempUlong = 0; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + + if ((srbDataBuffer == NULL) || (srbDataBufferLength < (ULONG)FIELD_OFFSET(CURRENT_INTERNAL_STATUS_PARAMETER_DATA, CurrentInternalStatusData))) { + + Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; + status = STOR_STATUS_INVALID_PARAMETER; + goto Done; + } + + REVERSE_BYTES(&tempUlong, Cdb->READ_BUFFER_16.AllocationLength); + bufferSize = tempUlong * IDE_GP_LOG_SECTOR_SIZE; + REVERSE_BYTES_QUAD(&bufferOffset, Cdb->READ_BUFFER_16.BufferOffset); + + if (!IsDumpMode(ChannelExtension->AdapterExtension)) { + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, bufferSize, &internalStatusBuffer, &internalStatusBufferPhysicalAddress); + if ((status != STOR_STATUS_SUCCESS) || (internalStatusBuffer == NULL) ) { + + Srb->SrbStatus = SRB_STATUS_ERROR; + status = STOR_STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + } else { + internalStatusBufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, srbDataBuffer, &bufferSizeDumpMode); + if ((internalStatusBufferPhysicalAddress.QuadPart == 0) || (bufferSizeDumpMode == 0)) { + + Srb->SrbStatus = SRB_STATUS_ERROR; + status = STOR_STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + internalStatusBuffer = srbDataBuffer; + bufferSize = min(bufferSize, bufferSizeDumpMode); + bufferSize = ALIGN_DOWN_BY(bufferSize, IDE_GP_LOG_SECTOR_SIZE); + } + + AhciZeroMemory((PCHAR)internalStatusBuffer, bufferSize); + AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); + + srbExtension->DataTransferLength = bufferSize; + srbExtension->CompletionContext = (PVOID)((ULONG)(bufferOffset / IDE_GP_LOG_SECTOR_SIZE)); + + IssueReadLogExtCommand(ChannelExtension, + Srb, + IDE_GP_LOG_CURRENT_DEVICE_INTERNAL_STATUS, + (USHORT)(bufferOffset / IDE_GP_LOG_SECTOR_SIZE), // Starting page number + (USHORT)tempUlong, // Number of sectors + 1, // Features field set to 1 + &internalStatusBufferPhysicalAddress, + (PVOID)internalStatusBuffer, + AtaGetDeviceCurrentInternalStatusDataCompletion + ); + + return status; + +Done: + + if ((internalStatusBuffer != NULL) && !IsDumpMode(ChannelExtension->AdapterExtension)) { + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, bufferSize, internalStatusBuffer, internalStatusBufferPhysicalAddress); + internalStatusBuffer = NULL; + } + + return status; +} + UCHAR AtaMapError( @@ -3884,6 +4983,7 @@ Return Value: deviceParameters->MaximumLun = 0; deviceParameters->ScsiDeviceType = DIRECT_ACCESS_DEVICE; + deviceParameters->StateFlags.RemovableMedia = identifyDeviceData->GeneralConfiguration.RemovableMedia; deviceParameters->MaxDeviceQueueDepth = min(ChannelExtension->MaxPortQueueDepth, (UCHAR)identifyDeviceData->QueueDepth); @@ -3937,15 +5037,15 @@ Return Value: // Fill some firmware support information. // if (identifyDeviceData->MinBlocksPerDownloadMicrocodeMode03 > 0) { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(identifyDeviceData->MinBlocksPerDownloadMicrocodeMode03, AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE); + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(identifyDeviceData->MinBlocksPerDownloadMicrocodeMode03, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE); } else { ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = 1; } if (identifyDeviceData->MaxBlocksPerDownloadMicrocodeMode03 > 0) { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(identifyDeviceData->MaxBlocksPerDownloadMicrocodeMode03, AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE); + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(identifyDeviceData->MaxBlocksPerDownloadMicrocodeMode03, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE); } else { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE; + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE; } } @@ -4062,7 +5162,7 @@ IOCTLtoATA( nvCacheRequest = (PNVCACHE_REQUEST_BLOCK)(srbControl + 1); if ( ((srbDataBufferLength - sizeof(SRB_IO_CONTROL) - sizeof(NVCACHE_REQUEST_BLOCK)) < nvCacheRequest->DataBufSize) || - (nvCacheRequest->DataBufSize > AHCI_MAX_TRANSFER_LENGTH) ) { + (nvCacheRequest->DataBufSize > AHCI_MAX_TRANSFER_LENGTH_DEFAULT) ) { nvCacheRequest->NRBStatus = NRB_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; @@ -4073,7 +5173,6 @@ IOCTLtoATA( // process NVCACHE request according to function switch (nvCacheRequest->Function) { - default: status = NVCacheGeneric (ChannelExtension, Srb); break; @@ -4083,8 +5182,6 @@ IOCTLtoATA( break; } - - case IOCTL_SCSI_MINIPORT_DSM_GENERAL: status = DsmGeneralIoctlProcess(ChannelExtension, Srb); break; @@ -4129,6 +5226,19 @@ IOCTLtoATA( break; } + StorPortEtwEvent2(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS) &(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventBuildIO, + L"IOCTLtoATA finished", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"SrbControl", + srbControl->ControlCode); + return status; } @@ -4491,6 +5601,7 @@ NVCacheGeneric( ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); ULONG srbFlags = SrbGetSrbFlags(Srb); PVOID resultBuffer = NULL; + PHYSICAL_ADDRESS resultBufferPhysicalAddress = {0}; srbExtension = GetSrbExtension(Srb); srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); @@ -4586,7 +5697,8 @@ NVCacheGeneric( // status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, sizeof(ATA_TASK_FILE), - &resultBuffer); + &resultBuffer, + &resultBufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (resultBuffer == NULL) ) { @@ -4599,6 +5711,7 @@ NVCacheGeneric( AhciZeroMemory((PCHAR)resultBuffer, sizeof(ATA_TASK_FILE)); srbExtension->ResultBuffer = resultBuffer; + srbExtension->ResultBufferPhysicalAddress.QuadPart = resultBufferPhysicalAddress.QuadPart; srbExtension->ResultBufferLength = sizeof(ATA_TASK_FILE); // @@ -4697,7 +5810,7 @@ HybridInfoCompletion( 0); } - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Hybrid Info log read failed. \n", ChannelExtension->PortNumber); return; } @@ -4815,7 +5928,7 @@ HybridInfoCompletion( hybridInfo->Priorities.DirtyThresholdHigh); } - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Hybrid Info log read successfully. \n", ChannelExtension->PortNumber); @@ -4897,13 +6010,12 @@ Return Value: } else { PVOID logPageBuffer = NULL; - STOR_PHYSICAL_ADDRESS logPagePhysialAddress; - ULONG tempLength; + STOR_PHYSICAL_ADDRESS logPagePhysicalAddress; // // We need to allocate a new data buffer for log page // - status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, &logPageBuffer); + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, &logPageBuffer, &logPagePhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (logPageBuffer == NULL) ) { // @@ -4916,7 +6028,6 @@ Return Value: } AhciZeroMemory((PCHAR)logPageBuffer, ATA_BLOCK_SIZE); - logPagePhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)logPageBuffer, &tempLength); // // Note that logPageBuffer will be released in completion routine. @@ -4927,7 +6038,7 @@ Return Value: 0, 1, 0, // feature field - &logPagePhysialAddress, + &logPagePhysicalAddress, logPageBuffer, HybridInfoCompletion ); @@ -6480,7 +7591,7 @@ Return Value: } if (buffer != NULL) { - AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer); + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer, srbExtension->DataBufferPhysicalAddress); } if (evictContext != NULL) { @@ -6517,7 +7628,7 @@ HybridEvict( ULONG i; PVOID buffer = NULL; // DMA buffer allocated for EVICT command - STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; + STOR_PHYSICAL_ADDRESS bufferPhysicalAddress = {0}; logicalSectorSize = BytesPerLogicalSector(&ChannelExtension->DeviceExtension->DeviceParameters); @@ -6644,7 +7755,7 @@ HybridEvict( // // allocate DMA buffer, this buffer will be used to store ATA LBA Ranges for EVICT command // - status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, &buffer); + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, &buffer, &bufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -6660,10 +7771,10 @@ HybridEvict( srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = bufferPhysicalAddress.QuadPart; srbExtension->DataTransferLength = evictContext->AllocatedBufferLength; srbExtension->CompletionContext = (PVOID)evictContext; - bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &i); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; @@ -6703,7 +7814,7 @@ Exit: // if (status != STOR_STATUS_SUCCESS) { if (buffer != NULL) { - AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer); + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer, bufferPhysicalAddress); } if (evictContext != NULL) { @@ -6736,9 +7847,9 @@ Return Value: --*/ { ULONG status = STOR_STATUS_SUCCESS; - PSRB_IO_CONTROL srbControl; - PFIRMWARE_REQUEST_BLOCK firmwareRequest; - PSTORAGE_FIRMWARE_INFO_V2 firmwareInfo; + PSRB_IO_CONTROL srbControl = NULL; + PFIRMWARE_REQUEST_BLOCK firmwareRequest = NULL; + PSTORAGE_FIRMWARE_INFO_V2 firmwareInfo = NULL; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); @@ -6748,7 +7859,7 @@ Return Value: srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; status = STOR_STATUS_INVALID_PARAMETER; - return status; + goto Exit; } // @@ -6764,7 +7875,7 @@ Return Value: srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; status = STOR_STATUS_INVALID_PARAMETER; - return status; + goto Exit; } AhciZeroMemory((PCHAR)firmwareInfo, firmwareRequest->DataBufferLength); @@ -6807,6 +7918,37 @@ Return Value: status = STOR_STATUS_SUCCESS; } +Exit: + + if (status != STOR_STATUS_SUCCESS) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareInfo, + L"Get Firmware Info", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"Status", + status, + L"DataBufferLen", + firmwareRequest->DataBufferLength, + L"ReturnCode", + srbControl->ReturnCode, + L"Version", + (firmwareInfo == NULL) ? (0) : (firmwareInfo->Version), + L"Size", + (firmwareInfo == NULL) ? (0) : (firmwareInfo->Size), + L"UpgradeSupport", + (firmwareInfo == NULL) ? (0) : (firmwareInfo->UpgradeSupport), + NULL, + 0); + + } + return status; } @@ -6830,10 +7972,10 @@ Return Value: --*/ { - ULONG status; + ULONG status = STOR_STATUS_SUCCESS; ULONG srbDataBufferLength = 0; - PSRB_IO_CONTROL srbControl; - PFIRMWARE_REQUEST_BLOCK firmwareRequest; + PSRB_IO_CONTROL srbControl = NULL; + PFIRMWARE_REQUEST_BLOCK firmwareRequest = NULL; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); srbDataBufferLength = SrbGetDataTransferLength(Srb); @@ -6842,8 +7984,10 @@ Return Value: // A Firmware request must have at least SRB_IO_CONTROL and FIRMWARE_REQUEST_BLOCK in input buffer. // if (srbDataBufferLength < (sizeof(SRB_IO_CONTROL) + sizeof(FIRMWARE_REQUEST_BLOCK))) { + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; - return STOR_STATUS_INVALID_PARAMETER; + status = STOR_STATUS_INVALID_PARAMETER; + goto Exit; } firmwareRequest = (PFIRMWARE_REQUEST_BLOCK)(srbControl + 1); @@ -6851,13 +7995,15 @@ Return Value: if ((ULONGLONG)(srbDataBufferLength) < ((ULONGLONG)firmwareRequest->DataBufferOffset + firmwareRequest->DataBufferLength)) { srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; - return STOR_STATUS_INVALID_PARAMETER; + status = STOR_STATUS_INVALID_PARAMETER; + goto Exit; } if (firmwareRequest->Version < FIRMWARE_REQUEST_BLOCK_STRUCTURE_VERSION) { srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; - return STOR_STATUS_INVALID_PARAMETER; + status = STOR_STATUS_INVALID_PARAMETER; + goto Exit; } // @@ -6866,7 +8012,8 @@ Return Value: if (firmwareRequest->DataBufferOffset < ALIGN_UP(sizeof(SRB_IO_CONTROL) + sizeof(FIRMWARE_REQUEST_BLOCK), PVOID)) { srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; - return STOR_STATUS_INVALID_PARAMETER; + status = STOR_STATUS_INVALID_PARAMETER; + goto Exit; } // @@ -6887,6 +8034,37 @@ Return Value: } +Exit: + + if (status != STOR_STATUS_SUCCESS) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareIoctl, + L"Firmware Ioctl", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbDataBufLen", + srbDataBufferLength, + L"SrbStatus", + Srb->SrbStatus, + L"ReturnCode", + srbControl->ReturnCode, + L"FWReqVersion", + (firmwareRequest == NULL) ? (0) : (firmwareRequest->Version), + L"FWReqFunction", + (firmwareRequest == NULL) ? (0) : (firmwareRequest->Function), + L"FWBufOffset", + (firmwareRequest == NULL) ? (0) : (firmwareRequest->DataBufferOffset), + L"Status", + status, + NULL, + 0); + + } + return status; } @@ -7543,7 +8721,10 @@ QueryTemperatureInfoCompletion( NT_ASSERT(FALSE); } - srbExtension->DataBuffer = NULL; + if (srbExtension->DataBuffer != NULL) { + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); + srbExtension->DataBuffer = NULL; + } srbExtension->CompletionContext = NULL; srbExtension->CompletionRoutine = NULL; srbExtension->AtaFunction = 0; @@ -7581,7 +8762,6 @@ Return Value: PVOID buffer = NULL; STOR_PHYSICAL_ADDRESS bufferPhysicalAddress = {0}; - ULONG tempLength = 0; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); srbDataBufferLength = SrbGetDataTransferLength(Srb); @@ -7616,7 +8796,7 @@ Return Value: // // allocate DMA buffer to retrieve temperature statistics log. // - status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, (PVOID*)&buffer); + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, (PVOID*)&buffer, &bufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_ERROR; @@ -7626,8 +8806,6 @@ Return Value: AhciZeroMemory((PCHAR)buffer, IDE_GP_LOG_SECTOR_SIZE); - bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &tempLength); - IssueReadLogExtCommand(ChannelExtension, Srb, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, @@ -7911,8 +9089,6 @@ exit: return status; } - - #if _MSC_VER >= 1200 #pragma warning(pop) #else @@ -7920,4 +9096,3 @@ exit: #pragma warning(default:4201) #pragma warning(default:26015) #endif - diff --git a/storage/miniports/storahci/src/common.h b/storage/miniports/storahci/src/common.h index 3b066131..7a7b8d9c 100644 --- a/storage/miniports/storahci/src/common.h +++ b/storage/miniports/storahci/src/common.h @@ -44,6 +44,7 @@ Revision History: #define IDE_FEATURE_INVALID 0xFF +#define SCSI_VENDOR_ID_LENGTH (8) // // ATA function code @@ -84,7 +85,6 @@ Revision History: #define ATA_FLAGS_ACTIVE_REFERENCE (1 << 10) // indicates Active Reference needs to be acquired before processing the Srb and released after processing the Srb #define ATA_FLAGS_SENSEDATA_SET (1 << 11) // indicates sense data has been set to the Srb - // // helper macros // @@ -114,9 +114,23 @@ typedef enum _AHCI_ETW_EVENT_IDS { AhciEtwEventUnitHybridEvict = 11, AhciEtwEventUnitHybridSetDirtyThreshold = 12, AhciEtwEventUnitHybridWriteThrough = 13, + AhciEtwEventUnitFirmwareIoctl = 14, + AhciEtwEventUnitFirmwareInfo = 15, + AhciEtwEventUnitFirmwareDownload = 16, + AhciEtwEventUnitFirmwareDownloadComplete = 17, + AhciEtwEventUnitFirmwareActivate = 18, + AhciEtwEventUnitFirmwareActivateComplete = 19, + AhciEtwEventUnitGetPhysicalElementStatusComplete = 20, + AhciEtwEventUnitRemoveElementAndTruncateComplete = 21, + AhciEtwEventUnitGetInternalStatusDataHeaderComplete = 22, + AhciEtwEventUnitGetInternalStatusDataComplete = 23, + AhciEtwEventBuildIO = 24, + AhciEtwEventStartIO = 25, + AhciEtwEventHandleInterrupt = 26, + AhciEtwEventPortReset = 27, + AhciEtwEventIOCompletion = 28 } AHCI_ETW_EVENT_IDS, *PAHCI_ETW_EVENT_IDS; - // // task file register contents // @@ -312,6 +326,14 @@ typedef struct _HYBRID_EVICT_CONTEXT { } HYBRID_EVICT_CONTEXT, *PHYBRID_EVICT_CONTEXT; +__inline +BOOLEAN +IsUnknownDevice( + _In_ PATA_DEVICE_PARAMETERS DeviceParameters + ) +{ + return (DeviceParameters->AtaDeviceType == DeviceUnknown); +} __inline BOOLEAN @@ -601,7 +623,8 @@ __inline AhciAllocateDmaBuffer ( _In_ PVOID AdapterExtension, _In_ ULONG BufferLength, - _Post_writable_byte_size_(BufferLength) PVOID* Buffer + _Post_writable_byte_size_(BufferLength) PVOID* Buffer, + _Out_ PSTOR_PHYSICAL_ADDRESS PhysicalAddress ) { ULONG status; @@ -613,15 +636,16 @@ AhciAllocateDmaBuffer ( maxPhysicalAddress.QuadPart = 0x7FFFFFFF; // (2GB - 1) boundaryPhysicalAddress.QuadPart = 0; + status = StorPortAllocateDmaMemory(AdapterExtension, + BufferLength, + minPhysicalAddress, + maxPhysicalAddress, + boundaryPhysicalAddress, + MmCached, + MM_ANY_NODE_OK, + Buffer, + PhysicalAddress); - status = StorPortAllocateContiguousMemorySpecifyCacheNode(AdapterExtension, - BufferLength, - minPhysicalAddress, - maxPhysicalAddress, - boundaryPhysicalAddress, - MmCached, - MM_ANY_NODE_OK, - Buffer); return status; } @@ -631,14 +655,18 @@ __inline AhciFreeDmaBuffer ( _In_ PVOID AdapterExtension, _In_ ULONG_PTR BufferLength, - _In_reads_bytes_(BufferLength) _Post_invalid_ PVOID Buffer + _In_reads_bytes_(BufferLength) _Post_invalid_ PVOID Buffer, + _In_opt_ STOR_PHYSICAL_ADDRESS PhysicalAddress ) { - ULONG status; - status = StorPortFreeContiguousMemorySpecifyCache(AdapterExtension, - Buffer, - BufferLength, - MmCached); + ULONG status; + + status = StorPortFreeDmaMemory(AdapterExtension, + Buffer, + BufferLength, + MmCached, + PhysicalAddress); + return status; } @@ -862,6 +890,33 @@ AtaReportLunsCommand( _In_ PVOID Context ); +ULONG +AtaGetPhysicalElementStatusRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ); + +ULONG +AtaRemoveElementAndTruncateRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ); + +ULONG +AtaGetDeviceCurrentInternalStatusData( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ); + +ULONG +AtaGetDeviceCurrentInternalStatusDataHeader( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ); UCHAR AtaMapError( @@ -965,6 +1020,53 @@ FirmwareIoctlProcess( ); +// +// AHCI Telemetry event related. +// +#define AHCI_TELEMETRY_EVENT_VERSION 0x1 +#define AHCI_TELEMETRY_DRIVER_VERSION 0x1 + +#define AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING 0x1 + +typedef enum _AHCI_TELEMETRY_EVENT_ID { + AhciTelemetryEventIdGeneral = 0, + AhciTelemetryEventIdPortReset = 1, + AhciTelemetryEventIdPortRunningStartFail = 2, + AhciTelemetryEventIdPortErrorRecovery = 3, + AhciTelemetryEventIdNonqueuedErrorRecovery = 4, + AhciTelemetryEventIdNCQErrorRecovery = 5, + AhciTelemetryEventIdNCQErrorRecoveryComplete = 6, + AhciTelemetryEventIdResetBus = 7, + AhciTelemetryEventIdResetDeviceRequest = 8, + AhciTelemetryEventIdSurpriseRemove = 9, + AhciTelemetryEventIdLpmAdaptiveSetting = 10, + AhciTelemetryEventIdLpmSettingsModes = 11, + AhciTelemetryEventIdPortStartSuccess = 12, + AhciTelemetryEventIdReservedSlotStuck = 13, + AhciTelemetryEventIdMax = 256 +} AHCI_TELEMETRY_EVENT_ID, *PAHCI_TELEMETRY_EVENT_ID; + +// +// AHCI mark device failure related. +// +#define AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK (0x1 << 0) +#define AHCI_BUS_CHANGE_COUNT_WARNING_THRESHOLD (20) + +#define AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK (0x1 << 1) +#define AHCI_NCQ_ERROR_COUNT_WARNING_THRESHOLD (100) + +#define AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK (0x1 << 2) +#define AHCI_NON_QUEUED_ERROR_COUNT_WARNING_THRESHOLD (100) + +#define AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK (0x1 << 3) + +typedef enum _AHCI_DEVICE_FAILURE_REASON { + AhciDeviceFailureUnspecific = 0, + AhciDeviceFailureTooManyBusChange = 1, + AhciDeviceFailureTooManyNCQError = 2, + AhciDeviceFailureTooManyNonQueuedError = 3, + AhciDeviceFailureDeviceStuck = 4 +} AHCI_DEVICE_FAILURE_REASON, *PAHCI_DEVICE_FAILURE_REASON; #if _MSC_VER >= 1200 #pragma warning(pop) @@ -972,4 +1074,3 @@ FirmwareIoctlProcess( #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/data.h b/storage/miniports/storahci/src/data.h new file mode 100644 index 00000000..6bec0934 --- /dev/null +++ b/storage/miniports/storahci/src/data.h @@ -0,0 +1,96 @@ +/*++ + +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + data.h + +Abstract: + + Definitions needed by storahci log/diagnostic function. + +Author: + +--*/ + +#pragma once + +// +// Diagnostic data definitions for STORAHCI +// + +#define MAX_EXECUTION_HISTORY_ENTRY_COUNT 200 // 200 entries take up 20KB + +typedef struct _SLOT_MANAGER { + + ULONG HighPriorityAttribute; + + ULONG NCQueueSlice; + ULONG NormalQueueSlice; + ULONG SingleIoSlice; + + ULONG CommandsIssued; + ULONG CommandsToComplete; + + // + // These issued slices are used to determine the type of command + // being programmed to adapter. + // They are used instead of reading PxCI and PxSACT. + // + ULONG NCQueueSliceIssued; + ULONG NormalQueueSliceIssued; + ULONG SingleIoSliceIssued; + + ULONG Reserved; + +} SLOT_MANAGER, *PSLOT_MANAGER; + +typedef struct _EXECUTION_HISTORY { + + ULONG Function; + ULONG IS; + SLOT_MANAGER SlotManager; //SLOT_MANAGER from _AHCI_CHANNEL_EXTENSION + ULONG Px[0x10]; //Px registers value, end to AHCI_SNOTIFICATION -- SNTF + LARGE_INTEGER TimeStamp; + +} EXECUTION_HISTORY, *PEXECUTION_HISTORY; + +typedef struct _ATA_IO_RECORD { + + ULONG SuccessCount; + + ULONG CrcErrorCount; + ULONG MediaErrorCount; + ULONG EndofMediaCount; + ULONG IllegalCommandCount; + ULONG AbortedCommandCount; + ULONG DeviceFaultCount; + + ULONG OtherErrorCount; + + ULONG NcqReadLogErrorCount; // used to record the READ LOG EXT command error count when used for NCQ Error Recovery. + + ULONG PortDriverResetCount; + ULONG TotalResetCount; + +} ATA_IO_RECORD, *PATA_IO_RECORD; + +typedef struct _STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY { + + ULONG ExecutionHistoryCount; + ULONG ExecutionHistoryIndex; + EXECUTION_HISTORY History[MAX_EXECUTION_HISTORY_ENTRY_COUNT]; + +} STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY, *PSTORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY; + +typedef struct _STORAGE_DIAGNOSTIC_AHCI_DATA { + + ULONG Version; + ULONG Count; + STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY ExecutionHistory; + AHCI_PORT Registers; + ATA_IO_RECORD IoRecord; + +} STORAGE_DIAGNOSTIC_AHCI_DATA, *PSTORAGE_DIAGNOSTIC_AHCI_DATA; + diff --git a/storage/miniports/storahci/src/entrypts.c b/storage/miniports/storahci/src/entrypts.c index e9c070c8..efbe2279 100644 --- a/storage/miniports/storahci/src/entrypts.c +++ b/storage/miniports/storahci/src/entrypts.c @@ -21,11 +21,10 @@ Revision History: #pragma warning(push) #endif -#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression +#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression #pragma warning(disable:4214) // bit field types other than int #pragma warning(disable:4201) // nameless struct/union - #include "generic.h" @@ -102,12 +101,12 @@ Return Value: hwInitializationData.NumberOfAccessRanges = NUM_ACCESS_RANGES; // - // TODO : It's not a given that SATA on ARM64 will only ever be ACPI enumerated - // This is just for the on-board controller. We will have to come up with a - // solution for PCI bus enuerated on ARM64 + // Support both PCI/ACPI enumerations on ARM64 platform for SATA device. + // Miniport uses flag to indicate storport to query/override the interface type reported here. // -#if defined (_ARM64_) - hwInitializationData.AdapterInterfaceType = ACPIBus; +#if defined(_ARM_) || defined(_ARM64_) + hwInitializationData.FeatureSupport |= STOR_FEATURE_SET_ADAPTER_INTERFACE_TYPE; + hwInitializationData.AdapterInterfaceType = InterfaceTypeUndefined; #else hwInitializationData.AdapterInterfaceType = PCIBus; #endif @@ -122,6 +121,7 @@ Return Value: hwInitializationData.FeatureSupport |= STOR_FEATURE_DEVICE_DESCRIPTOR_FROM_ATA_INFO_VPD; // indicating that port driver forms STORAGE_DEVICE_DESCRIPTOR from ATA Information VPD page rather than INQUIRY data hwInitializationData.FeatureSupport |= STOR_FEATURE_EXTRA_IO_INFORMATION; // Indicating that miniport driver wants SRBEX_DATA_IO_INFO in a SRBEX if available hwInitializationData.FeatureSupport |= STOR_FEATURE_ADAPTER_NOT_REQUIRE_IO_PORT; // Indicating that miniport driver doesn't require IO Port resource for its adapter. + hwInitializationData.FeatureSupport |= STOR_FEATURE_DUMP_INFO; // Indicating that the miniport driver supports the dump info SRBs. // Set required extension sizes. hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION); @@ -206,7 +206,6 @@ Return Values: AdapterExtension->StateFlags.InterruptMessagePerPort = 0; } - StorPortDebugPrint(3, "StorAHCI - Interrupt Mode: Adapter 0x%04X-0x%04X --- %s\n", AdapterExtension->VendorID, AdapterExtension->DeviceID, @@ -242,11 +241,16 @@ AllocateResourcesForAdapter( Therefore the number of Command Headers must be 256/32 = 8. Round cap.NCS to the next multiple of 8 */ { + ULONG alignment = 0x400; // 1K + BOOLEAN dumpMode = IsDumpMode(AdapterExtension); ULONG paddedNCS = 0; ULONG paddedSrbExtensionSize = 0; ULONG nonCachedExtensionSize = 0; + ULONG channelExtensionSize = sizeof(AHCI_CHANNEL_EXTENSION); PVOID portsChannelExtension = NULL; - PCHAR portsUncachedExtension = NULL; + ULONG executionHistorySize = sizeof(EXECUTION_HISTORY) * MAX_EXECUTION_HISTORY_ENTRY_COUNT; + PVOID portsExecutionHistory = NULL; + ULONG_PTR left = 0; ULONG i = 0; ULONG j = 0; @@ -268,33 +272,40 @@ AllocateResourcesForAdapter( sizeof(IDENTIFY_DEVICE_DATA) + // 512 bytes ATA_BLOCK_SIZE + // ReadLogExtPageData --- 512 bytes INQUIRYDATABUFFERSIZE; // Inquiry Data + // round up to KiloBytes if it's not dump mode. this makes sure that NonCachedExtension for next port can align to 1K. - if (!IsDumpMode(AdapterExtension)) { - nonCachedExtensionSize = ((nonCachedExtensionSize - 1) / 0x400 + 1) * 0x400; - // total size for all ports - nonCachedExtensionSize *= PortCount; + if (!dumpMode) { + nonCachedExtensionSize = ((nonCachedExtensionSize - 1) / alignment + 1) * alignment; + + AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension, + ConfigInfo, + nonCachedExtensionSize * PortCount); } else { // dump mode, address returned from StorPortGetUncachedExtension() is not guaranteed align with 1K. // adding 1K into length so that we can start from 1K alignment safely. // NOTE: StorPortAllocatePool is not supported in dump stack, so we allocate ChannelExtension from UnCachedExtension as work around. - nonCachedExtensionSize += 0x400 + sizeof(AHCI_CHANNEL_EXTENSION); - } + nonCachedExtensionSize += alignment; - AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension, ConfigInfo, nonCachedExtensionSize); + AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension, + ConfigInfo, + (nonCachedExtensionSize + channelExtensionSize) * PortCount); + } if (AdapterExtension->NonCachedExtension == NULL) { - // we cannot continue if cannot get nonCachedMemory for Channels. + + // we cannot continue if cannot get nonCachedMemory for Channels. + NT_ASSERT(FALSE); return FALSE; } - AhciZeroMemory((PCHAR)AdapterExtension->NonCachedExtension, nonCachedExtensionSize); + AhciZeroMemory((PCHAR)AdapterExtension->NonCachedExtension, nonCachedExtensionSize * PortCount); // 2.2 allocate resources for Ports which need AHCI_CHANNEL_EXTENSION for each of them. - if (!IsDumpMode(AdapterExtension)) { + if (!dumpMode) { ULONG status = STOR_STATUS_SUCCESS; // allocate pool and zero the content status = StorPortAllocatePool(AdapterExtension, - PortCount * sizeof(AHCI_CHANNEL_EXTENSION), + channelExtensionSize * PortCount, AHCI_POOL_TAG, (PVOID*)&portsChannelExtension); @@ -302,36 +313,49 @@ AllocateResourcesForAdapter( // we cannot continue if cannot get memory for ChannelExtension. return FALSE; } - AhciZeroMemory((PCHAR)portsChannelExtension, PortCount * sizeof(AHCI_CHANNEL_EXTENSION)); - //get the starting pointer - portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension; - } else { - ULONG_PTR left = 0; - // get channelExtension - portsChannelExtension = (PCHAR)AdapterExtension->NonCachedExtension + nonCachedExtensionSize - sizeof(AHCI_CHANNEL_EXTENSION); - //get the starting pointer; align the starting location to 1K. - left = ((ULONG_PTR)AdapterExtension->NonCachedExtension) % 1024; + status = StorPortAllocatePool(AdapterExtension, + (ULONG)(executionHistorySize * PortCount), + AHCI_POOL_TAG, + (PVOID*)&portsExecutionHistory); - if (left > 0) { - portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension + 1024 - left; - } else { - portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension; + if ((status != STOR_STATUS_SUCCESS) || (portsExecutionHistory == NULL)) { + // Continue if cannot get memory for ExecutionHistory. + NT_ASSERT(FALSE); } + } else { + // get channelExtension from UnCachedExtension + portsChannelExtension = (PCHAR)AdapterExtension->NonCachedExtension + (nonCachedExtensionSize * PortCount); } - // reset nonCachedExtensionSize to be the size for one Port, it works for dump case also as PortCount is '1'. - nonCachedExtensionSize /= PortCount; + AhciZeroMemory((PCHAR)portsChannelExtension, channelExtensionSize * PortCount); - // assign allocated memory and uncachedExension into ChannelExtensions + // assign allocated uncachedExension into ChannelExtensions for (i = 0; i <= AdapterExtension->HighestPort; i++) { if ( (AdapterExtension->PortImplemented & (1 << i)) != 0 ) { + PCHAR portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension + nonCachedExtensionSize * j; + // this port is implemented, allocate and initialize extension for the port - AdapterExtension->PortExtension[i] = (PAHCI_CHANNEL_EXTENSION)((PCHAR)portsChannelExtension + sizeof(AHCI_CHANNEL_EXTENSION) * j); + AdapterExtension->PortExtension[i] = (PAHCI_CHANNEL_EXTENSION)((PCHAR)portsChannelExtension + channelExtensionSize * j); AdapterExtension->PortExtension[i]->AdapterExtension = AdapterExtension; AdapterExtension->PortExtension[i]->PortNumber = i; // set ChannelExtension fields that use NonCachedExtension - AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension + nonCachedExtensionSize * j); + if (!dumpMode) { + AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension); + if (portsExecutionHistory != NULL) { + AdapterExtension->PortExtension[i]->ExecutionHistory = (PEXECUTION_HISTORY)((PCHAR)portsExecutionHistory + executionHistorySize * j); + } + } else { + //get the starting pointer; align the starting location to 1K. + left = ((ULONG_PTR)portsUncachedExtension) % alignment; + + if (left > 0) { + AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension + alignment - left); + } else { + AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension); + } + } + AdapterExtension->PortExtension[i]->ReceivedFIS = (PAHCI_RECEIVED_FIS)((PCHAR)AdapterExtension->PortExtension[i]->CommandList + sizeof(AHCI_COMMAND_HEADER) * paddedNCS); AdapterExtension->PortExtension[i]->Local.SrbExtension = (PAHCI_SRB_EXTENSION)((PCHAR)AdapterExtension->PortExtension[i]->ReceivedFIS + sizeof(AHCI_RECEIVED_FIS)); AdapterExtension->PortExtension[i]->Sense.SrbExtension = (PAHCI_SRB_EXTENSION)((PCHAR)AdapterExtension->PortExtension[i]->Local.SrbExtension + paddedSrbExtensionSize); @@ -377,7 +401,7 @@ It performs: 2.2 Initialize adapterExtension with version & cap values 3.1 Turn on AE, reset the controller if AE is already set AHCI 1.1 Section 10.1.2 - 1. - "Indicate that system software is AHCI aware by setting GHC.AE to �1�." + "Indicate that system software is AHCI aware by setting GHC.AE to '1'." 3.2 Determine which ports are implemented by the HBA AHCI 1.1 Section 10.1.2 - 2. "Determine which ports are implemented by the HBA, by reading the PI register. This bitmap value will aid software in determining how many ports are available and which port registers need to be initialized." @@ -387,10 +411,11 @@ It performs: 4.1 Turn on IE, pending interrupts will be cleared when port starts This has to be done after 3.2 because we need to know the number of channels before we check each PxIS. Verify that none of the PxIS registers are loaded, but take no action - Note: Due to the multi-tiered nature of the AHCI HBA�s interrupt architecture, system software must always ensure that the PxIS (clear this first) and IS.IPS (clear this second) registers are cleared to �0� before programming the PxIE and GHC.IE registers. This will prevent any residual bits set in these registers from causing an interrupt to be asserted. + Note: Due to the multi-tiered nature of the AHCI HBA's interrupt architecture, system software must always ensure that the PxIS (clear this first) and IS.IPS (clear this second) registers are cleared to '0' before programming the PxIE and GHC.IE registers. This will prevent any residual bits set in these registers from causing an interrupt to be asserted. However, the interrupt handler hasn't been hooked up by StorPort yet, so no interrupts will be handled by software until that happens. - 4.2 Allocate resources for both DMA use and all Channel/Port extensions. - 4.3 Initialize ports and start them. Dump stack will do this when receiving the INQUIRY command + 4.2 Initialize the remaining port configuration settings after getting registry settings. + 4.3 Allocate resources for both DMA use and all Channel/Port extensions. + 4.4 Initialize ports and start them. Dump stack will do this when receiving the INQUIRY command Affected Variables/Registers: AdapterExtension->ABAR_Address @@ -399,7 +424,7 @@ Affected Variables/Registers: AdapterExtension->Version GHC.AE, GHC.IE, GHC.HR - IS and all fields in the HBA�s register memory space except PxFB/PxFBU/PxCLB/PxCLBU that are not HwInit + IS and all fields in the HBA's register memory space except PxFB/PxFBU/PxCLB/PxCLBU that are not HwInit Return Values: The miniport driver returns TRUE if it successfully execute the whole function. @@ -421,11 +446,12 @@ Note: ULONG piMask = 0; UCHAR numberOfHighestPort = 0; ULONG portCount = 0; + ULONG portNumber; //Used to enable the AHCI interface AHCI_Global_HBA_CONTROL ghc = {0}; //guids GUID powerSettingChangeGuids[2] = {0}; - + BOOLEAN dumpMode = FALSE; PAHCI_DUMP_CONTEXT dumpContext = (PAHCI_DUMP_CONTEXT)ConfigInfo->MiniportDumpData; UNREFERENCED_PARAMETER(HwContext); @@ -447,48 +473,54 @@ Note: adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber; adapterExtension->SlotNumber = ConfigInfo->SlotNumber; - //1.1 Get dump mode + //1.1 Get dump mode adapterExtension->DumpMode = ConfigInfo->DumpMode; + dumpMode = IsDumpMode(adapterExtension); - if (IsDumpMode(adapterExtension)) { + if (dumpMode) { if (dumpContext != NULL) { - // In dump/hibernation mode, need to mark ConfigInfo->MiniportDumpData and any embedded memory buffer(s) in MiniportDumpData - StorPortMarkDumpMemory(AdapterExtension, dumpContext, sizeof(AHCI_DUMP_CONTEXT), 0); + // In dump/hibernation mode, need to mark ConfigInfo->MiniportDumpData + // and any embedded memory buffer(s) in MiniportDumpData + StorPortMarkDumpMemory(AdapterExtension, + dumpContext, + FIELD_OFFSET(AHCI_DUMP_CONTEXT, Ports) + + (dumpContext->PortCount * sizeof(AHCI_DUMP_PORT_CONTEXT)), + 0); } else { NT_ASSERT(FALSE); return SP_RETURN_ERROR; } } - //1.2 Gather Vendor,Device,Revision IDs from PCI - if (!IsDumpMode(adapterExtension)) { + //1.2 Gather Vendor,Device,Revision IDs from PCI + if (!dumpMode) { - // - // TODO : Ensure that we can enumerate using PCI as well as ACPI - // right now this code is for ACPI only. - // -#if !defined(_ARM64_) - ULONG pcicfgLen = 0; - UCHAR pcicfgBuffer[0x30] = {0}; + // + // Enumeration may be via either PCI or ACPI. Bus type is set by storport/miniport. + // Check if we are enumerating via PCI, and retrieve the address appropriately. + // + if (ConfigInfo->AdapterInterfaceType == PCIBus) { + ULONG pcicfgLen = 0; + UCHAR pcicfgBuffer[0x30] = {0}; - pcicfgLen = StorPortGetBusData(adapterExtension, - PCIConfiguration, - ConfigInfo->SystemIoBusNumber, - (ULONG)ConfigInfo->SlotNumber, - (PVOID)pcicfgBuffer, - (ULONG)0x30); - if (pcicfgLen == 0x30) { - PPCI_COMMON_CONFIG pciConfigData = (PPCI_COMMON_CONFIG)pcicfgBuffer; - adapterExtension->VendorID = pciConfigData->VendorID; - adapterExtension->DeviceID = pciConfigData->DeviceID; - adapterExtension->RevisionID = pciConfigData->RevisionID; - // on PCI bus, AHCI Base Address is BAR5. Bits 0-3 defined for other usages, not part of address value. - adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0); - } else { - NT_ASSERT(FALSE); - return SP_RETURN_ERROR; + pcicfgLen = StorPortGetBusData(adapterExtension, + PCIConfiguration, + ConfigInfo->SystemIoBusNumber, + (ULONG)ConfigInfo->SlotNumber, + (PVOID)pcicfgBuffer, + (ULONG)0x30); + if (pcicfgLen == 0x30) { + PPCI_COMMON_CONFIG pciConfigData = (PPCI_COMMON_CONFIG)pcicfgBuffer; + adapterExtension->VendorID = pciConfigData->VendorID; + adapterExtension->DeviceID = pciConfigData->DeviceID; + adapterExtension->RevisionID = pciConfigData->RevisionID; + // on PCI bus, AHCI Base Address is BAR5. Bits 0-3 defined for other usages, not part of address value. + adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0); + } else { + NT_ASSERT(FALSE); + return SP_RETURN_ERROR; + } } -#endif } else { adapterExtension->VendorID = dumpContext->VendorID; adapterExtension->DeviceID = dumpContext->DeviceID; @@ -499,22 +531,22 @@ Note: adapterExtension->LogFlags = dumpContext->LogFlags; } - //2.1 Initialize adapterExtension with AHCI abar + //2.1 Initialize adapterExtension with AHCI abar abar = GetABARAddress(adapterExtension, ConfigInfo); - //2.1.1 If abar is still NULL after all of that, malformed resources. We aren't going to get very far. + //2.1.1 If abar is still NULL after all of that, malformed resources. We aren't going to get very far. if (abar == NULL) { return SP_RETURN_ERROR; } else { adapterExtension->ABAR_Address = abar; } - //2.2 Initialize adapterExtension with version & cap values + //2.2 Initialize adapterExtension with version & cap values adapterExtension->Version.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->VS.AsUlong); adapterExtension->CAP.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->CAP.AsUlong); adapterExtension->CAP2.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2.AsUlong); - //3.1 Turn on AE (AHCI 1.1 Section 10.1.2 - 1) + //3.1 Turn on AE (AHCI 1.1 Section 10.1.2 - 1) ghc.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->GHC.AsUlong); if (ghc.AE == 1) { if (!AhciAdapterReset(adapterExtension)) { @@ -527,40 +559,54 @@ Note: adapterExtension->IS = &abar->IS; - //3.2 Determine which ports are implemented by the HBA (AHCI 1.1 Section 10.1.2 - 2) - adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI); + //3.2 Determine which ports are implemented by the HBA + if (!dumpMode) { + // Get implemented ports from PI register (AHCI 1.1 Section 10.1.2 - 2) + adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI); + } else { + // Get implemented ports from the dump context + adapterExtension->PortImplemented = 0; - // - // AHCI specification requires that at least one bit is set in PI register. - // In other words, at least one port must be implemented. - // + for (i = 0; i < dumpContext->PortCount; i++) { + adapterExtension->PortImplemented |= 1 << dumpContext->Ports[i].PortNumber; + } + } + + // At least one port must be implemented if (adapterExtension->PortImplemented == 0) { return SP_RETURN_ERROR; } - //3.3 Get biggest port number value and implemented port count. - //3.3.1 get biggest port number value - numberOfHighestPort = AHCI_MAX_PORT_COUNT; - //Check from highest bit to lowest bit for the first highest bit set - for (piMask = (ULONG)(1 << (AHCI_MAX_PORT_COUNT - 1)); piMask != 0; piMask = (ULONG)(piMask >> 1)){ - numberOfHighestPort--; - if ( (adapterExtension->PortImplemented & piMask) != 0) { - break; + //3.3 Get biggest port number value and implemented port count + if (!dumpMode) { + numberOfHighestPort = AHCI_MAX_PORT_COUNT; + //Check from highest bit to lowest bit for the first highest bit set + for (piMask = (ULONG)(1 << (AHCI_MAX_PORT_COUNT - 1)); piMask != 0; piMask = (ULONG)(piMask >> 1)){ + numberOfHighestPort--; + if ((adapterExtension->PortImplemented & piMask) != 0) { + break; + } } - } //numberOfHighestPort now holds the correct value - //3.3.2 get implemented port count - if (!IsDumpMode(adapterExtension)) { for (i = 0; i <= numberOfHighestPort; i++) { - if ( (adapterExtension->PortImplemented & (1 << i)) != 0 ) { + if ((adapterExtension->PortImplemented & (1 << i)) != 0) { portCount++; } } } else { - // in dump environment, only use the desired port. - portCount = 1; + numberOfHighestPort = 0; + for (i = 0; i < dumpContext->PortCount; i++) { + if (dumpContext->Ports[i].PortNumber > numberOfHighestPort) { + numberOfHighestPort = (UCHAR)dumpContext->Ports[i].PortNumber; + } + } + + portCount = dumpContext->PortCount; } + NT_ASSERT(numberOfHighestPort < AHCI_MAX_PORT_COUNT); + adapterExtension->HighestPort = numberOfHighestPort; + // // AHCI specification requires that implemented port count (number of bit set in PI register) // is less than or equal to CAP.NP + 1 @@ -570,8 +616,8 @@ Note: // NT_ASSERT(portCount > 0 && portCount <= (adapterExtension->CAP.NP + 1)); - //3.4 Initializing the rest of PORT_CONFIGURATION_INFORMATION - ConfigInfo->MaximumTransferLength = AHCI_MAX_TRANSFER_LENGTH; + //3.4 Initializing the rest of PORT_CONFIGURATION_INFORMATION + ConfigInfo->MaximumTransferLength = AHCI_MAX_TRANSFER_LENGTH_DEFAULT; ConfigInfo->NumberOfPhysicalBreaks = 0x21; // Since "NumberOfPhysicalBreaks" has been used in storage stack as the count of entries, use value of physical breaks plus one. ConfigInfo->AlignmentMask = 1; // ATA devices need WORD alignment ConfigInfo->ScatterGather = TRUE; @@ -584,33 +630,10 @@ Note: ConfigInfo->BusResetHoldTime = 0; // StorAHCI wait RESET to be completed by itself, no need for port driver to wait. ConfigInfo->MaxNumberOfIO = portCount * adapterExtension->CAP.NCS; - if (adapterExtension->CAP.S64A) { -#if defined (_ARM64_) - ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_FULL64BIT_NO_BOUNDARY_REQ_SUPPORTED; -#else - ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_SUPPORTED; -#endif - // increase size of SrbExtension to accommodate 64-bit move commands - // and 1 extra Scripts instruction (turn off 64-bit mode) - ConfigInfo->SrbExtensionSize += (ULONG)((ConfigInfo->NumberOfPhysicalBreaks + 2) * 4); - } - ConfigInfo->FeatureSupport |= STOR_ADAPTER_FEATURE_STOP_UNIT_DURING_POWER_DOWN; ConfigInfo->FeatureSupport |= STOR_ADAPTER_FEATURE_RICH_TEMPERATURE_THRESHOLD; - - // 3.4.2 update PortImplemented, HighestPort and portCount if necessary - if (!IsDumpMode(adapterExtension)) { - adapterExtension->HighestPort = numberOfHighestPort; - } else { - - if (dumpContext->DumpPortNumber < AHCI_MAX_PORT_COUNT) { - adapterExtension->PortImplemented = 1 << dumpContext->DumpPortNumber; - adapterExtension->HighestPort = dumpContext->DumpPortNumber; - } else { - NT_ASSERT(FALSE); - return SP_RETURN_ERROR; - } - } + ConfigInfo->FeatureSupport |= STOR_ADAPTER_FEATURE_DEVICE_TELEMETRY; + ConfigInfo->FeatureSupport |= STOR_ADAPTER_DMA_V3_PREFERRED; // // Set interrupt related entry points. @@ -625,7 +648,7 @@ Note: // // Allocate MessageGroupAffinity buffer for later use. // - if (!IsDumpMode(adapterExtension)) { + if (!dumpMode) { StorPortAllocatePool(AdapterExtension, sizeof(GROUP_AFFINITY) * (adapterExtension->HighestPort + 1), AHCI_POOL_TAG, @@ -636,8 +659,8 @@ Note: ConfigInfo->InterruptSynchronizationMode = InterruptSynchronizeAll; } - //3.5 Register Power Setting Change Notification Guids - if (!IsDumpMode(adapterExtension)) { + //3.5 Register Power Setting Change Notification Guids + if (!dumpMode) { /* 0b2d69d7-a2a1-449c-9680-f91c70521c60 -DIPM/HIPM */ powerSettingChangeGuids[0].Data1 = 0x0b2d69d7; powerSettingChangeGuids[0].Data2 = 0xa2a1; @@ -667,13 +690,25 @@ Note: StorPortSetPowerSettingNotificationGuids(AdapterExtension, 2, powerSettingChangeGuids); } - //4.1 Turn on IE, pending interrupts will be cleared when port starts + //4.1 Turn on IE, pending interrupts will be cleared when port starts adapterExtension->LastInterruptedPort = (ULONG)(-1); ghc.IE = 1; StorPortWriteRegisterUlong(adapterExtension, &abar->GHC.AsUlong, ghc.AsUlong); + //4.2.2 Configure DMA 64 bit support settings. + if (adapterExtension->CAP.S64A) { +#if defined (_ARM64_) + ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_FULL64BIT_NO_BOUNDARY_REQ_SUPPORTED; +#else + ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_SUPPORTED; +#endif + + // increase size of SrbExtension to accommodate 64-bit move commands + // and 1 extra Scripts instruction (turn off 64-bit mode) + ConfigInfo->SrbExtensionSize += (ULONG)((ConfigInfo->NumberOfPhysicalBreaks + 2) * 4); + } - //4.2 allocate resources, implemented port information needs to be ready before calling the following routine. + //4.3 allocate resources, implemented port information needs to be ready before calling the following routine. if (adapterExtension->StateFlags.StoppedState == 0) { if (!AllocateResourcesForAdapter(adapterExtension, ConfigInfo, portCount)) { return SP_RETURN_ERROR; @@ -693,8 +728,8 @@ Note: // // 3. Currently WorkerTimer is used only for PartialToSlumber during interrupt servicing (not applicable in dump mode) // - if (!IsDumpMode(adapterExtension)) - { + if (!dumpMode) { + for (i = 0; i <= adapterExtension->HighestPort; i++) { if (adapterExtension->PortExtension[i] != NULL) { @@ -726,8 +761,8 @@ Note: // reset "StoppedState". NOTE: This field should not be referenced anymore in this function after following line. adapterExtension->StateFlags.StoppedState = 0; - // 4.3 initialize ports and start them. - //4.3.1 initialize all AHCI ports + //4.4 initialize ports and start them. + //4.4.1 initialize all AHCI ports for (i = 0; i <= adapterExtension->HighestPort; i++) { if (adapterExtension->PortExtension[i] != NULL) { // in case of PortInitialize fails, ChannelExtension->StateFlags.Initialized will remain as 'FALSE'. there will be not attempt to start the Port. @@ -735,35 +770,37 @@ Note: } } - if (IsDumpMode(adapterExtension)) { + if (dumpMode) { // // In dump mode copy registry flags and telemetry configuration from the dump context. If telemetry extends to more than one // device the logic should cover migration of per device settings, initially we are limited to a single boot device // - ULONG Index; -#pragma warning (suppress: 6385) // dumpContext->DumpPortNumber is guaranteed to be in-bound earlier - if (adapterExtension->PortExtension[dumpContext->DumpPortNumber] != NULL) { + for (i = 0; i < dumpContext->PortCount; i++) { + portNumber = dumpContext->Ports[i].PortNumber; - adapterExtension->PortExtension[dumpContext->DumpPortNumber]->RegistryFlags = dumpContext->PortRegistryFlags; + if (adapterExtension->PortExtension[portNumber] != NULL) { - // - // If the table in dump context is empty (not filled) - keep static data intact - // - if (dumpContext->PublicGPLogTableAddresses[0]) { - for(Index=0; - Index < sizeof(AhciPublicGPLogTableAddresses) / sizeof(AhciPublicGPLogTableAddresses[0] ); - Index++) { - AhciPublicGPLogTableAddresses[Index] = dumpContext->PublicGPLogTableAddresses[Index]; - } + adapterExtension->PortExtension[portNumber]->RegistryFlags = dumpContext->Ports[i].PortRegistryFlags; - AhciGPLogPageIntoPrivate = dumpContext->PrivateGPLogPageAddress; - } + // + // If the table in dump context is empty (not filled) - keep static data intact + // + /*if (dumpContext->PublicGPLogTableAddresses[0]) { + for(Index=0; + Index < sizeof(AhciPublicGPLogTableAddresses) / sizeof(AhciPublicGPLogTableAddresses[0] ); + Index++) { + AhciPublicGPLogTableAddresses[Index] = dumpContext->PublicGPLogTableAddresses[Index]; + } + + AhciGPLogPageIntoPrivate = dumpContext->PrivateGPLogPageAddress; + }*/ - StorPortMoveMemory((PVOID)&adapterExtension->PortExtension[dumpContext->DumpPortNumber]->DeviceExtension->HybridInfo, - (PVOID)&dumpContext->HybridInfo, - sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); + StorPortMoveMemory((PVOID)&adapterExtension->PortExtension[portNumber]->DeviceExtension->HybridInfo, + (PVOID)&dumpContext->Ports[i].HybridInfo, + sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); + } } } @@ -797,8 +834,6 @@ AhciHwInitialize ( // // Turn on DPC Redirection if it's supported. // - // TODO : Revisit this : Disable these perf optimizations for now during bringup. -#if !defined (_ARM64_) if ((status == STOR_STATUS_SUCCESS) && ((perfConfigData.Flags & STOR_PERF_DPC_REDIRECTION) != 0)) { @@ -841,7 +876,6 @@ AhciHwInitialize ( NT_ASSERT(status == STOR_STATUS_SUCCESS); } -#endif // // async process to get all ports into running state @@ -876,7 +910,6 @@ AhciHwPassiveInitialize ( // 2. check if ACPI supports turning off power on link AhciAdapterEvaluateDSMMethod(adapterExtension); - // // Get and cache D3Cold support. // @@ -886,8 +919,7 @@ AhciHwPassiveInitialize ( if (status == STOR_STATUS_SUCCESS) { adapterExtension->StateFlags.D3ColdSupported = d3ColdSupported; } - - + if (reportF1State) { bufferSize += STOR_POFX_COMPONENT_IDLE_STATE_SIZE; } @@ -911,8 +943,11 @@ AhciHwPassiveInitialize ( if (IsD3ColdAllowed(adapterExtension)) { adapterExtension->PoFxDevice->Flags = STOR_POFX_DEVICE_FLAG_ENABLE_D3_COLD; } + + // Indicate miniport opt-in adapter D3 wake support. + adapterExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_ADAPTER_D3_WAKE; - // indicate dump miniport can't bring adapter to active + // Indicate dump miniport can't bring adapter to active adapterExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_NO_DUMP_ACTIVE; adapterExtension->PoFxDevice->Components[0].Version = STOR_POFX_COMPONENT_VERSION_V1; @@ -948,7 +983,6 @@ AhciHwPassiveInitialize ( goto Exit; } - // register success adapterExtension->StateFlags.PoFxEnabled = TRUE; adapterExtension->StateFlags.PoFxActive = TRUE; @@ -983,7 +1017,6 @@ Return Value: AhciPortAcpiDSMControl(AdapterExtension, (ULONG)-1, FALSE); } - return; } @@ -1078,8 +1111,9 @@ AhciAdapterRemoval ( Release resources allocated for the adapter and its managed ports/devices */ { - ULONG i; - PVOID bufferToFree = NULL; + ULONG i; + PVOID bufferToFree = NULL; + PVOID executionHistoryToFree = NULL; if (IsDumpMode(AdapterExtension)) { return; @@ -1129,6 +1163,13 @@ AhciAdapterRemoval ( AdapterExtension->PortExtension[i]->StateFlags.PoFxActive = FALSE; } + if ((executionHistoryToFree == NULL) && (AdapterExtension->PortExtension[i]->ExecutionHistory != NULL)) { + executionHistoryToFree = AdapterExtension->PortExtension[i]->ExecutionHistory; + } + + if (AdapterExtension->PortExtension[i]->ExecutionHistory != NULL) { + AdapterExtension->PortExtension[i]->ExecutionHistory = NULL; + } AdapterExtension->PortExtension[i] = NULL; } @@ -1138,6 +1179,10 @@ AhciAdapterRemoval ( StorPortFreePool(AdapterExtension, bufferToFree); } + if (executionHistoryToFree != NULL) { + StorPortFreePool(AdapterExtension, executionHistoryToFree); + } + return; } @@ -1274,7 +1319,8 @@ Return Value: NT_ASSERT(adapterPower != NULL); - StorPortDebugPrint(3, "StorAHCI - LPM: Adapter - %s\n", adapterPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3"); + StorPortDebugPrint(3, "StorAHCI - LPM: Adapter SystemIoBusNumber:%d - %s\n", + adapterExtension->SystemIoBusNumber, adapterPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3"); if (adapterPower->PowerState == StorPowerDeviceD0) { AhciAdapterPowerUp(adapterExtension); //power up @@ -1296,12 +1342,12 @@ Return Value: if (powerHints->Size >= sizeof(STOR_SYSTEM_POWER_HINTS)) { - StorPortDebugPrint(3, "StorAHCI - LPM: Adapter - System Power Hint - State: %u - Latency: %u ms \n", powerHints->SystemPower, powerHints->ResumeLatencyMSec); + StorPortDebugPrint(3, "StorAHCI - LPM: Adapter SystemIoBusNumber:%d - System Power Hint - State: %u - Latency: %u ms\n", + adapterExtension->SystemIoBusNumber, powerHints->SystemPower, powerHints->ResumeLatencyMSec); adapterExtension->SystemPowerHintState = powerHints->SystemPower; adapterExtension->SystemPowerResumeLatencyMSec = powerHints->ResumeLatencyMSec; - if (adapterExtension->TracingEnabled) { StorPortEtwEvent2(AdapterExtension, NULL, @@ -1361,6 +1407,15 @@ AhciHwResetBus ( status = AhciPortReset(adapterExtension->PortExtension[PathId], TRUE); AhciInterruptSpinlockRelease(adapterExtension, MAXULONG, &lockhandle); adapterExtension->PortExtension[PathId]->DeviceExtension[0].IoRecord.PortDriverResetCount++; + + AhciTelemetryLogResetErrorRecovery(adapterExtension->PortExtension[PathId], + (PSTOR_ADDRESS)&(adapterExtension->PortExtension[PathId]->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdResetBus, + "AhciHwResetBus", + 0, + "PortResetStatus", + status + ); } return status; @@ -1390,7 +1445,9 @@ AhciHwBuildIo ( NT_ASSERT(Srb->SrbStatus == SRB_STATUS_PENDING); // SrbExtension is not Null-ed by Storport, so do it here. - AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); + // Zero PRDT later when elements number in the SGL is available. + AhciZeroMemory((PCHAR)(&(srbExtension->AtaFunction)), (sizeof(AHCI_SRB_EXTENSION) - FIELD_OFFSET(AHCI_SRB_EXTENSION, AtaFunction))); + AhciZeroMemory((PCHAR)srbExtension, FIELD_OFFSET(AHCI_COMMAND_TABLE, PRDT)); channelExtension = adapterExtension->PortExtension[pathId]; @@ -1409,11 +1466,63 @@ AhciHwBuildIo ( if ( !IsPortValid(adapterExtension, pathId) ) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + StorPortEtwChannelEvent2(adapterExtension, + NULL, + StorportEtwEventOperational, + AhciEtwEventBuildIO, + L"No device", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"function", + function, + L"srbFlags", + srbFlags); + goto exit; + } + + // + // If the adapter was removed, fail this request. + // We first check to see if the adapter is removable so that we can + // avoid incurring a register access if it is not removable. + // + if (IsAdapterRemovable(adapterExtension) && + IsAdapterRemoved(adapterExtension)) { + Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + StorPortEtwChannelEvent2(adapterExtension, + (channelExtension != NULL) ? ((PSTOR_ADDRESS) &(channelExtension->DeviceExtension->DeviceAddress)) : (NULL), + StorportEtwEventOperational, + AhciEtwEventBuildIO, + L"Adapter removed during BuildIo", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"function", + function, + L"srbFlags", + srbFlags); goto exit; } } switch (function) { + // + // Get the flag from Storport that tells us if the adapter is removable or not. + // + case SRB_FUNCTION_PNP: { + PSRBEX_DATA_PNP pnpData = (PSRBEX_DATA_PNP)SrbGetSrbExDataByType((PSTORAGE_REQUEST_BLOCK)Srb, SrbExDataTypePnP); + if (((pnpData->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST)) && + (pnpData->PnPAction == StorQueryCapabilities) && + (SrbGetDataTransferLength(Srb) >= sizeof(STOR_DEVICE_CAPABILITIES_EX))) { + PSTOR_DEVICE_CAPABILITIES_EX storCapabilities = (PSTOR_DEVICE_CAPABILITIES_EX)SrbGetDataBuffer(Srb); + adapterExtension->StateFlags.Removable = storCapabilities->Removable; + Srb->SrbStatus = SRB_STATUS_SUCCESS; + } + break; + } + case SRB_FUNCTION_IO_CONTROL: { if ((srbFlags & SRB_IOCTL_FLAGS_ADAPTER_REQUEST) == 0) { @@ -1424,6 +1533,10 @@ AhciHwBuildIo ( if ( ( srbExtension->Sgl == NULL ) && ( IsDataTransferNeeded((PSTORAGE_REQUEST_BLOCK)Srb) ) ) { srbExtension->Sgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(adapterExtension, Srb); } + if (srbExtension->Sgl != NULL) { + // Zero PRDT according to SGL elements number to avoid unnecessary CPU usage. + AhciZeroMemory((PCHAR)(srbExtension->CommandTable.PRDT), (srbExtension->Sgl->NumberOfElements * sizeof(AHCI_PRDT))); + } } } @@ -1438,6 +1551,10 @@ AhciHwBuildIo ( if ( ( srbExtension->Sgl == NULL ) && ( IsDataTransferNeeded((PSTORAGE_REQUEST_BLOCK)Srb) ) ) { srbExtension->Sgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(adapterExtension, Srb); } + if (srbExtension->Sgl != NULL) { + // Zero PRDT according to SGL elements number to avoid unnecessary CPU usage. + AhciZeroMemory((PCHAR)(srbExtension->CommandTable.PRDT), (srbExtension->Sgl->NumberOfElements * sizeof(AHCI_PRDT))); + } } break; @@ -1475,7 +1592,7 @@ AhciHwStartIo ( ) /* 1. Process Adapter request - 2. Bail out if it�s adapter request + 2. Bail out if it's adapter request 3. Validate Port Number, if not valid, bail out. 4. Process Device/Port request @@ -1488,7 +1605,7 @@ AhciHwStartIo ( BOOLEAN adapterRequest = FALSE; BOOLEAN processIO = FALSE; - //1 Work on Adapter requests + //1 Work on Adapter requests switch (function) { case SRB_FUNCTION_PNP: { PSRBEX_DATA_PNP pnpData = (PSRBEX_DATA_PNP)SrbGetSrbExDataByType((PSTORAGE_REQUEST_BLOCK)Srb, SrbExDataTypePnP); @@ -1509,6 +1626,21 @@ AhciHwStartIo ( } else { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; } + + StorPortEtwChannelEvent2(adapterExtension, + NULL, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwEventDiagnostic : StorportEtwEventOperational, + AhciEtwEventStartIO, + L"Processing PNP", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwLevelInformational : StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"PnPAction", + pnpData->PnPAction, + L"SrbStatus", + Srb->SrbStatus); + //complete all Adapter PnP request StorPortNotification(RequestComplete, AdapterExtension, Srb); } @@ -1563,21 +1695,62 @@ AhciHwStartIo ( // 2.1 All requests reach here should be for devices if ( !IsPortValid(adapterExtension, pathId) ) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + + StorPortEtwChannelEvent8(adapterExtension, + NULL, + StorportEtwEventOperational, + AhciEtwEventStartIO, + L"Port has no device", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"AdapterNumber", + adapterExtension->AdapterNumber, + L"SystemIoBusNumber", + adapterExtension->SystemIoBusNumber, + L"SlotNumber", + adapterExtension->SlotNumber, + L"AhciBaseAddress", + adapterExtension->AhciBaseAddress, + L"PathId", + pathId, + L"Function", + Srb->Function, + L"SrbStatus", + Srb->SrbStatus, + NULL, + 0); + StorPortNotification(RequestComplete, AdapterExtension, Srb); return TRUE; } - // 2.2 work on device requests + // 2.2 work on device requests + // Get channelExtension and storAddress to use for ETW Event logging + PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[pathId]; + PSTOR_ADDRESS storAddress = (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress); + switch (function) { case SRB_FUNCTION_RESET_BUS: // this one may come from class driver, not port driver. same as AhciHwResetBus case SRB_FUNCTION_RESET_DEVICE: case SRB_FUNCTION_RESET_LOGICAL_UNIT: { + // these reset requests target to Port AhciInterruptSpinlockAcquire(adapterExtension, pathId, &lockhandle); Srb->SrbStatus = AhciPortReset(adapterExtension->PortExtension[pathId], TRUE) ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR; StorPortNotification(RequestComplete, AdapterExtension, Srb); AhciInterruptSpinlockRelease(adapterExtension, pathId, &lockhandle); adapterExtension->PortExtension[pathId]->DeviceExtension[0].IoRecord.PortDriverResetCount++; + + AhciTelemetryLogResetErrorRecovery(adapterExtension->PortExtension[pathId], + (PSTOR_ADDRESS)&(adapterExtension->PortExtension[pathId]->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdResetDeviceRequest, + "AhciHwStartIo RESET Request", + 0, + "PortResetStatus", + Srb->SrbStatus + ); break; } @@ -1612,6 +1785,21 @@ AhciHwStartIo ( Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; StorPortNotification(RequestComplete, AdapterExtension, Srb); } + + StorPortEtwChannelEvent2(adapterExtension, + storAddress, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwEventDiagnostic : StorportEtwEventOperational, + AhciEtwEventStartIO, + L"Completed PnP request", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwLevelInformational : StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"SrbPnPFlags", + pnpData->SrbPnPFlags, + L"PnPAction", + pnpData->PnPAction); + break; } @@ -1623,20 +1811,38 @@ AhciHwStartIo ( BOOLEAN sendStandby = (IsDumpHiberMode(adapterExtension) || IsDumpCrashMode(adapterExtension)); deviceParameters->StateFlags.SystemPoweringDown = TRUE; + + USHORT *eventDescription; if (adapterExtension->PortExtension[pathId]->DeviceExtension->SupportedCommands.SetDateAndTime == 0x1) { IssueSetDateAndTimeCommand(adapterExtension->PortExtension[pathId], Srb, sendStandby); processIO = TRUE; + eventDescription = L"Shutdown set date and time"; } else if (sendStandby) { //in dump mode, this is the last Srb sent after SYNC CACHE, spin down the disk PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)Srb); srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_STANDBY_IMMEDIATE); processIO = TRUE; + eventDescription = L"Shutdown standby immediate"; } else { Srb->SrbStatus = SRB_STATUS_SUCCESS; + eventDescription = L"Shutdown"; StorPortNotification(RequestComplete, AdapterExtension, Srb); } + StorPortEtwEvent2(adapterExtension, + storAddress, + AhciEtwEventStartIO, + eventDescription, + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + Srb, + NULL, + 0, + NULL, + 0); + break; } @@ -1648,38 +1854,56 @@ AhciHwStartIo ( case SRB_FUNCTION_DUMP_POINTERS: { ULONG status = STOR_STATUS_SUCCESS; + ULONG size = 0; PAHCI_DUMP_CONTEXT dumpContext = NULL; PMINIPORT_DUMP_POINTERS dumpPointers = (PMINIPORT_DUMP_POINTERS)SrbGetDataBuffer(Srb); + USHORT *eventDescription = L"Completed dump pointers"; + if ( (dumpPointers == NULL) || (SrbGetDataTransferLength(Srb) < RTL_SIZEOF_THROUGH_FIELD(MINIPORT_DUMP_POINTERS, MiniportPrivateDumpData)) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + eventDescription = L"Invalid dump pointers request"; } else { + size = sizeof(AHCI_DUMP_CONTEXT); + // allocate pool and zero the content status = StorPortAllocatePool(AdapterExtension, - sizeof(AHCI_DUMP_CONTEXT), + size, AHCI_POOL_TAG, (PVOID*)&dumpContext); if ((status != STOR_STATUS_SUCCESS) || (dumpContext == NULL)) { // we cannot continue if cannot get memory for ChannelExtension. Srb->SrbStatus = SRB_STATUS_ERROR; + eventDescription = L"Allocate dump context failed"; } else { dumpPointers->Version = DUMP_MINIPORT_VERSION_1; dumpPointers->Size = sizeof(MINIPORT_DUMP_POINTERS); - dumpPointers->MiniportPrivateDumpData = (PVOID)dumpContext; + AhciZeroMemory((PCHAR)dumpContext, size); - AhciZeroMemory((PCHAR)dumpContext, sizeof(AHCI_DUMP_CONTEXT)); dumpContext->VendorID = adapterExtension->VendorID; dumpContext->DeviceID = adapterExtension->DeviceID; dumpContext->RevisionID = adapterExtension->RevisionID; dumpContext->AhciBaseAddress = adapterExtension->AhciBaseAddress; dumpContext->LogFlags = adapterExtension->LogFlags; dumpContext->AdapterRegistryFlags = adapterExtension->RegistryFlags; - dumpContext->DumpPortNumber = adapterExtension->PortExtension[pathId]->PortNumber; - dumpContext->PortRegistryFlags = adapterExtension->PortExtension[pathId]->RegistryFlags; + + dumpContext->PortCount = 1; + + dumpContext->Ports[0].PortNumber = adapterExtension->PortExtension[pathId]->PortNumber; + dumpContext->Ports[0].PortRegistryFlags = adapterExtension->PortExtension[pathId]->RegistryFlags; + + // + // Preserve Hybrid Disk Information. + // + if (IsDeviceHybridInfoSupported(adapterExtension->PortExtension[pathId])) { + StorPortMoveMemory((PVOID)&dumpContext->Ports[0].HybridInfo, + (PVOID)&adapterExtension->PortExtension[pathId]->DeviceExtension->HybridInfo, + sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); + } // // Fill telemetry collection context - shared with diskdump.sys stack @@ -1698,18 +1922,26 @@ AhciHwStartIo ( dumpContext->PrivateGPLogPageAddress = IDE_GP_LOG_CURRENT_DEVICE_INTERNAL_STATUS; } - // - // Preserve Hybrid Disk Information. - // - if (IsDeviceHybridInfoSupported(adapterExtension->PortExtension[pathId])) { - StorPortMoveMemory((PVOID)&dumpContext->HybridInfo, - (PVOID)&adapterExtension->PortExtension[pathId]->DeviceExtension->HybridInfo, - sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); - } + dumpPointers->MiniportPrivateDumpData = (PVOID)dumpContext; Srb->SrbStatus = SRB_STATUS_SUCCESS; } } + + StorPortEtwChannelEvent2(adapterExtension, + storAddress, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwEventDiagnostic : StorportEtwEventOperational, + AhciEtwEventStartIO, + eventDescription, + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwLevelInformational : StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"SrbStatus", + Srb->SrbStatus, + L"TransferLen", + SrbGetDataTransferLength(Srb)); + StorPortNotification(RequestComplete, AdapterExtension, Srb); break; } @@ -1725,6 +1957,21 @@ AhciHwStartIo ( status = StorPortFreePool(AdapterExtension, dumpPointers->MiniportPrivateDumpData); Srb->SrbStatus = (status == STOR_STATUS_SUCCESS) ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR; } + + StorPortEtwChannelEvent2(adapterExtension, + storAddress, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwEventDiagnostic : StorportEtwEventOperational, + AhciEtwEventStartIO, + L"Completed free dump pointers", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwLevelInformational : StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"SrbStatus", + Srb->SrbStatus, + L"TransferLen", + SrbGetDataTransferLength(Srb)); + StorPortNotification(RequestComplete, AdapterExtension, Srb); break; } @@ -1732,6 +1979,20 @@ AhciHwStartIo ( default: { // for unsupported SRB: complete with status: SRB_STATUS_INVALID_REQUEST Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + + StorPortEtwEvent2(adapterExtension, + storAddress, + AhciEtwEventStartIO, + L"Invalid request", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + Srb, + L"Function", + function, + NULL, + 0); + StorPortNotification(RequestComplete, AdapterExtension, Srb); break; } @@ -1749,7 +2010,6 @@ AhciHwStartIo ( AhciProcessIo(adapterExtension->PortExtension[pathId], (PSTORAGE_REQUEST_BLOCK)Srb, FALSE); ActivateQueue(adapterExtension->PortExtension[pathId], FALSE); - } return TRUE; @@ -1769,7 +2029,7 @@ Routine Description: This routine must attempt one clear the interrupt on the HBA before it returns TRUE. NOTE: - The following StorPort routines shall not be called from the AhciHwInterrupt routine � StorPortCompleteAllRequests and StorPortDeviceBusy. + The following StorPort routines shall not be called from the AhciHwInterrupt routine - StorPortCompleteAllRequests and StorPortDeviceBusy. The miniport could however request for a worker routine and make the calls in the worker routine. Called by: @@ -1823,7 +2083,6 @@ It performs: "If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2)." AHCI 1.1 Section 6.2.2.1 Non-Queued Error Recovery (this may take a while, better queue a DPC) Complete further processing in the worker routine and enable interrupts on the channel - 6.1 Partial to Slumber auto transit Affected Variables/Registers: @@ -1872,12 +2131,36 @@ Return Values: //call the correct error handling based on current hw queue workload type sact = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT); - - if ((sact == MAXULONG) && IsAdapterRemoved(ChannelExtension)) { + if ((sact == MAXULONG) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { // controller has been surprise removed + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (ChannelExtension != NULL) ? (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress) : (NULL), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"Adapter surprise removed", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelWarning, + StorportEtwEventOpcodeInfo, + NULL, + L"pxis", + pxis.AsUlong, + L"ssts", + ssts.AsUlong, + L"serr", + serr.AsUlong, + L"cmd", + cmd.AsUlong, + L"sact", + sact, + NULL, + 0, + NULL, + 0, + NULL, + 0); return; } else if (sact != 0) { - //5.1 NCQ, Handle error processing + //5.1 NCQ, Handle error processing ChannelExtension->StateFlags.CallAhciNcqErrorRecovery = 1; //Give NCQ one chance @@ -1903,7 +2186,32 @@ Return Values: if (pxis.DMPS || pxis.PCS) { cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); - if ((cmd.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension)) { + if ((cmd.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (ChannelExtension != NULL) ? (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress) : (NULL), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"Adapter surprise removed", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelWarning, + StorportEtwEventOpcodeInfo, + NULL, + L"pxis", + pxis.AsUlong, + L"ssts", + ssts.AsUlong, + L"serr", + serr.AsUlong, + L"cmd", + cmd.AsUlong, + L"sact", + sact, + NULL, + 0, + NULL, + 0, + NULL, + 0); return; } @@ -1937,13 +2245,13 @@ Return Values: // PhyRdy Change Status if (pxis.PRCS) { //Hot plug removals are detected via the PxIS.PRCS bit that directly reflects the PxSERR.DIAG.N bit. - //Note that PxSERR.DIAG.N is also set to �1� on insertions and during interface power management entry/exit. + //Note that PxSERR.DIAG.N is also set to '1' on insertions and during interface power management entry/exit. serrMask.DIAG.N = 1; StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SERR.AsUlong, serrMask.AsUlong); ssts.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong); - if ((ssts.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension)) { + if ((ssts.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { // controller has been surprise removed return; } @@ -2053,7 +2361,7 @@ Return Values: if (pxis.SDBS) { pxisMask.SDBS = 1; } - // Descriptor Processed (A PRD with the �I� bit set has transferred all of its data) + // Descriptor Processed (A PRD with the 'I' bit set has transferred all of its data) if (pxis.DPS) { pxisMask.DPS = 1; } @@ -2066,8 +2374,33 @@ Return Values: // preserve taskfile for using in command completion process ChannelExtension->TaskFileData.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->TFD.AsUlong); - if ((ChannelExtension->TaskFileData.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension)) { + if ((ChannelExtension->TaskFileData.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { // controller has been surprise removed + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (ChannelExtension != NULL) ? (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress) : (NULL), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"Adapter surprise removed", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelWarning, + StorportEtwEventOpcodeInfo, + NULL, + L"pxis", + pxis.AsUlong, + L"ssts", + ssts.AsUlong, + L"serr", + serr.AsUlong, + L"cmd", + cmd.AsUlong, + L"sact", + sact, + L"StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + NULL, + 0, + NULL, + 0); return; } @@ -2086,8 +2419,33 @@ Return Values: ci = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI); sact = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT); - if (((ci == MAXULONG) || (sact == MAXULONG)) && IsAdapterRemoved(ChannelExtension)) { + if (((ci == MAXULONG) || (sact == MAXULONG)) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { // controller has been surprise removed + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (ChannelExtension != NULL) ? (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress) : (NULL), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"Adapter removed: complete cmd", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelWarning, + StorportEtwEventOpcodeInfo, + NULL, + L"pxis", + pxis.AsUlong, + L"ssts", + ssts.AsUlong, + L"serr", + serr.AsUlong, + L"cmd", + cmd.AsUlong, + L"is", + is, + L"ci", + ci, + L"sact", + sact, + NULL, + 0); return; } @@ -2111,20 +2469,6 @@ Return Values: RecordInterruptHistory(ChannelExtension, pxis.AsUlong, ssts.AsUlong, serr.AsUlong, ci, sact, 0x20010005); //AhciHwInterrupt No IO completed } - //6.1 Partial to Slumber auto transit - if ((outstanding == 0) && - PartialToSlumberTransitionIsAllowed(ChannelExtension, &cmd)) { - - ULONG status; - - // convert interval value from ms to us. allow 20ms of coalescing with other timers - status = StorPortRequestTimer(ChannelExtension->AdapterExtension, ChannelExtension->WorkerTimer, AhciAutoPartialToSlumber, ChannelExtension, ChannelExtension->AutoPartialToSlumberInterval * 1000, 20000); - - if (status == STOR_STATUS_SUCCESS) { - StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Scheduled \n", ChannelExtension->PortNumber); - } - } - if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000005);//Exit AhciInterruptHandler } @@ -2356,7 +2700,8 @@ Return Value: if (IsPortValid(adapterExtension, storAddrBtl8->Path)) { PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; - StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - %s\n", storAddrBtl8->Path, unitControlPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3"); + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - %s\n", + adapterExtension->SystemIoBusNumber, storAddrBtl8->Path, unitControlPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3"); if (unitControlPower->PowerState == StorPowerDeviceD0) { AhciPortPowerUp(channelExtension); @@ -2379,16 +2724,15 @@ Return Value: } case ScsiUnitPoFxPowerInfo: { - PSTOR_POFX_UNIT_POWER_INFO unitPowerInfo = (PSTOR_POFX_UNIT_POWER_INFO)Parameters; - PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)unitPowerInfo->Header.Address; - BOOLEAN d3ColdEnabled = FALSE; + PSTOR_POFX_UNIT_POWER_INFO unitPowerInfo = (PSTOR_POFX_UNIT_POWER_INFO)Parameters; + PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)unitPowerInfo->Header.Address; + BOOLEAN d3ColdEnabled = FALSE; if (IsPortValid(adapterExtension, storAddrBtl8->Path)) { - ULONG storStatus = STOR_STATUS_SUCCESS; - ULONG bufferLength = STOR_POFX_DEVICE_V3_SIZE + STOR_POFX_COMPONENT_V2_SIZE + STOR_POFX_COMPONENT_IDLE_STATE_SIZE; + ULONG storStatus = STOR_STATUS_SUCCESS; + ULONG bufferLength = STOR_POFX_DEVICE_V3_SIZE + STOR_POFX_COMPONENT_V2_SIZE + STOR_POFX_COMPONENT_IDLE_STATE_SIZE; PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; - BOOLEAN reportF1State = FALSE; - + BOOLEAN reportF1State = FALSE; // // IdlePowerEnabled == TRUE indicates this unit is being @@ -2417,7 +2761,6 @@ Return Value: bufferLength, AHCI_POOL_TAG, (PVOID*)&channelExtension->PoFxDevice); - } if (storStatus == STOR_STATUS_SUCCESS) { @@ -2436,10 +2779,34 @@ Return Value: channelExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_NO_DUMP_ACTIVE; // - // If this is a drive with rotational media then enable adaptive D3 idle timeout. + // Disable idle debouncing if it's possible for us to + // initiate a Slumber transition. + // We set the Slumber timer when we get the Idle + // condition callback and cancel it when we get the + // Active condition callback. The slumber timer is + // typically set to 100ms and idle debouncing can add + // a bias of up to 1s. Significantly delaying Slumber + // entry can have a negative impact on power so we + // disable idle debouncing. + // CAP.SSC indicates if the HBA supports Slumber. + // CAP.SALP indicates if the HBA can automatically + // transition to Partial when idle, which is needed for + // us to subsequently initiate a Slumber transition. + // + if ((channelExtension->AdapterExtension->CAP.SSC) && + (channelExtension->AdapterExtension->CAP.SALP) && + IsDeviceSupportsHIPM(channelExtension->DeviceExtension[0].IdentifyDeviceData)) { + channelExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_NO_IDLE_DEBOUNCE; + } + + // + // If this is a drive with rotational media then enable adaptive D3 idle timeout, else if + // this is a SSD on an AOAC system, set the idle timeout flag and provide a minimum unit idle timeout. // if (DeviceIncursSeekPenalty(channelExtension)) { channelExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_ADAPTIVE_D3_IDLE_TIMEOUT; + } else if (IsReceivingSystemPowerHints(adapterExtension) && DeviceIncursNoSeekPenalty(channelExtension)) { + channelExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_IDLE_TIMEOUT; } component->Version = STOR_POFX_COMPONENT_VERSION_V2; @@ -2457,7 +2824,6 @@ Return Value: component->FStates[0].ResidencyRequirement = 0; component->FStates[0].NominalPower = STOR_POFX_UNKNOWN_POWER; - // registry runtime power management for Unit storStatus = StorPortInitializePoFxPower(AdapterExtension, (PSTOR_ADDRESS)storAddrBtl8, @@ -2504,19 +2870,30 @@ Return Value: case ScsiUnitPoFxPowerActive: { PSTOR_POFX_ACTIVE_CONTEXT activeContext = (PSTOR_POFX_ACTIVE_CONTEXT)Parameters; - PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)activeContext->Header.Address; + PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)activeContext->Header.Address; - if ( IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(adapterExtension->PortExtension[storAddrBtl8->Path]) ) { + if (IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(adapterExtension->PortExtension[storAddrBtl8->Path])) { PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; channelExtension->StateFlags.PoFxActive = activeContext->Active ? 1 : 0; - StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - %s\n", channelExtension->PortNumber, activeContext->Active ? "ACTIVE" : "IDLE"); + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - %s\n", + adapterExtension->SystemIoBusNumber, channelExtension->PortNumber, activeContext->Active ? "ACTIVE" : "IDLE"); if (activeContext->Active) { - ULONG busChangePending; - ULONG restorePreservedSettingsPending; + ULONG busChangePending; + ULONG restorePreservedSettingsPending; + // + // Cancel the Slumber timer. + // + StorPortRequestTimer(channelExtension->AdapterExtension, + channelExtension->WorkerTimer, + AhciAutoPartialToSlumber, + channelExtension, + 0, 0); + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - Transit into Slumber from Partial - Canceled\n", + adapterExtension->SystemIoBusNumber, channelExtension->PortNumber); busChangePending = InterlockedBitTestAndReset((LONG*)&channelExtension->PoFxPendingWork, 1); //BusChange is at bit 1 restorePreservedSettingsPending = InterlockedBitTestAndReset((LONG*)&channelExtension->PoFxPendingWork, 0); //RestorePreservedSettings is at bit 0 @@ -2529,6 +2906,7 @@ Return Value: if (restorePreservedSettingsPending == 1) { // continue on processing RestorePreservedSettings + RecordExecutionHistory(channelExtension, 0x10050041); // RestorePreservedSettings continue in Unit Active // Start the first command IssuePreservedSettingCommands(channelExtension, NULL); @@ -2538,10 +2916,30 @@ Return Value: PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb); if (srbExtension->AtaFunction != 0) { AhciProcessIo(channelExtension, (PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb, FALSE); + ActivateQueue(channelExtension, FALSE); } } } } else { + // + // Attempt to start the Slumber timer now that we're idle. + // Use a tolerable delay of 20ms to allow coalescing with + // other timers. + // + if (PartialToSlumberTransitionIsAllowed(channelExtension, NULL)) { + + status = StorPortRequestTimer(channelExtension->AdapterExtension, + channelExtension->WorkerTimer, + AhciAutoPartialToSlumber, + channelExtension, + channelExtension->AutoPartialToSlumberInterval * 1000, 20000); + + if (status == STOR_STATUS_SUCCESS) { + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - Transit into Slumber from Partial - Scheduled\n", + adapterExtension->SystemIoBusNumber, channelExtension->PortNumber); + } + } + } } else { status = ScsiUnitControlUnsuccessful; @@ -2552,14 +2950,13 @@ Return Value: case ScsiUnitPoFxPowerSetFState: { PSTOR_POFX_FSTATE_CONTEXT fStateContext = (PSTOR_POFX_FSTATE_CONTEXT)Parameters; - PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)fStateContext->Header.Address; - PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; + PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)fStateContext->Header.Address; + PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; - if ( IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(channelExtension) ) { + if (IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(channelExtension)) { - StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transition from F%u to F%u\n", channelExtension->PortNumber, - channelExtension->PoFxFState, - fStateContext->FState); + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - Transition from F%u to F%u\n", + adapterExtension->SystemIoBusNumber, channelExtension->PortNumber, channelExtension->PoFxFState, fStateContext->FState); channelExtension->PoFxFState = (UCHAR)fStateContext->FState; @@ -2607,6 +3004,16 @@ Return Value: AhciPortFailAllIos(channelExtension, SRB_STATUS_NO_DEVICE, TRUE); AhciInterruptSpinlockRelease(adapterExtension, channelExtension->PortNumber, &lockhandle); + + ++(channelExtension->TotalCountSurpriseRemove); + AhciTelemetryLogResetErrorRecovery(channelExtension, + (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdSurpriseRemove, + "ScsiUnitSurpriseRemoval", + AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING, + NULL, + 0 + ); } break; @@ -2629,4 +3036,3 @@ Return Value: #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/entrypts.h b/storage/miniports/storahci/src/entrypts.h index 56adcb0e..df1aa9ea 100644 --- a/storage/miniports/storahci/src/entrypts.h +++ b/storage/miniports/storahci/src/entrypts.h @@ -23,6 +23,7 @@ Authors: #pragma warning(disable:4214) // bit field types other than int #pragma warning(disable:4201) // nameless struct/union +#include "data.h" #define AHCI_POOL_TAG 'ichA' // "Ahci" - StorAHCI miniport driver @@ -34,10 +35,10 @@ Authors: #define AHCI_MAX_NCQ_REQUEST_COUNT 32 #define KB (1024) -#define AHCI_MAX_TRANSFER_LENGTH (128 * KB) +#define AHCI_MAX_TRANSFER_LENGTH_DEFAULT (128 * KB) +#define AHCI_MAX_TRANSFER_LENGTH (1024 * KB) #define MAX_SETTINGS_PRESERVED 32 #define MAX_CRB_LOG_INDEX 64 -#define MAX_EXECUTION_HISTORY_ENTRY_COUNT 100 #define INQUIRYDATABUFFERSIZE 36 @@ -71,22 +72,18 @@ Authors: // registry flags apply to port or device typedef struct _CHANNEL_REGISTRY_FLAGS { - ULONG Reserved : 20; - } CHANNEL_REGISTRY_FLAGS, *PCHANNEL_REGISTRY_FLAGS; // registry flags apply to the whole adapter typedef struct _ADAPTER_REGISTRY_FLAGS { - ULONG Reserved2 : 16; - + ULONG Reserved2 : 13; } ADAPTER_REGISTRY_FLAGS, *PADAPTER_REGISTRY_FLAGS; typedef struct _CHANNEL_STATE_FLAGS { - ULONG Initialized : 1; ULONG NoMoreIO : 1; ULONG QueuePaused : 1; @@ -112,31 +109,14 @@ typedef struct _CHANNEL_STATE_FLAGS { ULONG NcqErrorRecoveryInProcess : 1; ULONG PuisEnabled : 1; - ULONG Reserved0 : 12; + ULONG IdentifyDeviceSuccess : 1; + ULONG NeedQDR : 1; + ULONG PowerUpInitializationInProgress : 1; //Note: this field indicates that init/preserved settings commands(invoked by power up) are not completed yet. + ULONG Reserved0 : 9; ULONG Reserved1; } CHANNEL_STATE_FLAGS, *PCHANNEL_STATE_FLAGS; -typedef struct _ATA_IO_RECORD { - - ULONG SuccessCount; - - ULONG CrcErrorCount; - ULONG MediaErrorCount; - ULONG EndofMediaCount; - ULONG IllegalCommandCount; - ULONG AbortedCommandCount; - ULONG DeviceFaultCount; - - ULONG OtherErrorCount; - - ULONG NcqReadLogErrorCount; // used to record the READ LOG EXT command error count when used for NCQ Error Recovery. - - ULONG PortDriverResetCount; - ULONG TotalResetCount; - -} ATA_IO_RECORD, *PATA_IO_RECORD; - // // Data structure to retrieve a Log Page // @@ -234,6 +214,7 @@ typedef struct _AHCI_DEVICE_EXTENSION { ATA_SUPPORTED_GPL_PAGES SupportedGPLPages; ATA_COMMAND_SUPPORTED SupportedCommands; + GP_LOG_HYBRID_INFORMATION_HEADER HybridInfo; LONG HybridCachingMediumEnableRefs; @@ -242,8 +223,21 @@ typedef struct _AHCI_DEVICE_EXTENSION { PUSHORT ReadLogExtPageData; STOR_PHYSICAL_ADDRESS ReadLogExtPageDataPhysicalAddress; + BOOLEAN UpdateCachedLogPageInfo; + UCHAR Reserved[3]; } AHCI_DEVICE_EXTENSION, *PAHCI_DEVICE_EXTENSION; +typedef struct _AHCI_DEVICE_LOG_PAGE_INFO { + + ATA_GPL_PAGES_TO_QUERY QueryLogPages; + + ATA_SUPPORTED_GPL_PAGES SupportedGPLPages; + ATA_COMMAND_SUPPORTED SupportedCommands; + + DOWNLOAD_MICROCODE_CAPABILITIES FirmwareUpdate; + +} AHCI_DEVICE_LOG_PAGE_INFO, *PAHCI_DEVICE_LOG_PAGE_INFO; + typedef struct _COMMAND_HISTORY { union { //0x10 bytes ATA_TASK_FILE InitialTaskFile; @@ -258,36 +252,6 @@ typedef struct _COMMAND_HISTORY { ULONG SrbStatus; } COMMAND_HISTORY, *PCOMMAND_HISTORY; -typedef struct _SLOT_MANAGER { - ULONG HighPriorityAttribute; - - ULONG NCQueueSlice; - ULONG NormalQueueSlice; - ULONG SingleIoSlice; - - ULONG CommandsIssued; - ULONG CommandsToComplete; - - // - // These issued slices are used to determine the type of command - // being programmed to adapter. - // They are used instead of reading PxCI and PxSACT. - // - ULONG NCQueueSliceIssued; - ULONG NormalQueueSliceIssued; - ULONG SingleIoSliceIssued; - - ULONG Reserved; - -} SLOT_MANAGER, *PSLOT_MANAGER; - -typedef struct _EXECUTION_HISTORY { - ULONG Function; - ULONG IS; - SLOT_MANAGER SlotManager; //SLOT_MANAGER from _AHCI_CHANNEL_EXTENSION - ULONG Px[0x10]; //Px registers value, end to AHCI_SNOTIFICATION -- SNTF -} EXECUTION_HISTORY, *PEXECUTION_HISTORY; - typedef struct _SLOT_STATE_FLAGS { UCHAR FUA :1; UCHAR Reserved :7; @@ -356,9 +320,13 @@ typedef struct _LOCAL_SCATTER_GATHER_LIST { ULONG NumberOfElements; ULONG_PTR Reserved; _Field_size_(NumberOfElements) - STOR_SCATTER_GATHER_ELEMENT List[33]; + STOR_SCATTER_GATHER_ELEMENT List[257]; } LOCAL_SCATTER_GATHER_LIST, *PLOCAL_SCATTER_GATHER_LIST; +// +// Note: When adding new members to AHCI SRB extension, make sure the uncached extension allocation +// is successful in dump mode because there is MAX size limitation. +// typedef struct _AHCI_SRB_EXTENSION { AHCI_COMMAND_TABLE CommandTable; // this field MUST to be the first one as it's asked to be 128 aligned USHORT AtaFunction; // if this field is 0, it means the command does not need to be sent to device @@ -371,6 +339,7 @@ typedef struct _AHCI_SRB_EXTENSION { AHCI_H2D_REGISTER_FIS Cfis; // info related to Host to Device FIS (0x27) }; PVOID DataBuffer; // go with Cdb field when needed. + STOR_PHYSICAL_ADDRESS DataBufferPhysicalAddress; // Physical address of DataBuffer. ULONG DataTransferLength; // go with Cdb field when needed. PLOCAL_SCATTER_GATHER_LIST Sgl; // pointer to the local or port provided SGL LOCAL_SCATTER_GATHER_LIST LocalSgl; // local SGL @@ -381,6 +350,7 @@ typedef struct _AHCI_SRB_EXTENSION { ULONGLONG StartTime; PVOID ResultBuffer; // for requests marked with ATA_FLAGS_RETURN_RESULTS + STOR_PHYSICAL_ADDRESS ResultBufferPhysicalAddress; // Physical address of ResultBuffer. ULONG ResultBufferLength; } AHCI_SRB_EXTENSION, *PAHCI_SRB_EXTENSION; @@ -411,10 +381,16 @@ typedef struct _STORAHCI_QUEUE { ULONG DeepestDepth; ULONG DepthHistoryIndex; ULONG DepthHistory[100]; + LARGE_INTEGER LastTimeStampAddQueue; + LARGE_INTEGER LastTimeStampRemoveQueue; } STORAHCI_QUEUE, *PSTORAHCI_QUEUE; typedef struct _AHCI_ADAPTER_EXTENSION AHCI_ADAPTER_EXTENSION, *PAHCI_ADAPTER_EXTENSION; +// +// Note: When adding new members to AHCI channel extension, make sure the uncached extension allocation +// is successful in dump mode because there is MAX size limitation. +// typedef struct _AHCI_CHANNEL_EXTENSION { //Adapter Characteristics PAHCI_ADAPTER_EXTENSION AdapterExtension; @@ -458,6 +434,7 @@ typedef struct _AHCI_CHANNEL_EXTENSION { } AutoPartialToSlumberDbgStats; + struct { ULONG RestorePreservedSettings :1; //NOTE: this field is accessed in InterlockedBitTestAndReset, bit position (currently: 0) is used there. ULONG BusChange :1; //NOTE: this field is accessed in InterlockedBitTestAndReset, bit position (currently: 1) is used there. @@ -498,8 +475,29 @@ typedef struct _AHCI_CHANNEL_EXTENSION { //Logging UCHAR CommandHistoryNextAvailableIndex; COMMAND_HISTORY CommandHistory[64]; + UCHAR ExecutionHistoryNextAvailableIndex; - EXECUTION_HISTORY ExecutionHistory[MAX_EXECUTION_HISTORY_ENTRY_COUNT]; + PEXECUTION_HISTORY ExecutionHistory; // For non-dump mode, contains MAX_EXECUTION_HISTORY_ENTRY_COUNT elements. + +//Statistics counters for telemetry event. + ULONG TotalCountPortReset; + ULONG TotalCountRunningStartFailed; + ULONG TotalCountPortErrorRecovery; + ULONG TotalCountNonQueuedErrorRecovery; + ULONG TotalCountNCQError; + ULONG TotalCountNCQErrorRecoveryComplete; + ULONG TotalCountSurpriseRemove; + ULONG TotalCountPowerSettingNotification; + ULONGLONG LastLogResetErrorRecoveryTime; + ULONG TotalCountPowerUp; + ULONG TotalCountPowerDown; + ULONG TotalPortStartTime; + ULONG TotalCountBusChange; + ULONG DeviceFailureThrottleFlag; + +//Statistics for tracking DPC. + LARGE_INTEGER LastTimeStampDpcStart; + LARGE_INTEGER LastTimeStampDpcCompletion; } AHCI_CHANNEL_EXTENSION, *PAHCI_CHANNEL_EXTENSION; @@ -512,13 +510,15 @@ typedef struct _ADAPTER_STATE_FLAGS { ULONG SupportsAcpiDSM : 1; // indicates if the system has _DSM method implemented to control port/device power. when the value is 1, the _DSM method at least supports powering on all connected devices. ULONG Removed : 1; + ULONG InterruptMessagePerPort : 1; ULONG D3ColdSupported : 1; ULONG D3ColdEnabled : 1; ULONG UseAdapterF1InsteadOfD3 : 1; + ULONG Removable : 1; - ULONG Reserved : 21; + ULONG Reserved : 20; } ADAPTER_STATE_FLAGS, *PADAPTER_STATE_FLAGS; @@ -595,6 +595,19 @@ typedef struct _AHCI_ADAPTER_EXTENSION { } AHCI_ADAPTER_EXTENSION, *PAHCI_ADAPTER_EXTENSION; // information that will be transferred to dump/hibernate environment + +typedef struct _AHCI_DUMP_PORT_CONTEXT { + + ULONG PortNumber; + CHANNEL_REGISTRY_FLAGS PortRegistryFlags; + + // + // Hybrid Disk Information Log + // + GP_LOG_HYBRID_INFORMATION_HEADER HybridInfo; + +} AHCI_DUMP_PORT_CONTEXT, *PAHCI_DUMP_PORT_CONTEXT; + typedef struct _AHCI_DUMP_CONTEXT { // adapter information USHORT VendorID; @@ -607,10 +620,6 @@ typedef struct _AHCI_DUMP_CONTEXT { ADAPTER_LOG_FLAGS LogFlags; // internal log levels ADAPTER_REGISTRY_FLAGS AdapterRegistryFlags; - // port information - ULONG DumpPortNumber; - CHANNEL_REGISTRY_FLAGS PortRegistryFlags; - // // Device telemetry descriptors for the boot device // @@ -618,12 +627,25 @@ typedef struct _AHCI_DUMP_CONTEXT { ULONG PrivateGPLogPageAddress; // - // Hybrid Disk Information Log + // Port information of dump devices // - GP_LOG_HYBRID_INFORMATION_HEADER HybridInfo; + ULONG PortCount; + AHCI_DUMP_PORT_CONTEXT Ports[1]; } AHCI_DUMP_CONTEXT, *PAHCI_DUMP_CONTEXT; +typedef struct _ATA_COMMAND_ERROR_LOG { + ULONG Version; + ULONG Size; + CHANNEL_STATE_FLAGS StateFlags; + CHANNEL_START_STATE StartState; + STORAGE_REQUEST_BLOCK Srb; + CDB Cdb; // To differentiate SRBs if the struct is reused + UCHAR Command; // ATA taksfile/AHCI FIS payload + UCHAR AtaStatus; + UCHAR AtaError; +} ATA_COMMAND_ERROR, *PATA_COMMAND_ERROR; + // Storport miniport driver entry routines, with prefix: "AhciHw" sp_DRIVER_INITIALIZE DriverEntry; @@ -669,8 +691,6 @@ AhciDeviceStart ( extern ULONG AhciPublicGPLogTableAddresses[TC_PUBLIC_DEVICEDUMP_CONTENT_GPLOG_MAX]; extern ULONG AhciGPLogPageIntoPrivate; - - #if _MSC_VER >= 1200 #pragma warning(pop) #else @@ -678,4 +698,3 @@ extern ULONG AhciGPLogPageIntoPrivate; #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/generic.h b/storage/miniports/storahci/src/generic.h index 4595820b..1deedeaf 100644 --- a/storage/miniports/storahci/src/generic.h +++ b/storage/miniports/storahci/src/generic.h @@ -28,7 +28,6 @@ Revision History: #include "srbhelper.h" - // storahci header files #include "common.h" #include "ahci.h" @@ -168,4 +167,3 @@ typedef struct _SENDCMDOUTPARAMS { #define DISABLE_SMART 0xD9 #define RETURN_SMART_STATUS 0xDA #define ENABLE_DISABLE_AUTO_OFFLINE 0xDB - diff --git a/storage/miniports/storahci/src/hbastat.c b/storage/miniports/storahci/src/hbastat.c index fc518675..21c502d5 100644 --- a/storage/miniports/storahci/src/hbastat.c +++ b/storage/miniports/storahci/src/hbastat.c @@ -1,4 +1,4 @@ -/*++ +/*++ Copyright (C) Microsoft Corporation, 2009 @@ -12,7 +12,7 @@ Abstract: Notes: -Revision History: +Revision History: --*/ @@ -92,11 +92,11 @@ Return value: } //10.4.3 HBA Reset - //If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the problem, software may reset the entire HBA by setting GHC.HR to �1�. When software sets the GHC.HR bit to �1�, the HBA shall perform an internal reset action. + //If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR bit to ‘1’, the HBA shall perform an internal reset action. ghc.HR = 1; StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong); - //The bit shall be cleared to �0� by the HBA when the reset is complete. A software write of �0� to GHC.HR shall have no effect. To perform the HBA reset, software sets GHC.HR to �1� and may poll until this bit is read to be �0�, at which point software knows that the HBA reset has completed. + //The bit shall be cleared to ‘0’ by the HBA when the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset, software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that the HBA reset has completed. ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong); //5.2.2.1 H:Init //5.2.2.2 H:WaitForAhciEnable @@ -105,13 +105,13 @@ Return value: ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong); } - //If the HBA has not cleared GHC.HR to �0� within 1 second of software setting GHC.HR to �1�, the HBA is in a hung or locked state. + //If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in a hung or locked state. if(i == 50) { AdapterExtension->ErrorFlags = (1 << 29); return FALSE; } - //When GHC.HR is set to �1�, GHC.AE, GHC.IE, the IS register, and all port register fields (except PxFB/PxFBU/PxCLB/PxCLBU) that are not HwInit in the HBA�s register memory space are reset. The HBA�s configuration space and all other global registers/bits are not affected by setting GHC.HR to �1�. Any HwInit bits in the port specific registers are not affected by setting GHC.HR to �1�. The port specific registers PxFB, PxFBU, PxCLB, and PxCLBU are not affected by setting GHC.HR to �1�. If the HBA supports staggered spin-up, the PxCMD.SUD bit will be reset to �0�; software is responsible for setting the PxCMD.SUD and PxSCTL.DET fields appropriately such that communication can be established on the Serial ATA link. If the HBA does not support staggered spin-up, the HBA reset shall cause a COMRESET to be sent on the port + //When GHC.HR is set to ‘1’, GHC.AE, GHC.IE, the IS register, and all port register fields (except PxFB/PxFBU/PxCLB/PxCLBU) that are not HwInit in the HBA’s register memory space are reset. The HBA’s configuration space and all other global registers/bits are not affected by setting GHC.HR to ‘1’. Any HwInit bits in the port specific registers are not affected by setting GHC.HR to ‘1’. The port specific registers PxFB, PxFBU, PxCLB, and PxCLBU are not affected by setting GHC.HR to ‘1’. If the HBA supports staggered spin-up, the PxCMD.SUD bit will be reset to ‘0’; software is responsible for setting the PxCMD.SUD and PxSCTL.DET fields appropriately such that communication can be established on the Serial ATA link. If the HBA does not support staggered spin-up, the HBA reset shall cause a COMRESET to be sent on the port return TRUE; } @@ -124,10 +124,10 @@ AhciCOMRESET( ) /* PHY Reset:COMRESET - SCTL.DET Controls the HBA�s device detection and interface initialization. + SCTL.DET Controls the HBA’s device detection and interface initialization. DET=1 Performs interface communication initialization sequence to establish communication. This is functionally equivalent to a hard reset and results in the interface being reset and communications reinitialized. While this field is 1h, COMRESET is transmitted on the interface. Software should leave the DET field set to 1h for a minimum of 1 millisecond to ensure that a COMRESET is sent on the interface. - since we are in 5.3.2.3 P:NotRunning and PxCMD.SUD = �0� does this still take us to P:StartComm? + since we are in 5.3.2.3 P:NotRunning and PxCMD.SUD = ‘0’ does this still take us to P:StartComm? Called By: AhciPortReset,AhciNonQueuedErrorRecovery,P_Running_WaitWhileDET1,AhciHwControlIdeStart It assumes: @@ -191,7 +191,7 @@ Return Values: StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &Px->SCTL.AsUlong, sctl.AsUlong); //2.2 Wait for DET to be set - // AHCI 10.4.2 After clearing PxSCTL.DET to 0h, software should wait for communication to be re-established as indicated by bit 0 of PxSSTS.DET being set to �1�. + // AHCI 10.4.2 After clearing PxSCTL.DET to 0h, software should wait for communication to be re-established as indicated by bit 0 of PxSSTS.DET being set to ‘1’. // typically, it will be done in 10ms. max wait 30ms to be safe StorPortStallExecution(50); ssts.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &Px->SSTS.AsUlong); @@ -279,8 +279,8 @@ Return value: //1.1 Clear CMD.ST cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong); //AHCI 10.3.2 on FRE it says: - //Software shall not clear this [FRE] bit while PxCMD.ST remains set to �1�." - //System software places a port into the idle state by clearing PxCMD.ST and waiting for PxCMD.CR to return �0� when read. + //Software shall not clear this [FRE] bit while PxCMD.ST remains set to ‘1’." + //System software places a port into the idle state by clearing PxCMD.ST and waiting for PxCMD.CR to return ‘0’ when read. cmd.ST = 0; StorPortWriteRegisterUlong(adapterExtension, &Px->CMD.AsUlong, cmd.AsUlong); @@ -304,7 +304,7 @@ Return value: tfd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->TFD.AsUlong); if (tfd.STS.BSY) { - // AHCI 3.3.7 Command List Override (CLO): Setting this bit to �1� causes PxTFD.STS.BSY and PxTFD.STS.DRQ to be cleared to �0�. + // AHCI 3.3.7 Command List Override (CLO): Setting this bit to ‘1’ causes PxTFD.STS.BSY and PxTFD.STS.DRQ to be cleared to ‘0’. // This allows a software reset to be transmitted to the device regardless of whether the BSY and DRQ bits are still set in the PxTFD.STS register. // Do this to make sure the port can stop @@ -323,9 +323,9 @@ Return value: StorPortStallExecution(5000); //5 milliseconds } - //AHCI 10.4.2 If PxCMD.CR or PxCMD.FR do not clear to �0� correctly, then software may attempt a port reset or a full HBA reset to recover. + //AHCI 10.4.2 If PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recover. if (i == 101) { - RecordExecutionHistory(ChannelExtension, 0x10350011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to �0� correctly + RecordExecutionHistory(ChannelExtension, 0x10350011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly return FALSE; } @@ -339,17 +339,17 @@ Return value: StorPortStallExecution(50); //50 microseconds } - //If CI does not clear to �0� correctly abort the stop + //If CI does not clear to ‘0’ correctly abort the stop if (i == 101) { - RecordExecutionHistory(ChannelExtension, 0x10360011); // P_NotRunning, CI does not clear to �0� + RecordExecutionHistory(ChannelExtension, 0x10360011); // P_NotRunning, CI does not clear to ‘0’ return FALSE; } //2.1 Clear CMD.FRE cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong); if( (cmd.FRE | cmd.FR) != 0 ) { - //If PxCMD.FRE is set to �1�, software should clear it to �0� and wait at least 500 milliseconds for PxCMD.FR to return �0� when read. - //AHCI 10.3.2 Software shall not clear this bit while PxCMD.ST or PxCMD.CR is set to �1�. + //If PxCMD.FRE is set to ‘1’, software should clear it to ‘0’ and wait at least 500 milliseconds for PxCMD.FR to return ‘0’ when read. + //AHCI 10.3.2 Software shall not clear this bit while PxCMD.ST or PxCMD.CR is set to ‘1’. cmd.FRE = 0; StorPortWriteRegisterUlong(adapterExtension, &Px->CMD.AsUlong, cmd.AsUlong); @@ -371,18 +371,18 @@ Return value: StorPortStallExecution(5000); // 5 milliseconds } - if ( i == 101 ) { //If PxCMD.CR or PxCMD.FR do not clear to �0� correctly, then software may attempt a port reset or a full HBA reset to recover. + if ( i == 101 ) { //If PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recover. if (supportsCLO) { - RecordExecutionHistory(ChannelExtension, 0x10380011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to �0�, CLO enabled + RecordExecutionHistory(ChannelExtension, 0x10380011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’, CLO enabled } else { - RecordExecutionHistory(ChannelExtension, 0x10370011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to �0�, CLO not enabled + RecordExecutionHistory(ChannelExtension, 0x10370011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’, CLO not enabled } return FALSE; } } //2.2 wait for CLO to clear - // AHCI 3.3.7 Software must wait for CLO to be cleared to �0� before setting PxCMD.ST to �1�. + // AHCI 3.3.7 Software must wait for CLO to be cleared to ‘0’ before setting PxCMD.ST to ‘1’. // register bit CLO might be set in this function, and it should have been cleared before function exit. if ( supportsCLO && (cmd.CLO == 0) ) { for (i = 1; i < 101; i++) { @@ -394,7 +394,7 @@ Return value: } if ( i == 101 ) { - RecordExecutionHistory(ChannelExtension, 0x10390011); // P_NotRunning, PxCMD.CLO not clear to �0�, CLO enabled + RecordExecutionHistory(ChannelExtension, 0x10390011); // P_NotRunning, PxCMD.CLO not clear to ‘0’, CLO enabled return FALSE; } } @@ -464,6 +464,14 @@ RunNextPort( } else { //starting all ports process is completed. adapterExtension->InRunningPortsProcess = FALSE; + + // + // Note: If we are at DIRQL when get here, it implies previous port start finishes quickly since no + // timer scheduled during port start. Then there is no need to log the port start time. + // + if (!AtDIRQL) { + AhciPortStartTelemetry(adapterExtension); + } } return; @@ -588,7 +596,7 @@ It performs: 1.2 Next, is the port somehow already running? 1.3 Make sure the device knows it is supposed to be spun up 1.4 Check to make sure that FR and CR are both 0. Attempt to bring the controller into a consistent state by stopping the controller. - 1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not �4h� + 1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not ‘4h’ 2.1 Dispatch to the current state (states are responsible for selecting the next state) @@ -633,7 +641,6 @@ Return Values: return TRUE; } - //1.3 Make sure the device knows it is supposed to be spun up cmd.SUD = 1; StorPortWriteRegisterUlong(adapterExtension, &px->CMD.AsUlong, cmd.AsUlong); @@ -662,7 +669,7 @@ Return Values: } } - //1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not �4h� + //1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not ‘4h’ ssts.AsUlong = StorPortReadRegisterUlong(adapterExtension, &px->SSTS.AsUlong); if( ssts.DET == 0x4) { P_Running_StartFailed(ChannelExtension, TimerCallbackProcess); @@ -1088,10 +1095,10 @@ Affected Variables/Registers: */ { - AHCI_COMMAND cmd; - AHCI_TASK_FILE_DATA tfd; - AHCI_SERIAL_ATA_ERROR serr; - USHORT totalstarttime; + AHCI_COMMAND cmd; + AHCI_TASK_FILE_DATA tfd; + AHCI_SERIAL_ATA_ERROR serr; + USHORT totalstarttime; PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension; @@ -1099,7 +1106,7 @@ WaitOnBSYDRQ_Start: RecordExecutionHistory(ChannelExtension, 0x0000001a);//P_Running_WaitOnBSYDRQ tfd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->TFD.AsUlong); - //1.1 Enable BSY and DRQ to go to 0 + //1.1 Enable BSY and DRQ to go to 0 if ( (tfd.STS.BSY) || (tfd.STS.DRQ) ) { //When [serr.DIAG.X is] set to one this bit indicates a COMINIT signal was received. This bit is reflected in the P0IS.PCS bit. //to allow the TFD to be updated serr.DIAG.X must be cleared. @@ -1107,10 +1114,10 @@ WaitOnBSYDRQ_Start: serr.DIAG.X = 1; StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->SERR.AsUlong, serr.AsUlong); } - //3.1 Set ST to 1 + //3.1 Set ST to 1 if ( ( tfd.STS.BSY == 0) && ( tfd.STS.DRQ == 0) ) { - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - BOOLEAN needSpinLock; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + BOOLEAN needSpinLock; if ( TimerCallbackProcess && (ChannelExtension->StartState.DirectStartInProcess == 1) ) { //This is timer callback and a direct port start process has been started separately, bail out this one. @@ -1124,11 +1131,11 @@ WaitOnBSYDRQ_Start: AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } - //3.2 Enable Interrupts on the channel (AHCI 1.1 Section 10.1.2 - 7) + //3.2 Enable Interrupts on the channel (AHCI 1.1 Section 10.1.2 - 7) PortClearPendingInterrupt(ChannelExtension); Set_PxIE(ChannelExtension, &ChannelExtension->Px->IE); - //We made it! Set ST and start the IO we have collected! + //We made it! Set ST and start the IO we have collected! cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong); cmd.ST = 1; StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong); @@ -1138,7 +1145,19 @@ WaitOnBSYDRQ_Start: StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Port Started\n", ChannelExtension->PortNumber); - //Start requests on this Port + // + // Note: If ChannelStateBSYDRQCount is larger than 50, which implies the other counts are + // all cleared to 0 according to port start state machine implementation. But typically + // WaitOnBSYDRQ is always the most significant time consuming phase of port start, so the + // total port start time here is still able to reflect the rough port start status. + // + ChannelExtension->TotalPortStartTime = (ULONG)(ChannelExtension->StartState.ChannelStateDETCount + + ChannelExtension->StartState.ChannelStateDET1Count + + ChannelExtension->StartState.ChannelStateDET3Count + + ChannelExtension->StartState.ChannelStateFRECount + + (ChannelExtension->StartState.ChannelStateBSYDRQCount * 2)) * 10; + + //Start requests on this Port ActivateQueue(ChannelExtension, TRUE); ChannelExtension->StartState.DirectStartInProcess = 0; @@ -1149,16 +1168,20 @@ WaitOnBSYDRQ_Start: AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } + if (ChannelExtension->StateFlags.NeedQDR) { + StorPortIssueDpc(ChannelExtension->AdapterExtension, &(ChannelExtension->BusChangeDpc), ChannelExtension, NULL); + ChannelExtension->StateFlags.NeedQDR = FALSE; + } + RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL)); return; } else { - //2.3 After waiting for the remainder of the 60 second maximum Channel Start time for BSY and DRQ to clear, give up - //Some big HDDs takes close to 20 second to spin up + //2.3 After waiting for the remainder of the 60 second maximum Channel Start time for BSY and DRQ to clear, give up + //Some big HDDs takes close to 20 second to spin up ULONG portStartTimeoutIn10MS = (AHCI_PORT_START_TIMEOUT_IN_SECONDS * 100); - // calculate the total time in unit of 10 ms. totalstarttime = ChannelExtension->StartState.ChannelStateDETCount + ChannelExtension->StartState.ChannelStateDET1Count + @@ -1170,7 +1193,7 @@ WaitOnBSYDRQ_Start: P_Running_StartFailed(ChannelExtension, TimerCallbackProcess); RecordExecutionHistory(ChannelExtension, 0x00ff001a);//P_Running_WaitOnBSYDRQ timed out return; - //2.2 After a total amount of 1 second working on the start COMRESET + //2.2 After a total amount of 1 second working on the start COMRESET } else if ( ChannelExtension->StartState.ChannelStateBSYDRQCount == 50 ){ //Stop FRE,FR in preparation for RESET if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ){ @@ -1196,7 +1219,7 @@ WaitOnBSYDRQ_Start: RecordExecutionHistory(ChannelExtension, 0x00fd001a);//P_Running_WaitOnBSYDRQ crossing 1 second. COMRESET done, going to WaitOnDET3 P_Running_WaitOnDET3(ChannelExtension, TimerCallbackProcess); return; - //2.1 Wait for the rest of the 4 seconds for BSY and DRQ to clear + //2.1 Wait for the rest of the 4 seconds for BSY and DRQ to clear } else { ChannelExtension->StartState.ChannelStateBSYDRQCount++; RecordExecutionHistory(ChannelExtension, 0x0008001a);//P_Running_WaitOnBSYDRQ still waiting @@ -1244,8 +1267,21 @@ Affected Variables/Registers: none */ { - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - BOOLEAN needSpinLock; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + BOOLEAN needSpinLock; + + ++(ChannelExtension->TotalCountRunningStartFailed); + // + // Note: If ChannelStateBSYDRQCount is larger than 50, which implies the other counts are + // all cleared to 0 according to port start state machine implementation. But typically + // WaitOnBSYDRQ is always the most significant time consuming phase of port start, so the + // total port start time here is still able to reflect the rough port start status. + // + ChannelExtension->TotalPortStartTime = (ULONG)(ChannelExtension->StartState.ChannelStateDETCount + + ChannelExtension->StartState.ChannelStateDET1Count + + ChannelExtension->StartState.ChannelStateDET3Count + + ChannelExtension->StartState.ChannelStateFRECount + + (ChannelExtension->StartState.ChannelStateBSYDRQCount * 2)) * 10; if ( TimerCallbackProcess && (ChannelExtension->StartState.DirectStartInProcess == 1) ) { //This is timer callback and a direct port start process has been started separately, bail out this one. @@ -1259,7 +1295,7 @@ Affected Variables/Registers: AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } - //1.1 update state machines + //1.1 update state machines ChannelExtension->StartState.ChannelNextStartState = StartFailed; ChannelExtension->StateFlags.IgnoreHotplugInterrupt = FALSE; ChannelExtension->StateFlags.D3ColdEnabled = FALSE; //this flag may be set to "TRUE" for last connected device, clear it when no device connected. @@ -1281,7 +1317,7 @@ Affected Variables/Registers: AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES)); AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED)); - //1.2 clear out the programmed slots + //1.2 clear out the programmed slots ChannelExtension->SlotManager.CommandsToComplete = GetOccupiedSlots(ChannelExtension); ChannelExtension->SlotManager.CommandsIssued = 0; ChannelExtension->SlotManager.NCQueueSliceIssued = 0; @@ -1292,11 +1328,11 @@ Affected Variables/Registers: ChannelExtension->SlotManager.SingleIoSlice = 0; ChannelExtension->SlotManager.HighPriorityAttribute = 0; - //1.3 Enable Interrupts on the Channel (AHCI 1.1 Section 10.1.2 - 7) + //1.3 Enable Interrupts on the Channel (AHCI 1.1 Section 10.1.2 - 7) PortClearPendingInterrupt(ChannelExtension); Set_PxIE(ChannelExtension, &ChannelExtension->Px->IE); - //2.1 Call AhciPortFailAllIos to complete all outstanding commands now that ChannelNextStartState is StartFailed + //2.1 Call AhciPortFailAllIos to complete all outstanding commands now that ChannelNextStartState is StartFailed AhciPortFailAllIos(ChannelExtension, SRB_STATUS_NO_DEVICE, TRUE); ChannelExtension->StartState.DirectStartInProcess = 0; @@ -1320,11 +1356,11 @@ AHCI 1.1 Section 6.2.2.1 "The flow for system software to recover from an error when non-queued commands are issued is as follows: Reads PxCI to see which commands are still outstanding Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred - Clears PxCMD.ST to �0� to reset the PxCI register, waits for PxCMD.CR to clear to �0� + Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ Clears any error bits in PxSERR to enable capturing new errors. Clears status bits in PxIS as appropriate - If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to �1�, issue a COMRESET to the device to put it in an idle state - Sets PxCMD.ST to �1� to enable issuing new commands + If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state + Sets PxCMD.ST to ‘1’ to enable issuing new commands It assumes: Called asynchronously @@ -1343,10 +1379,10 @@ It performs: 1.1 Initialize 1.2 Reads PxCI to see which commands are still outstanding 1.3 Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred - 1.4.1 Clears PxCMD.ST to �0� to reset the PxCI register, waits for PxCMD.CR to clear to �0� + 1.4.1 Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ 1.5 Clears any error bits in PxSERR to enable capturing new errors. 1.6 Clears status bits in PxIS as appropriate - 1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to �1�, issue a COMRESET to the device to put it in an idle state + 1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state 1.4.3 If a COMRESET was issued, restore Preserved Settings 1.4.4 Start the channel @@ -1374,6 +1410,16 @@ Affected Variables/Registers: //1.1 Initialize variables RecordExecutionHistory(ChannelExtension, 0x00000013);//AhciNonQueuedErrorRecovery + ++(ChannelExtension->TotalCountNonQueuedErrorRecovery); + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdNonqueuedErrorRecovery, + "AhciNonQueuedErrorRecovery", + 0, + NULL, + 0 + ); + senseSrb = NULL; performedCOMRESET = FALSE; @@ -1383,7 +1429,7 @@ Affected Variables/Registers: //1.3 Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); - //1.4 Clears PxCMD.ST to �0� to reset the PxCI register, waits for PxCMD.CR to clear to �0� + //1.4 Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ){ //This clears PxCI RecordExecutionHistory(ChannelExtension, 0x10160013);//AhciNonQueuedErrorRecovery, Port Stop Failed AhciPortReset(ChannelExtension, FALSE); @@ -1434,6 +1480,10 @@ Affected Variables/Registers: // Keep in mind although Px.CMD.ST was just shut off, we are working with the previous snapshot of ST. if (cmd.ST == 1) { + + ULONG storStatus = STOR_STATUS_NOT_IMPLEMENTED; + STOR_REQUEST_INFO requestInfo = {0}; + srbExtension = GetSrbExtension(ChannelExtension->Slot[failingCommand].Srb); // Remove the errant command from the Commands Issued. ci &= ~(1 << failingCommand); @@ -1448,7 +1498,16 @@ Affected Variables/Registers: // Fill in the status of the command srbExtension->AtaStatus = ChannelExtension->TaskFileData.STS.AsUchar; srbExtension->AtaError = ChannelExtension->TaskFileData.ERR; - ChannelExtension->Slot[failingCommand].Srb->SrbStatus = SRB_STATUS_ERROR; + + // Check whether the failed command is paging IO, if so, return busy status to indicate storport to retry it. + storStatus = StorPortGetRequestInfo(ChannelExtension, (PSCSI_REQUEST_BLOCK)ChannelExtension->Slot[failingCommand].Srb, &requestInfo); + if ((storStatus == STOR_STATUS_SUCCESS) && + (requestInfo.Flags & REQUEST_INFO_PAGING_IO_FLAG)) { + ChannelExtension->Slot[failingCommand].Srb->SrbStatus = SRB_STATUS_BUSY; + } else { + ChannelExtension->Slot[failingCommand].Srb->SrbStatus = SRB_STATUS_ERROR; + } + // Handle Request Sense if needed if ( NeedRequestSense(ChannelExtension->Slot[failingCommand].Srb) ) { // This is for ATAPI device only @@ -1476,7 +1535,7 @@ Affected Variables/Registers: //1.6 Clears status bits in PxIS as appropriate //Handled in the Interrupt routine - //1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to �1�, issue a COMRESET to the device to put it in an idle state + //1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state tfd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->TFD.AsUlong); if(tfd.STS.BSY || tfd.STS.DRQ) { @@ -1527,12 +1586,22 @@ NcqErrorRecoveryCompletion ( */ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); - ULONG issuedCommands = (ULONG)(ULONG_PTR)srbExtension->CompletionContext; - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - BOOLEAN fallBacktoReset = FALSE; + ULONG issuedCommands = (ULONG)(ULONG_PTR)srbExtension->CompletionContext; + STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; + BOOLEAN fallBacktoReset = FALSE; RecordExecutionHistory(ChannelExtension, 0x0000001b); //Enter NcqErrorRecoveryCompletion + ++(ChannelExtension->TotalCountNCQErrorRecoveryComplete); + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdNCQErrorRecoveryComplete, + "NcqErrorRecoveryCompletion", + 0, + NULL, + 0 + ); + // // In case of reset happened when Read Log command was executing, all requests have been returned back to port driver. // There is no more recovery work this routine needs to do. @@ -1553,9 +1622,9 @@ NcqErrorRecoveryCompletion ( // Other commands will be put back into NCQ slices and will be issued again to adapter. // PGP_LOG_NCQ_COMMAND_ERROR ncqErrorLog = (PGP_LOG_NCQ_COMMAND_ERROR)ChannelExtension->DeviceExtension->ReadLogExtPageData; - UCHAR failingCommand; - PSTORAGE_REQUEST_BLOCK failingSrb = NULL; - PAHCI_SRB_EXTENSION failingSrbExtension = NULL; + UCHAR failingCommand; + PSTORAGE_REQUEST_BLOCK failingSrb = NULL; + PAHCI_SRB_EXTENSION failingSrbExtension = NULL; NT_ASSERT(ncqErrorLog->NonQueuedCmd == 0); NT_ASSERT(ncqErrorLog->NcqTag != 0); @@ -1569,71 +1638,89 @@ NcqErrorRecoveryCompletion ( if (((1 << failingCommand) & issuedCommands) != 0) { failingSrb = ChannelExtension->Slot[failingCommand].Srb; - NT_ASSERT(failingSrb != NULL); - - failingSrbExtension = GetSrbExtension(failingSrb); // - // Record error and status - // - failingSrbExtension->AtaStatus = ncqErrorLog->Status; - failingSrbExtension->AtaError = ncqErrorLog->Error; + // If the surprise remove happens just after the NCQ error handling, then + // there is chance that the failingSrb is NULL here. + // + if (failingSrb != NULL) { + failingSrbExtension = GetSrbExtension(failingSrb); - if( IsReturnResults(failingSrbExtension->Flags) ) { - SetReturnRegisterValues(ChannelExtension, failingSrb, NULL); - } + // + // Record error and status + // + failingSrbExtension->AtaStatus = ncqErrorLog->Status; + failingSrbExtension->AtaError = ncqErrorLog->Error; - // - // Get Sense Data from log, or from translation. - // - if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaFeaturesSupported.NCQAutosense == 1) && - (ncqErrorLog->SenseKey != SCSI_SENSE_NO_SENSE)) { + if( IsReturnResults(failingSrbExtension->Flags) ) { + SetReturnRegisterValues(ChannelExtension, failingSrb, NULL); + } - StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. Sense Data returned: %02X/%02X/%02X\n", - ChannelExtension->PortNumber, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ); + // + // Get Sense Data from log, or from translation. + // + if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaFeaturesSupported.NCQAutosense == 1) && + (ncqErrorLog->SenseKey != SCSI_SENSE_NO_SENSE)) { + + StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. Sense Data returned: %02X/%02X/%02X\n", + ChannelExtension->PortNumber, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ); + + AhciSetSenseData(failingSrb, SRB_STATUS_ERROR, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ); + } else { + // + // Set SrbStatus indicates it's an error condition. + // Sense data will be translated from Error register in ReleaseSlottedCommand() + // + StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. No Sense Data returned\n", ChannelExtension->PortNumber); + + failingSrb->SrbStatus = SRB_STATUS_ERROR; + AtaMapError(ChannelExtension, failingSrb, ChannelExtension->Slot[failingCommand].StateFlags.FUA); + } - AhciSetSenseData(failingSrb, SRB_STATUS_ERROR, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ); - } else { // - // Set SrbStatus indicates it's an error condition. - // Sense data will be translated from Error register in ReleaseSlottedCommand() + // Inform ReleaseSlottedCommand() function that sense data has been set. // - StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. No Sense Data returned\n", ChannelExtension->PortNumber); + SETMASK(failingSrbExtension->Flags, ATA_FLAGS_SENSEDATA_SET); - failingSrb->SrbStatus = SRB_STATUS_ERROR; - AtaMapError(ChannelExtension, failingSrb, ChannelExtension->Slot[failingCommand].StateFlags.FUA); - } + // + // Acquire spinlock before touching Slots. + // + AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); - // - // Inform ReleaseSlottedCommand() function that sense data has been set. - // - SETMASK(failingSrbExtension->Flags, ATA_FLAGS_SENSEDATA_SET); + // + // Remove the failing command from NCQ Slice. + // Put back other previous outstanding commands. + // + ChannelExtension->SlotManager.NCQueueSlice &= ~(1 << failingCommand); - // - // Acquire spinlock before touching Slots. - // - AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); + // + // Clear the NCQ Error Recovery state flag, so that queue will be resumed in ReleaseSlottedCommand(). + // + ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0; - // - // Remove the failing command from NCQ Slice. - // Put back other previous outstanding commands. - // - ChannelExtension->SlotManager.NCQueueSlice &= ~(1 << failingCommand); + // + // Complete the failing command. + // + ChannelExtension->SlotManager.CommandsToComplete |= (1 << failingCommand); + ReleaseSlottedCommand(ChannelExtension, (UCHAR)failingCommand, TRUE); - // - // Clear the NCQ Error Recovery state flag, so that queue will be resumed in ReleaseSlottedCommand(). - // - ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0; + ActivateQueue(ChannelExtension, TRUE); - // - // Complete the failing command. - // - ChannelExtension->SlotManager.CommandsToComplete |= (1 << failingCommand); - ReleaseSlottedCommand(ChannelExtension, (UCHAR)failingCommand, TRUE); + AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); - ActivateQueue(ChannelExtension, TRUE); + } else { + // + // If failingSrb is NULL, clear the NCQ Error Recovery state flag. + // + ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0; - AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); + StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully, but the failing srb is NULL!\n", ChannelExtension->PortNumber); + + // + // Capture the scenarios that failing srb is NULL, need investigate cases other than surprise remove. + // + NT_ASSERT(failingSrb != NULL); + } } else { StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Tag doesn't belong to issued commands.\n", ChannelExtension->PortNumber); @@ -1695,13 +1782,13 @@ AHCI 1.1 Section 6.2.2.2 Native Command Queuing Error Recovery The flow for system software to recover from an error when native command queuing commands are issued is as follows: - � Reads PxSACT to see which commands have not yet completed - � Clears PxCMD.ST to �0� to reset the PxCI and PxSACT registers, waits for PxCMD.CR to clear to �0� - � Clears any error bits in PxSERR to enable capturing new errors. - � Clears status bits in PxIS as appropriate - � If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to �1�, issue a COMRESET to the device to put it in an idle state - � Sets PxCMD.ST to �1� to enable issuing new commands - � Issue READ LOG EXT to determine the cause of the error condition if software did not have to perform a reset (COMRESET or software reset) as part of the error recovery + • Reads PxSACT to see which commands have not yet completed + • Clears PxCMD.ST to ‘0’ to reset the PxCI and PxSACT registers, waits for PxCMD.CR to clear to ‘0’ + • Clears any error bits in PxSERR to enable capturing new errors. + • Clears status bits in PxIS as appropriate + • If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state + • Sets PxCMD.ST to ‘1’ to enable issuing new commands + • Issue READ LOG EXT to determine the cause of the error condition if software did not have to perform a reset (COMRESET or software reset) as part of the error recovery Software then either completes commands that did not finish with error to higher level software, or reissues them to the device. @@ -1733,6 +1820,15 @@ Called by: // RecordExecutionHistory(ChannelExtension, 0x00000014); //AhciNcqErrorRecovery + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdNCQErrorRecovery, + "AhciNcqErrorRecovery Enter", + 0, + NULL, + 0 + ); + // // Reads PxSACT and PxCI to see which commands are still outstanding // @@ -1742,7 +1838,7 @@ Called by: #endif // - // Clears PxCMD.ST to �0�, waits for PxCMD.CR to clear to �0� + // Clears PxCMD.ST to ‘0’, waits for PxCMD.CR to clear to ‘0’ // If it doesn't succeed, issue COMRESET to recover. // if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ) { @@ -1761,7 +1857,7 @@ Called by: if (needCOMRESET == FALSE) { // - // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to �1�, issue a COMRESET to the device to put it in an idle state + // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state // ChannelExtension->TaskFileData has been read from register in ISR. // if ((ChannelExtension->TaskFileData.STS.BSY != 0) || (ChannelExtension->TaskFileData.STS.DRQ != 0)) { @@ -1863,6 +1959,15 @@ Called by: RecordExecutionHistory(ChannelExtension, 0x10000014); //Exit AhciNcqErrorRecovery + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdNCQErrorRecovery, + "AhciNcqErrorRecovery Exit", + 0, + NULL, + 0 + ); + return; } @@ -1886,6 +1991,44 @@ Return Values: None */ { + ++(ChannelExtension->TotalCountPortErrorRecovery); + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortErrorRecovery, + "AhciPortErrorRecovery", + 0, + NULL, + 0 + ); + + // Log the StateFlags + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"AhciPortErrorRecovery", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + NULL, + L"StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + L"PortErrRecovery", + ChannelExtension->TotalCountPortErrorRecovery, + L"StartState", + *(ULONGLONG *)&(ChannelExtension->StartState), + L"PortReset", + ChannelExtension->TotalCountPortReset, + L"RunningStrtFail", + ChannelExtension->TotalCountRunningStartFailed, + L"NonQ'dErrRecov", + ChannelExtension->TotalCountNonQueuedErrorRecovery, + L"NCQError|NCQErrorComplete", + (((ULONGLONG)ChannelExtension->TotalCountNCQError << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNCQErrorRecoveryComplete)), + L"BusChange", + ChannelExtension->TotalCountBusChange); + if (ChannelExtension->StateFlags.CallAhciReportBusChange == 1) { // Handle AHCI 6.2.2.3 Recovery of Unsolicited COMINIT and hot plug // AhciPortBusChangeDpcRoutine() will issue RESET, ignore other error recovery marks. @@ -1894,6 +2037,11 @@ Return Values: ChannelExtension->StateFlags.CallAhciReset = 0; ChannelExtension->StateFlags.CallAhciNcqErrorRecovery = 0; + ++(ChannelExtension->TotalCountBusChange); + if (ChannelExtension->TotalCountBusChange >= AHCI_BUS_CHANGE_COUNT_WARNING_THRESHOLD) { + AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyBusChange, 0, TRUE); + } + if (!IsDumpMode(ChannelExtension->AdapterExtension)) { StorPortIssueDpc(ChannelExtension->AdapterExtension, &ChannelExtension->BusChangeDpc, ChannelExtension, NULL); } else { @@ -1910,6 +2058,11 @@ Return Values: ChannelExtension->StateFlags.CallAhciReset = 0; ChannelExtension->StateFlags.CallAhciNcqErrorRecovery = 0; + ++(ChannelExtension->TotalCountNCQError); + if (ChannelExtension->TotalCountNCQError >= AHCI_NCQ_ERROR_COUNT_WARNING_THRESHOLD) { + AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyNCQError, 0, TRUE); + } + if (SupportsEnhancedNcqErrorRecovery(ChannelExtension)) { AhciNcqErrorRecovery(ChannelExtension); } else { @@ -1932,6 +2085,10 @@ Return Values: // Handle AHCI 6.2.2.1 Non-Queued Error Recovery ChannelExtension->StateFlags.CallAhciNonQueuedErrorRecovery = 0; + if (ChannelExtension->TotalCountNonQueuedErrorRecovery >= AHCI_NON_QUEUED_ERROR_COUNT_WARNING_THRESHOLD) { + AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyNonQueuedError, 0, TRUE); + } + AhciNonQueuedErrorRecovery(ChannelExtension); } @@ -1939,16 +2096,16 @@ Return Values: } BOOLEAN -AhciPortReset ( +AhciPortReset( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ BOOLEAN CompleteAllRequests ) /*++ -AhciPortReset can be called even if the miniport driver is not ready for another request. -The miniport driver should complete all pending requests and must reset the given channel. + AhciPortReset can be called even if the miniport driver is not ready for another request. + The miniport driver should complete all pending requests and must reset the given channel. It assumes: - nothing + DIRQL Called by: @@ -1970,16 +2127,106 @@ Return Values: If the reset failed the routine must return FALSE. --*/ { - ULONG commandsToCompleteCount; + ULONG commandsToCompleteCount = 0; + BOOLEAN isPortStartInProgress = FALSE; + BOOLEAN isCompletionDPCQueued = FALSE; + BOOLEAN isCompletionDPCInterrupted = FALSE; + + //1.1 Initialize Variables + ++(ChannelExtension->TotalCountPortReset); + + if ((CompleteAllRequests) && + (ChannelExtension->StartState.ChannelNextStartState != StartComplete)) { + + NT_ASSERT(FALSE); + + isPortStartInProgress = TRUE; + RecordExecutionHistory(ChannelExtension, 0x10040050); //AhciPortReset, Port Start still in progress, device probably hang + + } else { + RecordExecutionHistory(ChannelExtension, 0x00000050); //AhciPortReset + } + + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortReset, + (isPortStartInProgress ? "PortStartTakeTooLong" : "AhciPortReset"), + (isPortStartInProgress ? AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING : 0), + NULL, + 0 + ); + + if (ChannelExtension->CompletionQueue.LastTimeStampAddQueue.QuadPart > ChannelExtension->LastTimeStampDpcStart.QuadPart) { + isCompletionDPCQueued = TRUE; + } + + if (ChannelExtension->LastTimeStampDpcStart.QuadPart > ChannelExtension->LastTimeStampDpcCompletion.QuadPart) { + isCompletionDPCInterrupted = TRUE; + } + + if (isCompletionDPCQueued || isCompletionDPCInterrupted) { + + LARGE_INTEGER currentTimeStamp; + StorPortQuerySystemTime(¤tTimeStamp); + ULONGLONG duration = 0; + + NT_ASSERT(FALSE); + + if (isCompletionDPCQueued && isCompletionDPCInterrupted) { + if (ChannelExtension->LastTimeStampDpcStart.QuadPart < ChannelExtension->CompletionQueue.LastTimeStampRemoveQueue.QuadPart) { + duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->CompletionQueue.LastTimeStampRemoveQueue.QuadPart); + } else { + duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->LastTimeStampDpcStart.QuadPart); + } + } else if (isCompletionDPCQueued) { + duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->CompletionQueue.LastTimeStampAddQueue.QuadPart); + } else if (isCompletionDPCInterrupted) { + duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->LastTimeStampDpcStart.QuadPart); + } - //1.1 Initialize Variables - RecordExecutionHistory(ChannelExtension, 0x00000050);//AhciPortReset + RecordExecutionHistory(ChannelExtension, 0x10050050); //AhciPortReset, DPC queued or interrupted - //2.1 Stop the channel + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortReset, + "DPC queued or interrupted", + AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING, + "Duration(in 100ns)", + duration + ); + } + + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + StorportEtwEventOperational, + AhciEtwEventPortReset, + L"Port reset", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + NULL, + L"PortReset", + ChannelExtension->TotalCountPortReset, + L"RunningStrtFail", + ChannelExtension->TotalCountRunningStartFailed, + L"PortErrRecovery", + ChannelExtension->TotalCountPortErrorRecovery, + L"NonQ'dErrRecov", + ChannelExtension->TotalCountNonQueuedErrorRecovery, + L"NCQErrRecovery", + ChannelExtension->TotalCountNCQError, + L"NCQErrRecovCmpl", + ChannelExtension->TotalCountNCQErrorRecoveryComplete, + L"SurpriseRemove", + ChannelExtension->TotalCountSurpriseRemove, + L"StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags)); + + //2.1 Stop the channel P_NotRunning(ChannelExtension, ChannelExtension->Px); - //2.2 Perform the COMRESET - // AHCI 10.1.2 - 3: If PxCMD.CR or PxCMD.FR do not clear to �0� correctly, then software may attempt a port reset or a full HBA reset to recover. + //2.2 Perform the COMRESET + // AHCI 10.1.2 - 3: If PxCMD.CR or PxCMD.FR do not clear to '0' correctly, then software may attempt a port reset or a full HBA reset to recover. AhciCOMRESET(ChannelExtension, ChannelExtension->Px); // @@ -1990,14 +2237,89 @@ Return Values: ChannelExtension->StateFlags.QueuePaused = 0; } - //2.3 If either Init Command or PreservedSettings Command is being processed, reset the command index to start from the first one again. - // The process will be continued by the Srb's completion routine. + if ((CompleteAllRequests) && + (ChannelExtension->StateFlags.QueuePaused == 1) && + (ChannelExtension->SlotManager.CommandsIssued == 0)) { + + // + // This should never happen. Resetting state. + // + + NT_ASSERT(FALSE); + + ChannelExtension->StateFlags.QueuePaused = 0; + + RecordExecutionHistory(ChannelExtension, 0x10010050); //AhciPortReset, Unpausing queue + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortReset, + "Unpausing queue", + AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING, + NULL, + 0 + ); + } + + //2.3 If either Init Command or PreservedSettings Command is being processed, reset the command index to start from the first one again. + // The process will be continued by the Srb's completion routine. if (ChannelExtension->StateFlags.ReservedSlotInUse == 1) { ChannelExtension->DeviceInitCommands.CommandToSend = 0; ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots; } - //3.1 Complete all issued commands + if ((CompleteAllRequests) && + (ChannelExtension->StateFlags.ReservedSlotInUse == 1) && + (ChannelExtension->SlotManager.SingleIoSliceIssued == 0) && + ((ChannelExtension->SlotManager.SingleIoSlice & 0x1) == 0)) { + + NT_ASSERT(FALSE); + + // + // This could happen if init/preserved setting command completed by device, but the completion DPC is queued(or interrupted) for long time. + // If there is other scenario, it is needed to further triage. + // + // Note: Force clear ReservedSlotInUse flag only if corresponding registry key is set. + // Otherwise, there maybe add queue issue. E.g. Buggy device repeatedly generate bus change interrupt, + // which could result in add queue bugcheck eventually. + // + + if (!(isCompletionDPCQueued || isCompletionDPCInterrupted)) { + RecordExecutionHistory(ChannelExtension, 0x10020050); //AhciPortReset, ReservedSlotInUse flag maybe stuck + // + // There maybe possibility that init/restore preserved settings command stuck in device and never complete. + // + AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureDeviceStuck, 0, TRUE); + } else { + RecordExecutionHistory(ChannelExtension, 0x10060050); //AhciPortReset, ReservedSlotInUse flag outstanding + } + + } + + if (((ChannelExtension->SlotManager.SingleIoSliceIssued & 0x1) != 0) && + (ChannelExtension->Slot[0].Srb != NULL) && + ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb == ChannelExtension->Slot[0].Srb)) { + + if ((ChannelExtension->Local.SrbExtension != NULL) && + ((ChannelExtension->Local.SrbExtension->CompletionRoutine == IssuePreservedSettingCommands) || + (ChannelExtension->Local.SrbExtension->CompletionRoutine == IssueInitCommands))) { + + BOOLEAN isInitCommand = (ChannelExtension->Local.SrbExtension->CompletionRoutine == IssueInitCommands) ? (TRUE) : (FALSE); + + NT_ASSERT(FALSE); + + RecordExecutionHistory(ChannelExtension, 0x10030050); //AhciPortReset, Init/Preserved Setting command timeout + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortReset, + "Slot 0 command timeout", + AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING, + (isInitCommand ? "Init Command" : "Preserved Setting Command"), + 0 + ); + } + } + + //3.1 Complete all issued commands ChannelExtension->SlotManager.CommandsToComplete = ChannelExtension->SlotManager.CommandsIssued; ChannelExtension->SlotManager.CommandsIssued = 0; ChannelExtension->SlotManager.NCQueueSliceIssued = 0; @@ -2030,17 +2352,17 @@ Return Values: AhciCompleteIssuedSRBs(ChannelExtension, completeStatus, TRUE); //AhciPortReset is under Interrupt spinlock } - //3.2 Complete all other commands miniport own for this device, when it's necessary or when the Reset is requested by Storport + //3.2 Complete all other commands miniport own for this device, when it's necessary or when the Reset is requested by Storport if (CompleteAllRequests) { AhciPortFailAllIos(ChannelExtension, SRB_STATUS_BUS_RESET, TRUE); } - //4.1 Restore device configuration + //4.1 Restore device configuration if (ChannelExtension->StateFlags.ReservedSlotInUse == 0) { RestorePreservedSettings(ChannelExtension, TRUE); } - //2.3 Start the channel + //2.3 Start the channel P_Running_StartAttempt(ChannelExtension, TRUE); //AhciPortReset is under Interrupt spinlock // record that the channel is reset. @@ -2057,4 +2379,3 @@ Return Values: #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/hbastat.h b/storage/miniports/storahci/src/hbastat.h index 40986668..b401a81e 100644 --- a/storage/miniports/storahci/src/hbastat.h +++ b/storage/miniports/storahci/src/hbastat.h @@ -107,4 +107,3 @@ AhciPortErrorRecovery( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ); - diff --git a/storage/miniports/storahci/src/inbox/storahci.vcxproj b/storage/miniports/storahci/src/inbox/storahci.vcxproj index 7e694879..eb2e4f82 100644 --- a/storage/miniports/storahci/src/inbox/storahci.vcxproj +++ b/storage/miniports/storahci/src/inbox/storahci.vcxproj @@ -76,11 +76,6 @@ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> </ImportGroup> <ItemGroup Label="WrappedTaskItems"> - <ClCompile Include="..\entrypts.c; ..\pnppower.c; ..\hbastat.c; ..\util.c; ..\io.c; ..\common.c"> - <WppEnabled>true</WppEnabled> - <WppKernelMode>true</WppKernelMode> - <WppTraceFunction>TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction> - </ClCompile> <OtherWpp Include="..\storahci.rc"> <WppEnabled>true</WppEnabled> <WppKernelMode>true</WppKernelMode> @@ -186,5 +181,37 @@ <ItemGroup> <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> + <ItemGroup> + <ClCompile Include="..\common.c"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppTraceFunction>TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction> + </ClCompile> + <ClCompile Include="..\entrypts.c"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppTraceFunction>TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction> + </ClCompile> + <ClCompile Include="..\hbastat.c"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppTraceFunction>TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction> + </ClCompile> + <ClCompile Include="..\io.c"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppTraceFunction>TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction> + </ClCompile> + <ClCompile Include="..\pnppower.c"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppTraceFunction>TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction> + </ClCompile> + <ClCompile Include="..\util.c"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppTraceFunction>TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction> + </ClCompile> + </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> </Project>
\ No newline at end of file diff --git a/storage/miniports/storahci/src/io.c b/storage/miniports/storahci/src/io.c index 3a517854..44913b55 100644 --- a/storage/miniports/storahci/src/io.c +++ b/storage/miniports/storahci/src/io.c @@ -44,7 +44,6 @@ Affected Variables/Registers: Return Value: --*/ { - UCHAR activeCount = 0; UCHAR emptyCount; UCHAR requestCount; @@ -52,26 +51,27 @@ Return Value: ULONG slotToActivate = 0; UCHAR i; - //1 Device's queue depth is smaller than Controller's + // 1. Device's queue depth is smaller than Controller's NT_ASSERT(ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth <= ChannelExtension->AdapterExtension->CAP.NCS); - //count the number of slots already in use + // Count the number of slots already in use if (ChannelExtension->SlotManager.CommandsIssued > 0) { activeCount = NumberOfSetBits(ChannelExtension->SlotManager.CommandsIssued); } - //1.1 Check if all slots are active. + + // 1.1 Check if all slots are active. if (activeCount >= ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth) { - //if all possible slots are full, no matter what, return no work (0) + // If all possible slots are full, no matter what, return no work (0) return 0; } - //2 Look for any entry from last active slot + // 2. Look for any entry from last active slot requestCount = NumberOfSetBits(TargetSlots); lastActiveSlot = ChannelExtension->LastActiveSlot; emptyCount = ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth - activeCount; - //3.1 Look for any entry from last active slot + // 3.1 Look for any entry from last active slot for (i = lastActiveSlot; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) { if ((TargetSlots & (1 << i)) > 0) { slotToActivate |= (1 << i); @@ -84,8 +84,8 @@ Return Value: } } - //3.2 Look for any entry from beginning to last active slot - //Slot 0 is reserved for internal command + // 3.2 Look for any entry from beginning to last active slot + // Slot 0 is reserved for internal command for (i = 1 ; i <= lastActiveSlot; i++) { if ((TargetSlots & (1 << i)) > 0) { slotToActivate |= (1 << i); @@ -138,23 +138,23 @@ Return Value: PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension; - //1.1 Initialize variables + // 1.1 Initialize variables limit = ChannelExtension->CurrentCommandSlot; - // if there is internal request pending, always get it first. - if ( (ChannelExtension->SlotManager.SingleIoSlice & 1) > 0 ) { + // if there is internal request pending, always get it first. + if ((ChannelExtension->SlotManager.SingleIoSlice & 1) > 0) { return 0; } - //2.1 Chose the slot circularly starting with CCS + // 2.1 Chose the slot circularly starting with CCS for (i = limit; i <= adapterExtension->CAP.NCS; i++) { - if ( (ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0){ + if ((ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0) { return i; } } for (i = 0; i < limit; i++) { - if ( (ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0){ + if ((ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0) { return i; } } @@ -163,7 +163,7 @@ Return Value: } VOID -AddQueue ( +AddQueue( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _Inout_ PSTORAHCI_QUEUE Queue, _In_ PSTORAGE_REQUEST_BLOCK Srb, @@ -179,10 +179,13 @@ Input Parameters: Tag: bit 31 ~ 24, Queue->CurrentDepth: bit 23 ~ 0 */ { - PVOID tempTail, foundSrb; - ULONG srbsFound; + PVOID tempTail, foundSrb; + ULONG srbsFound; + UNREFERENCED_PARAMETER(ChannelExtension); + StorPortQuerySystemTime(&(Queue->LastTimeStampAddQueue)); + if (Queue->Tail == NULL) { Queue->Tail = (PVOID) Srb; Queue->Head = (PVOID) Srb; @@ -190,11 +193,14 @@ Input Parameters: tempTail = Queue->Tail; if (SrbGetNextSrb(tempTail) == NULL) { //Verify SRBs are not about to be severed from the Queue SrbSetNextSrb(tempTail, (PVOID)Srb); + } Queue->Tail = (PVOID)Srb; + } Queue->CurrentDepth++; if (Queue->Head == NULL) { //It is impossible for Head to be NULL here + srbsFound = 0; } else { foundSrb = Queue->Head; @@ -209,6 +215,7 @@ Input Parameters: Queue->DepthHistoryIndex++; Queue->DepthHistoryIndex %= 100; Queue->DepthHistory[Queue->DepthHistoryIndex] = Signature; + if (Queue->CurrentDepth > Queue->DeepestDepth) { Queue->DeepestDepth = Queue->CurrentDepth; } @@ -217,23 +224,25 @@ Input Parameters: } PSTORAGE_REQUEST_BLOCK -RemoveQueue ( +RemoveQueue( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _Inout_ PSTORAHCI_QUEUE Queue, _In_ ULONG Signature, _In_ UCHAR Tag ) { - PVOID nextSrb, foundSrb; - ULONG srbsFound; + PVOID nextSrb, foundSrb; + ULONG srbsFound; UNREFERENCED_PARAMETER(ChannelExtension); - //Check to see if the queue is empty + // Check to see if the queue is empty if (Queue->Head == NULL) { return NULL; } + StorPortQuerySystemTime(&(Queue->LastTimeStampRemoveQueue)); + //if it is not empty, pop nextSrb = Queue->Head; Queue->Head = SrbGetNextSrb(nextSrb); @@ -258,6 +267,7 @@ RemoveQueue ( Queue->DepthHistoryIndex++; Queue->DepthHistoryIndex %= 100; Queue->DepthHistory[Queue->DepthHistoryIndex] = Signature; + if (Queue->CurrentDepth > Queue->DeepestDepth) { Queue->DeepestDepth = Queue->CurrentDepth; } @@ -299,20 +309,20 @@ Affected Variables/Registers: Return Values: --*/ { - BOOLEAN status = TRUE; - ULONG slotsToActivate = 0; - BOOLEAN activateNcq = FALSE; - int i; + BOOLEAN status = TRUE; + ULONG slotsToActivate = 0; + BOOLEAN activateNcq = FALSE; + int i; PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension; - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; - //1.1 Initialize variables + // 1.1 Initialize variables if (LogExecuteFullDetail(adapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x00000022);//ActivateQueue } - // If the programming should not happen now, leave + // If the programming should not happen now, leave if (ChannelExtension->StartState.ChannelNextStartState != StartComplete) { RecordExecutionHistory(ChannelExtension, 0x10010022);//ActivateQueue, Channel Not Yet Started status = FALSE; @@ -325,13 +335,13 @@ Return Values: return status; } - if ( !IsPortStartCapable(ChannelExtension) ) { + if (!IsPortStartCapable(ChannelExtension)) { RecordExecutionHistory(ChannelExtension, 0x10030022);//ActivateQueue, Channel Not Start Capable status = FALSE; return status; } - if ( ErrorRecoveryIsPending(ChannelExtension) ) { + if (ErrorRecoveryIsPending(ChannelExtension)) { RecordExecutionHistory(ChannelExtension, 0x10070022);//ActivateQueue, Error Recovery is pending. status = FALSE; return status; @@ -341,10 +351,10 @@ Return Values: AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } - //1.1.1 If there is no command to program, leave - if ( (ChannelExtension->SlotManager.SingleIoSlice == 0) && - (ChannelExtension->SlotManager.NormalQueueSlice == 0) && - (ChannelExtension->SlotManager.NCQueueSlice == 0) ) { + // 1.1.1 If there is no command to program, leave + if ((ChannelExtension->SlotManager.SingleIoSlice == 0) && + (ChannelExtension->SlotManager.NormalQueueSlice == 0) && + (ChannelExtension->SlotManager.NCQueueSlice == 0)) { if (LogExecuteFullDetail(adapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10040022);//ActivateQueue, No Commands to program @@ -363,26 +373,60 @@ Return Values: goto Exit; } - //2.1 Choose the Queue with which to program the controller - //2.1.2 Single IO SRBs have highest priority. - if(ChannelExtension->SlotManager.SingleIoSlice != 0) { - if ( (ChannelExtension->SlotManager.NCQueueSliceIssued | ChannelExtension->SlotManager.NormalQueueSliceIssued | ChannelExtension->SlotManager.SingleIoSliceIssued) == 0 ) { - //Safely get Single IO in round robin fashion + // 2.1 Choose the Queue with which to program the controller + // 2.1.1 Single IO SRBs have highest priority. + if (ChannelExtension->SlotManager.SingleIoSlice != 0) { + if ((ChannelExtension->SlotManager.NCQueueSliceIssued | ChannelExtension->SlotManager.NormalQueueSliceIssued | ChannelExtension->SlotManager.SingleIoSliceIssued) == 0) { + // Safely get Single IO in round robin fashion i = GetSingleIo(ChannelExtension); if (i != 0xff) { + PSLOT_CONTENT slotContent = &ChannelExtension->Slot[i]; + slotsToActivate = (1 << i); ChannelExtension->SlotManager.SingleIoSlice &= ~slotsToActivate; ChannelExtension->SlotManager.SingleIoSliceIssued |= slotsToActivate; ChannelExtension->StateFlags.QueuePaused = TRUE; //and pause the queue so no other IO get programmed + + if (LogExecuteFullDetail(adapterExtension->LogFlags)) { + RecordExecutionHistory(ChannelExtension, 0x10090022); //ActivateQueue, Single IO pause queue + } + + // 2.1.1.1 Update the Identify command for enumeration. This needs to be here for PxSIG to be accessible. + // Accessing PxSIG register here to avoid invalid values from time when the port is being reset or not started, or other scenarios. + if (slotContent->Srb != NULL) { + + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(slotContent->Srb); + PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; + + if ((srbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) && + (srbExtension->CompletionRoutine == AhciPortIdentifyDevice) && + (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_NOT_VALID) && + (cmdTable->CFIS.Command == IDE_COMMAND_NOT_VALID) ) { + + // For enumeration command, it's safe now to check the PxSIG register as starting port has been executed. + ULONG sig = 0; + sig = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong); + + if (sig == ATA_DEVICE_SIGNATURE_ATA) { //ATA + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_IDENTIFY; + cmdTable->CFIS.Command = IDE_COMMAND_IDENTIFY; + } else { //ATAPI + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + cmdTable->CFIS.Command = IDE_COMMAND_ATAPI_IDENTIFY; + } + } + } else { + NT_ASSERT(FALSE); + } } } - //2.1.2 When there are no Single IO commands, Normal IO get the next highest priority + // 2.1.2 When there are no Single IO commands, Normal IO get the next highest priority } else if (ChannelExtension->SlotManager.NormalQueueSlice != 0) { // Normal commands can not be sent when NCQ commands are outstanding. When the NCQ commands complete ActivateQueue will get called again. if (ChannelExtension->SlotManager.NCQueueSliceIssued == 0) { - //Grab the High Priority Normal IO before the Low Priority Normal IO + // Grab the High Priority Normal IO before the Low Priority Normal IO slotsToActivate = ChannelExtension->SlotManager.HighPriorityAttribute & ChannelExtension->SlotManager.NormalQueueSlice; - //If there aren't any High Priority, grab everything else + // If there aren't any High Priority, grab everything else if (slotsToActivate > 0) { ChannelExtension->SlotManager.NormalQueueSlice &= ~slotsToActivate; } else { @@ -391,41 +435,41 @@ Return Values: } ChannelExtension->SlotManager.NormalQueueSliceIssued |= slotsToActivate; } - //2.1.3 When there are no Single or Normal commands, NCQ commands get the next highest priority + // 2.1.3 When there are no Single or Normal commands, NCQ commands get the next highest priority } else if ((ChannelExtension->SlotManager.NCQueueSlice != 0) && (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 0)) { // NCQ commands can not be sent when Normal commands are outstanding. When the Normal commands complete, Activate Queue will get called again. - if ( ( ChannelExtension->SlotManager.SingleIoSliceIssued | ChannelExtension->SlotManager.NormalQueueSliceIssued ) != 0 ) { + if ((ChannelExtension->SlotManager.SingleIoSliceIssued | ChannelExtension->SlotManager.NormalQueueSliceIssued ) != 0) { slotsToActivate = 0; } else { - //Grab the High Priority NCQ IO before the Low Priority NCQ IO + // Grab the High Priority NCQ IO before the Low Priority NCQ IO slotsToActivate = ChannelExtension->SlotManager.HighPriorityAttribute & ChannelExtension->SlotManager.NCQueueSlice; - //If there aren't any High Priority, grab everything else + // If there aren't any High Priority, grab everything else if (slotsToActivate == 0) { slotsToActivate = ChannelExtension->SlotManager.NCQueueSlice; } - //and apply any device outstanding IO limits to filter down which IO to activate + // And apply any device outstanding IO limits to filter down which IO to activate if (slotsToActivate > 0) { - if (ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth < ChannelExtension->MaxPortQueueDepth ) { - // get allowed slots if the device queue depth is less than the port can support. + if (ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth < ChannelExtension->MaxPortQueueDepth) { + // Get allowed slots if the device queue depth is less than the port can support. slotsToActivate = GetSlotToActivate(ChannelExtension, slotsToActivate); } if (slotsToActivate > 0) { - //and if there are any IO still selected, clear them from the NCQueue + // And if there are any IO still selected, clear them from the NCQueue activateNcq = TRUE; //Remember to program SACT for these commands ChannelExtension->SlotManager.NCQueueSlice &= ~slotsToActivate; ChannelExtension->SlotManager.NCQueueSliceIssued |= slotsToActivate; - //the selected IO will be activated at the end of this function + // The selected IO will be activated at the end of this function } } } - //2.1.4 In the case that no IO is present in any Slices, program nothing + // 2.1.4 In the case that no IO is present in any Slices, program nothing } - //2.1 Program all the IO from the chosen queue into the controller + // 2.1 Program all the IO from the chosen queue into the controller if (slotsToActivate != 0) { - //2.2 Get command start time - if(adapterExtension->TracingEnabled) { + // 2.2 Get command start time + if (adapterExtension->TracingEnabled) { LARGE_INTEGER perfCounter = {0}; ULONG pendingProgrammingCommands = slotsToActivate; @@ -434,9 +478,11 @@ Return Values: StorPortQueryPerformanceCounter((PVOID)adapterExtension, NULL, &perfCounter); while (pendingProgrammingCommands) { - if (pendingProgrammingCommands & 1) { + // TODO: there is potential race condition that causes slot flags mismatch and Srb is NULL here. + if ((pendingProgrammingCommands & 1) && (ChannelExtension->Slot[i].Srb != NULL)) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(ChannelExtension->Slot[i].Srb); srbExtension->StartTime = perfCounter.QuadPart; + } i++; @@ -446,7 +492,7 @@ Return Values: ChannelExtension->SlotManager.CommandsIssued |= slotsToActivate; - // program registers + // Program registers if (activateNcq) { StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->SACT, slotsToActivate); } @@ -486,21 +532,21 @@ CalculateTimeDurationIn100ns ( { ULONGLONG timeIn100ns = 0; - // as the counter can hold more than 20,000 years for the same power cycle, + // As the counter can hold more than 20,000 years for the same power cycle, // the situation about the counter start over again is considered an error case of inputs. if (CounterFrequency > 0) { - // difference between performance counters, needs to convert to 100ns. + // Difference between performance counters, needs to convert to 100ns. ULONGLONG countersDiff = TimeDuration; - // get seconds + // Get seconds timeIn100ns = countersDiff / CounterFrequency; - // get milliseconds + // Get milliseconds countersDiff = (countersDiff % CounterFrequency) * 1000; timeIn100ns *= 1000; timeIn100ns += countersDiff / CounterFrequency; - // get 100 nanoseconds + // Get 100 nanoseconds countersDiff = (countersDiff % CounterFrequency) * 10000; timeIn100ns *= 10000; timeIn100ns += countersDiff / CounterFrequency; @@ -546,35 +592,34 @@ Affected Variables/Registers: --*/ { - PSLOT_CONTENT slotContent; - UCHAR i; + PSLOT_CONTENT slotContent; + UCHAR i; PAHCI_ADAPTER_EXTENSION adapterExtension; - PAHCI_SRB_EXTENSION srbExtension; - - LARGE_INTEGER perfCounter = {0}; - LARGE_INTEGER perfFrequency = {0}; + PAHCI_SRB_EXTENSION srbExtension; + LARGE_INTEGER perfCounter = { 0 }; + LARGE_INTEGER perfFrequency = { 0 }; - //1.1 Initialize variables + // 1.1 Initialize variables adapterExtension = ChannelExtension->AdapterExtension; if (LogExecuteFullDetail(adapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x00000046);//AhciCompleteIssuedSRBs } - if( adapterExtension->TracingEnabled && (ChannelExtension->SlotManager.CommandsToComplete) ) { + if (adapterExtension->TracingEnabled && (ChannelExtension->SlotManager.CommandsToComplete)) { StorPortQueryPerformanceCounter((PVOID)adapterExtension, &perfFrequency, &perfCounter); } - //2.1 For every command marked as completed + // 2.1 For every command marked as completed for (i = 0; i <= (adapterExtension->CAP.NCS); i++) { - if( ( ChannelExtension->SlotManager.CommandsToComplete & (1 << i) ) > 0) { + if ((ChannelExtension->SlotManager.CommandsToComplete & (1 << i)) > 0) { slotContent = &ChannelExtension->Slot[i]; if (slotContent->Srb == NULL) { - //This shall never happen. - //The completed slot has no SRB so it can not be completed back to Storport. - //Give back the empty slot + // This shall never happen. + // The completed slot has no SRB so it can not be completed back to Storport. + // Give back the empty slot NT_ASSERT(FALSE); ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << i); ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << i); @@ -582,12 +627,12 @@ Affected Variables/Registers: } if (slotContent->CmdHeader == NULL) { - //This shall never happen. - //Give back the empty slot + // This shall never happen. + // Give back the empty slot NT_ASSERT(FALSE); ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << i); ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << i); - //It is now impossible to determine if a data transfer completed correctly + // It is now impossible to determine if a data transfer completed correctly slotContent->Srb->SrbStatus = SRB_STATUS_ABORTED; AhciCompleteRequest(ChannelExtension, slotContent->Srb, AtDIRQL); continue; @@ -595,46 +640,45 @@ Affected Variables/Registers: srbExtension = GetSrbExtension(slotContent->Srb); - //2.1.2 Log command execution time, if it's allowed - if ( adapterExtension->TracingEnabled && - (srbExtension->StartTime != 0) && - (perfCounter.QuadPart != 0) && - !IsMiniportInternalSrb(ChannelExtension, slotContent->Srb) ) { + // 2.1.2 Log command execution time, if it's allowed + if (adapterExtension->TracingEnabled && + (srbExtension->StartTime != 0) && + (perfCounter.QuadPart != 0) && + !IsMiniportInternalSrb(ChannelExtension, slotContent->Srb)) { ULONGLONG durationTime = CalculateTimeDurationIn100ns((perfCounter.QuadPart - srbExtension->StartTime), perfFrequency.QuadPart); StorPortNotification(IoTargetRequestServiceTime, (PVOID)adapterExtension, durationTime, slotContent->Srb); } - //2.2 Set the status - if( (SrbStatus == SRB_STATUS_SUCCESS) && + // 2.2 Set the status + if ((SrbStatus == SRB_STATUS_SUCCESS) && (!IsRequestSenseSrb(srbExtension->AtaFunction)) && (srbExtension->AtaFunction != ATA_FUNCTION_ATA_SMART) && (!IsNCQCommand(srbExtension)) && - (slotContent->CmdHeader->PRDBC != RequestGetDataTransferLength(slotContent->Srb)) ) { - // + (slotContent->CmdHeader->PRDBC != RequestGetDataTransferLength(slotContent->Srb))) { if (slotContent->CmdHeader->PRDBC < RequestGetDataTransferLength(slotContent->Srb)) { - //buffer underrun, + // Buffer underrun, RequestSetDataTransferLength(slotContent->Srb, slotContent->CmdHeader->PRDBC); slotContent->Srb->SrbStatus = SrbStatus; } else { - //buffer overrun, return error + // Buffer overrun, return error NT_ASSERT(FALSE); slotContent->Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; } } else { - //If anything has set a STATUS on this SRB, honor that over the one passed in + // If anything has set a STATUS on this SRB, honor that over the one passed in if (slotContent->Srb->SrbStatus == SRB_STATUS_PENDING) { slotContent->Srb->SrbStatus = SrbStatus; } } - //2.3 Monitor to see that any NCQ commands are completing - if ( (slotContent->Srb->SrbStatus == SRB_STATUS_SUCCESS) && - (IsNCQCommand(srbExtension)) ) { + // 2.3 Monitor to see that any NCQ commands are completing + if ((slotContent->Srb->SrbStatus == SRB_STATUS_SUCCESS) && + (IsNCQCommand(srbExtension))) { ChannelExtension->StateFlags.NCQ_Succeeded = TRUE; } - //2.4 Give the slot back + // 2.4 Give the slot back ReleaseSlottedCommand(ChannelExtension, i, AtDIRQL); // Request sense is handled here. if (LogExecuteFullDetail(adapterExtension->LogFlags)) { @@ -643,7 +687,7 @@ Affected Variables/Registers: } } - //3.1 Start the next IO(s) if any + // 3.1 Start the next IO(s) if any ActivateQueue(ChannelExtension, AtDIRQL); return; @@ -677,21 +721,23 @@ Affected Variables/Registers: UNREFERENCED_PARAMETER(ChannelExtension); - //1.1 Map SRB fields to CFIS fields + // 1.1 Map SRB fields to CFIS fields cmdTable->CFIS.FisType = 0x27; cmdTable->CFIS.PMPort = 0; // StorAHCI doesn't support Port Multiplier cmdTable->CFIS.Reserved1 = 0; cmdTable->CFIS.C = 1; cmdTable->CFIS.Command = srbExtension->TaskFile.Current.bCommandReg; - //1.2 Special case mapping of NCQ - if( IsNCQCommand(srbExtension) ){ + + // 1.2 Special case mapping of NCQ + if (IsNCQCommand(srbExtension)) { + cmdTable->CFIS.Features = srbExtension->TaskFile.Current.bSectorCountReg; cmdTable->CFIS.Features_Exp = srbExtension->TaskFile.Previous.bSectorCountReg; cmdTable->CFIS.SectorCount = (srbExtension->QueueTag << 3); cmdTable->CFIS.Dev_Head = 0xF & srbExtension->TaskFile.Current.bDriveHeadReg; cmdTable->CFIS.Dev_Head |= (1 << 6); - if( SlotContent->StateFlags.FUA ){ + if (SlotContent->StateFlags.FUA) { cmdTable->CFIS.Dev_Head |= ATA_NCQ_FUA_BIT; } else { cmdTable->CFIS.Dev_Head &= ~ATA_NCQ_FUA_BIT; @@ -705,7 +751,7 @@ Affected Variables/Registers: cmdTable->CFIS.Dev_Head = srbExtension->TaskFile.Current.bDriveHeadReg; } - //1.1 Map SRB fields to CFIS fields + // 1.1 Map SRB fields to CFIS fields cmdTable->CFIS.SectorNumber = srbExtension->TaskFile.Current.bSectorNumberReg; cmdTable->CFIS.SecNum_Exp = srbExtension->TaskFile.Previous.bSectorNumberReg; @@ -749,11 +795,11 @@ Affected Variables/Registers: Command Table --*/ { - //These are used for copying a CDB to ACMD on ATAPI commands. + // These are used for copying a CDB to ACMD on ATAPI commands. ULONG dataLength; PVOID cdb; - //1.1 Memcopy CDB into ACMD + // 1.1 Memcopy CDB into ACMD PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; @@ -768,37 +814,37 @@ Affected Variables/Registers: NT_ASSERT(cdb != NULL); } - //2.1 Put the PACKET Command in the CFIS + // 2.1 Put the PACKET Command in the CFIS cmdTable->CFIS.FisType = 0x27; cmdTable->CFIS.PMPort = 0; // StorAHCI doesn't support Port Multiplier cmdTable->CFIS.Reserved1 = 0; cmdTable->CFIS.C = 1; cmdTable->CFIS.Command = IDE_COMMAND_ATAPI_PACKET; //A0 is the PACKET command - //2.2 Populate DMA bit properly, only use it for data transfer + // 2.2 Populate DMA bit properly, only use it for data transfer if (IsDmaCommand(srbExtension->Flags)) { cmdTable->CFIS.Features = 0x1; - //If word 62 bit 15 is set to one, then the DMADIR bit in the PACKET command is required by the device for - //PACKET commands using the DMA data transfer protocol and: + // If word 62 bit 15 is set to one, then the DMADIR bit in the PACKET command is required by the device for + // PACKET commands using the DMA data transfer protocol and: // a) word 63 bits (2:0); -- MultiWordDMASupport & 0x7 == 0 // b) word 49 bit 15; -- Capabilities.InterleavedDmaSupported // c) word 49 bit 8; and -- Capabilities.DmaSupported // d) word 88 bits (6:0), -- UltraDMASupport & 0x7F == 0 //shall be cleared to zero. - if ( (ChannelExtension->DeviceExtension[0].IdentifyPacketData->DMADIR.DMADIRBitRequired != 0) && - (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.DmaSupported != 0) && - (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.InterleavedDmaSupported == 0) && - ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->MultiWordDMASupport & 0x7) == 0) && - ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->UltraDMASupport & 0x7F) == 0) ) { - //bit 0 is DMA, bit 2 is DMADIR. (0 = transfer to the device; 4 = transfer to the host). + if ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->DMADIR.DMADIRBitRequired != 0) && + (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.DmaSupported != 0) && + (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.InterleavedDmaSupported == 0) && + ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->MultiWordDMASupport & 0x7) == 0) && + ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->UltraDMASupport & 0x7F) == 0)) { + // Bit 0 is DMA, bit 2 is DMADIR. (0 = transfer to the device; 4 = transfer to the host). cmdTable->CFIS.Features |= (srbExtension->Flags & ATA_FLAGS_DATA_IN) ? 4 : 0; } } else { cmdTable->CFIS.Features = 0x0; } - //2.1 Put the PACKET Command in the CFIS + // 2.1 Put the PACKET Command in the CFIS cmdTable->CFIS.SectorNumber = 0; cmdTable->CFIS.CylLow = (UCHAR)dataLength; //put the low byte of the in CylLow dataLength >>= 8; @@ -841,8 +887,8 @@ Affected Variables/Registers: Command Table --*/ { - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); - PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); + PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; UNREFERENCED_PARAMETER(ChannelExtension); @@ -865,7 +911,7 @@ Affected Variables/Registers: // // Set common data fields. // - if( IsNCQCommand(srbExtension) ) { + if (IsNCQCommand(srbExtension)) { cmdTable->CFIS.Count7_0 = (srbExtension->QueueTag << 3); } cmdTable->CFIS.FisType = 0x27; @@ -880,7 +926,6 @@ Affected Variables/Registers: cmdTable->CFIS.Control = 0; // Device control consists of the 48bit HighOrderByte, SRST and nIEN. None apply here. } - ULONG SRBtoPRDT( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, @@ -910,39 +955,39 @@ Return Values: If the value returned is -1 the PRDT could not be built. --*/ { - ULONG i; - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); - PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; - PLOCAL_SCATTER_GATHER_LIST sgl = srbExtension->Sgl; + ULONG i; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); + PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; + PLOCAL_SCATTER_GATHER_LIST sgl = srbExtension->Sgl; if (sgl == NULL) { - //return as invalid request in case of cannot get scatter gather list. + // Return as invalid request in case of cannot get scatter gather list. NT_ASSERT(FALSE); return (ULONG)-1; } for (i = 0; i < sgl->NumberOfElements; i++) { - //1.2 Verify that the DataBuffer is properly aligned - if ( (sgl->List[i].PhysicalAddress.LowPart & 0x1) == 0) { - //2.1 Map SGL entries into PRDT entries + // 1.2 Verify that the DataBuffer is properly aligned + if ((sgl->List[i].PhysicalAddress.LowPart & 0x1) == 0) { + // 2.1 Map SGL entries into PRDT entries if (sgl->List[i].Length != 0x20000) { cmdTable->PRDT[i].DBA.AsUlong = sgl->List[i].PhysicalAddress.LowPart; - if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too + if (ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too cmdTable->PRDT[i].DBAU = sgl->List[i].PhysicalAddress.HighPart; } - //2.2 Break up a 128K single entry IO into 2 64K IO entries (128K is max transfer so there can be only 1 in any SGL) - // although one entry can represent at max 4M length IO, some adapters cannot handle a DBC >= 128K. + // 2.2 Break up a 128K single entry IO into 2 64K IO entries (128K is max transfer so there can be only 1 in any SGL) + // although one entry can represent at max 4M length IO, some adapters cannot handle a DBC >= 128K. } else { // Entry 0 cmdTable->PRDT[0].DBA.AsUlong = sgl->List[0].PhysicalAddress.LowPart; - if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too + if (ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too cmdTable->PRDT[0].DBAU = sgl->List[0].PhysicalAddress.HighPart; } cmdTable->PRDT[0].DI.DBC = (0x10000 - 1); // Entry 1 cmdTable->PRDT[1].DBA.AsUlong = (sgl->List[0].PhysicalAddress.LowPart + 0x10000); - if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too - if ( (sgl->List[0].PhysicalAddress.LowPart + 0x10000) < sgl->List[0].PhysicalAddress.LowPart) { + if (ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too + if ((sgl->List[0].PhysicalAddress.LowPart + 0x10000) < sgl->List[0].PhysicalAddress.LowPart) { cmdTable->PRDT[1].DBAU = (sgl->List[0].PhysicalAddress.HighPart + 1); //add 1 to the highpart if adding 0x10000 caused a rollover } else { cmdTable->PRDT[1].DBAU = sgl->List[0].PhysicalAddress.HighPart; @@ -956,13 +1001,13 @@ Return Values: return (ULONG)-1; } - //1.3 Verify that the DataLength is even - // all SATA transfers must be even - // DBC is a 0 based number (i.e. 0 is 1, 1 is 2, etc. - // sgl->Elements.Length is not (i.e. 0 is 0, 1 is 1, etc. - if( (sgl->List[i].Length & 1) == 0 ) { //therefore length must be even here - //2.3 Set Datalength in the PRDT entries - cmdTable->PRDT[i].DI.DBC = sgl->List[i].Length - 1; //but it must be odd here + // 1.3 Verify that the DataLength is even + // all SATA transfers must be even + // DBC is a 0 based number (i.e. 0 is 1, 1 is 2, etc. + // sgl->Elements.Length is not (i.e. 0 is 0, 1 is 1, etc. + if ((sgl->List[i].Length & 1) == 0) { //therefore length must be even here + // 2.3 Set Datalength in the PRDT entries + cmdTable->PRDT[i].DI.DBC = sgl->List[i].Length - 1; //but it must be odd here } else if (sgl->List[i].Length <= RequestGetDataTransferLength(SlotContent->Srb)) { // Storport may send down SCSI commands with odd number of data transfer length, and it builds SGL using that transfer length value. // we use the length -1 to get as much data as we can. If the data length is over (length - 1), buffer overrun will be reported when the command is completed. @@ -973,6 +1018,7 @@ Return Values: return (ULONG)-1; } } + return sgl->NumberOfElements; } @@ -994,32 +1040,32 @@ Affected Variables/Registers: Command Header --*/ { - PAHCI_COMMAND_HEADER cmdHeader = SlotContent->CmdHeader; - PSTORAGE_REQUEST_BLOCK srb = SlotContent->Srb; - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(srb); + PAHCI_COMMAND_HEADER cmdHeader = SlotContent->CmdHeader; + PSTORAGE_REQUEST_BLOCK srb = SlotContent->Srb; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(srb); UNREFERENCED_PARAMETER(ChannelExtension); -// a. PRDTL containing the number of entries in the PRD table + // a. PRDTL containing the number of entries in the PRD table cmdHeader->DI.PRDTL = Length; -// b. CFL set to the length of the command in the CFIS area + // b. CFL set to the length of the command in the CFIS area cmdHeader->DI.CFL = 5; -// c. A bit set if it is an ATAPI command + // c. A bit set if it is an ATAPI command cmdHeader->DI.A = (srbExtension->AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) ? 1 : 0; -// d. W (Write) bit set if data is going to the device + // d. W (Write) bit set if data is going to the device cmdHeader->DI.W = (srbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0; -// e. P (Prefetch) bit optionally set (see rules in section 5.5.2) + // e. P (Prefetch) bit optionally set (see rules in section 5.5.2) //Some controllers have problems if P is set. cmdHeader->DI.P = 0; -// f. If a Port Multiplier is attached, the PMP field set to the correct Port Multiplier port. + // f. If a Port Multiplier is attached, the PMP field set to the correct Port Multiplier port. cmdHeader->DI.PMP = 0; - //Reset + // Reset cmdHeader->DI.R = Reset; cmdHeader->DI.B = 0; cmdHeader->DI.C = Reset; - //initialize the PRD byte count + // Initialize the PRD byte count cmdHeader->PRDBC = 0; cmdHeader->Reserved[0] = 0; @@ -1046,14 +1092,14 @@ Note: This routine can be called even the Port is stopped. */ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); - UCHAR pathId = 0; - UCHAR targetId = 0; - UCHAR lun = 0; - STOR_LOCK_HANDLE lockHandle = {InterruptLock, 0}; + UCHAR pathId = 0; + UCHAR targetId = 0; + UCHAR lun = 0; + STOR_LOCK_HANDLE lockHandle = { InterruptLock, 0 }; SrbGetPathTargetLun(Srb, &pathId, &targetId, &lun); - //1.0 complete Srb if no command should be sent to device. + // 1.0 complete Srb if no command should be sent to device. if (srbExtension->AtaFunction == 0) { NT_ASSERT(FALSE); // should investigate if this ASSERT fires Srb->SrbStatus = SRB_STATUS_ERROR; @@ -1062,7 +1108,7 @@ Note: This routine can be called even the Port is stopped. return TRUE; } - //1.1 Check if command processing should happen + // 1.1 Check if command processing should happen if (ChannelExtension->StartState.ChannelNextStartState == StartFailed) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; MarkSrbToBeCompleted(Srb); @@ -1070,42 +1116,81 @@ Note: This routine can be called even the Port is stopped. return TRUE; } - //2. central place for command special handling. this part cannot be put in command translation layer, as there might be internal command also needs special handling. - //2.1 central place for special handling of Enable/Disable WRITE CACHE, update persistent settings command. - if ( (srbExtension->AtaFunction == ATA_FUNCTION_ATA_COMMAND) && - (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_SET_FEATURE) ) { - //// + // 2. central place for command special handling. this part cannot be put in command translation layer, as there might be internal command also needs special handling. + // 2.1 central place for special handling of Enable/Disable WRITE CACHE, update persistent settings command. + if ((srbExtension->AtaFunction == ATA_FUNCTION_ATA_COMMAND) && + (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_SET_FEATURE)) { + if (srbExtension->TaskFile.Current.bFeaturesReg == IDE_FEATURE_DISABLE_WRITE_CACHE) { - //IDE_FEATURE_DISABLE_WRITE_CACHE => Modify Persistent Settings + // IDE_FEATURE_DISABLE_WRITE_CACHE => Modify Persistent Settings UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_ENABLE_WRITE_CACHE, IDE_FEATURE_DISABLE_WRITE_CACHE, 0, 0); } + if (srbExtension->TaskFile.Current.bFeaturesReg == IDE_FEATURE_ENABLE_WRITE_CACHE) { - //IDE_FEATURE_ENABLE_WRITE_CACHE => Modify Persistent Settings + // IDE_FEATURE_ENABLE_WRITE_CACHE => Modify Persistent Settings UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_DISABLE_WRITE_CACHE, IDE_FEATURE_ENABLE_WRITE_CACHE, 0, 0); } } - //2.2 update the Identify command for enumeration. This needs to be here waiting for PxSIG to be accessible. - if ( (srbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) && - (srbExtension->CompletionRoutine == AhciPortIdentifyDevice) && - (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_NOT_VALID) ) { - // for enumeration command, it's safe now to check the PxSIG register as starting port has been executed. - ULONG sig = 0; + // 2.2 update the Identify command for enumeration. This needs to be here waiting for PxSIG to be accessible. + if ((srbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) && + (srbExtension->CompletionRoutine == AhciPortIdentifyDevice) && + (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_NOT_VALID)) { + // For enumeration command, it's safe now to check the PxSIG register as starting port has been executed. + // Note: there is chance that the signature register is FFFFFFFFh when accessing it here because reset is in progress. + // Handle this special case in the follow checking device type code. + ULONG sig = 0; sig = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong); - if (sig == 0x101) { //ATA + if (sig == ATA_DEVICE_SIGNATURE_ATA) { srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_IDENTIFY; - } else { //ATAPI + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAta; + } else if (sig == ATA_DEVICE_SIGNATURE_ATAPI) { srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAtapi; + } else { + + ULONG ssts = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong); + RecordExecutionHistory(ChannelExtension, 0x10090020); // Unexpected signature register value + + if ((sig == 0) || ((sig == (ULONG)~0) && (ssts != (ULONG)~0))) { + + // Case 1: A value of 0 is not a valid signature. Some devices + // initially report a valid signature but soon after the PxSIG + // register becomes 0. + // Case 2: A value of FFFFFFFFh is set to signature register during reset according to AHCI 1.3.1 spec, + // while the value maybe also FFFFFFFFh when device is removed, because PCI configuration space that mapped with AHCI registers + // are FFFFFFFFh. To differentiate whether it is reset or device removed, check Px.SSTS register value(it is 0 during reset). + // + // If above situations happen, just set the identify command according to the currently known device type. + switch (ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType) { + case DeviceIsAta: + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_IDENTIFY; + break; + + case DeviceIsAtapi: + default: + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + break; + } + + } else { + + // Other devices types, such as with signature ATA_DEVICE_SIGNATURE_ENCLOSURE, or + // ATA_DEVICE_SIGNATURE_PORT_MULTIPLIER, are not supported. + // IDENTIFY PACKET DEVICE command will fail on these devices. + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceUnknown; + } } } - //2.3 this is central place to update this register value in case it's wrongly set. + // 2.3 this is central place to update this register value in case it's wrongly set. if (IsAtaCommand(srbExtension->AtaFunction)) { SetDeviceReg((&srbExtension->TaskFile.Current), 0); //make sure set to device0 } - //3 Find an available slot/tag (AHCI 1.1 Section 5.5.1) + // 3 Find an available slot/tag (AHCI 1.1 Section 5.5.1) if (!AtDIRQL) { AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockHandle); } @@ -1113,11 +1198,11 @@ Note: This routine can be called even the Port is stopped. GetAvailableSlot(ChannelExtension, Srb); //srbExtension->QueueTag will be set // 3.1 If no tag is available, reject the command to be retried later - if ( srbExtension->QueueTag > ChannelExtension->AdapterExtension->CAP.NCS ) { - //wait for 8 IO (random picked number) being completed before re-starting sending IO to miniport for this device. + if (srbExtension->QueueTag > ChannelExtension->AdapterExtension->CAP.NCS) { + // Wait for 8 IO (random picked number) being completed before re-starting sending IO to miniport for this device. // if filled up, if (!IsMiniportInternalSrb(ChannelExtension, Srb)) { - // report busy only for IOs from Storport + // Report busy only for IOs from Storport StorPortDeviceBusy(ChannelExtension->AdapterExtension, pathId, targetId, lun, min(8, ChannelExtension->AdapterExtension->CAP.NCS)); } Srb->SrbStatus = SRB_STATUS_BUSY; @@ -1129,19 +1214,19 @@ Note: This routine can be called even the Port is stopped. #ifdef DBG { - ULONG ci; - ULONG sact; + ULONG ci; + ULONG sact; ci = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI); sact = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT); // 3.2 if the tag returned can't be used, something is seriously wrong. Reject the command to be retried later - if( ( ( (1 << srbExtension->QueueTag) & ci) > 0) || ( ( (1 << srbExtension->QueueTag) & sact) > 0) ) { + if ((( (1 << srbExtension->QueueTag) & ci) > 0) || (((1 << srbExtension->QueueTag) & sact) > 0)) { if (!IsMiniportInternalSrb(ChannelExtension, Srb)) { // pause device only for IOs from Storport StorPortPauseDevice(ChannelExtension->AdapterExtension, pathId, targetId, lun, 1); //pause for 1 seconds. } // One reason why this can happen is if the adapter was removed. - if (IsAdapterRemoved(ChannelExtension)) { + if (IsAdapterRemoved(ChannelExtension->AdapterExtension)) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; } else { NT_ASSERT(FALSE); //can this happen? catch it. @@ -1179,32 +1264,32 @@ AhciFormIo( Caller: only AhciProcessIo() */ { - PAHCI_SRB_EXTENSION srbExtension; - PSLOT_CONTENT slotContent; - PAHCI_COMMAND_TABLE cmdTable; - PAHCI_COMMAND_HEADER cmdHeader; + PAHCI_SRB_EXTENSION srbExtension; + PSLOT_CONTENT slotContent; + PAHCI_COMMAND_TABLE cmdTable; + PAHCI_COMMAND_HEADER cmdHeader; STOR_PHYSICAL_ADDRESS cmdTablePhysicalAddress; - ULONG prdtLength = 0; - PCDB cdb = SrbGetCdb(Srb); + ULONG prdtLength = 0; + PCDB cdb = SrbGetCdb(Srb); srbExtension = GetSrbExtension(Srb); - // command header is allocated in ChannelExtension for all command slots, get the correct one. + // Command header is allocated in ChannelExtension for all command slots, get the correct one. cmdHeader = ChannelExtension->CommandList; cmdHeader += srbExtension->QueueTag; slotContent = &ChannelExtension->Slot[srbExtension->QueueTag]; - // 1. setup slot content and log history + // 1. setup slot content and log history slotContent->Srb = Srb; slotContent->CmdHeader = cmdHeader; - //1.1 Update FUA tracking flag if needed - if( !IsReturnResults(srbExtension->Flags) && + // 1.1 Update FUA tracking flag if needed + if (!IsReturnResults(srbExtension->Flags) && (cdb != NULL) && ((cdb->CDB10.OperationCode == SCSIOP_WRITE) || (cdb->CDB10.OperationCode == SCSIOP_WRITE16)) && (cdb->CDB10.ForceUnitAccess == 1) && - IsFuaSupported(ChannelExtension) ) { + IsFuaSupported(ChannelExtension)) { // Keep track of FUA to add it back in when the command is put in the FIS slotContent->StateFlags.FUA = TRUE; } @@ -1245,18 +1330,17 @@ AhciFormIo( cmdHistory->SrbStatus = 0; } - //already get a slot, after this point, any request completion effort needs to release the slot. + // Already get a slot, after this point, any request completion effort needs to release the slot. // just like what's done in: ReleaseSlottedCommand() - //2. Program the CFIS in the CommandTable (allocated in srbExtension) + // 2. Program the CFIS in the CommandTable (allocated in srbExtension) cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; - AhciZeroMemory((PCHAR)cmdTable, sizeof(AHCI_COMMAND_TABLE)); - if ( IsAtapiCommand(srbExtension->AtaFunction) ) { + if (IsAtapiCommand(srbExtension->AtaFunction)) { SRBtoATAPI_CFIS(ChannelExtension, slotContent); - } else if ( IsAtaCfisPayload(srbExtension->AtaFunction) ) { + } else if (IsAtaCfisPayload(srbExtension->AtaFunction)) { CfistoATA_CFIS(ChannelExtension, slotContent); - } else if ( IsAtaCommand(srbExtension->AtaFunction) ) { + } else if (IsAtaCommand(srbExtension->AtaFunction)) { SRBtoATA_CFIS(ChannelExtension, slotContent); } else { NT_ASSERT(FALSE); @@ -1266,10 +1350,8 @@ AhciFormIo( return TRUE; } - - - //3. Build the PRD Table in CommandTable. - if( IsDataTransferNeeded(slotContent->Srb) ) { + // 3. Build the PRD Table in CommandTable. + if (IsDataTransferNeeded(slotContent->Srb)) { prdtLength = SRBtoPRDT(ChannelExtension, slotContent); if (prdtLength == -1) { NT_ASSERT(FALSE); @@ -1280,22 +1362,21 @@ AhciFormIo( } } - //4. Program the Command Header (allocated in ChannelExtension for all command slots) + // 4. Program the Command Header (allocated in ChannelExtension for all command slots) SRBtoCmdHeader(ChannelExtension, slotContent, prdtLength, FALSE); - //4.1. Get the Command Table's physical address to verify the alignment and program cmdHeader->CTBA + // 4.1. Get the Command Table's physical address to verify the alignment and program cmdHeader->CTBA if ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb == Srb) { cmdTablePhysicalAddress = ChannelExtension->Local.SrbExtensionPhysicalAddress; } else if ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb == Srb) { cmdTablePhysicalAddress = ChannelExtension->Sense.SrbExtensionPhysicalAddress; } else { ULONG length; - cmdTablePhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, - NULL, - srbExtension, - &length); + // SRB is needed for StorPortGetPhysicalAddress to calculate logical address when DMAR is enabled. + cmdTablePhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, (PSCSI_REQUEST_BLOCK)Srb, srbExtension, &length); } - if ((cmdTablePhysicalAddress.LowPart % 128) == 0 ) { + + if ((cmdTablePhysicalAddress.LowPart % 128) == 0) { cmdHeader->CTBA.AsUlong = cmdTablePhysicalAddress.LowPart; } else { slotContent->Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -1303,28 +1384,29 @@ AhciFormIo( RecordExecutionHistory(ChannelExtension, 0x10080020);//Invalid PhyscialAddress alignment return TRUE; } - //If the controller supports 64 bits, write the high part too - if (ChannelExtension->AdapterExtension->CAP.S64A) { + + // If the controller supports 64 bits, write the high part too + if (ChannelExtension->AdapterExtension->CAP.S64A) { cmdHeader->CTBAU = cmdTablePhysicalAddress.HighPart; } - //5. Sort it, put request in one of IO Slices. - //5.1. Keep track of high priority outside of the Slices - if ( IsHighPriorityCommand(srbExtension->Flags) ){ + // 5. Sort it, put request in one of IO Slices. + // 5.1. Keep track of high priority outside of the Slices + if (IsHighPriorityCommand(srbExtension->Flags)) { ChannelExtension->SlotManager.HighPriorityAttribute |= ( 1 << srbExtension->QueueTag); } - //5.2. Put the slot content in the correct Slice - if ( IsNCQCommand(srbExtension) ) { + + // 5.2. Put the slot content in the correct Slice + if (IsNCQCommand(srbExtension)) { ChannelExtension->SlotManager.NCQueueSlice |= ( 1 << srbExtension->QueueTag); - } else if ( IsReturnResults(srbExtension->Flags) ) { + } else if (IsReturnResults(srbExtension->Flags)) { ChannelExtension->SlotManager.SingleIoSlice |= ( 1 << srbExtension->QueueTag); - } else if ( IsNormalCommand(slotContent->Srb) ) { + } else if (IsNormalCommand(slotContent->Srb)) { ChannelExtension->SlotManager.NormalQueueSlice |= ( 1 << srbExtension->QueueTag); } else { ChannelExtension->SlotManager.SingleIoSlice |= ( 1 << srbExtension->QueueTag); } - if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000020);//Exit AhciHwStartIo } @@ -1340,19 +1422,19 @@ BuildRequestSenseSrb( _In_ PSTORAGE_REQUEST_BLOCK FailingSrb ) { - STOR_PHYSICAL_ADDRESS dataBufferPhysicalAddress; - ULONG length; - PCDB cdb; - PSCSI_REQUEST_BLOCK senseSrb = &ChannelExtension->Sense.Srb; - PAHCI_SRB_EXTENSION srbExtension = ChannelExtension->Sense.SrbExtension; - PVOID srbSenseBuffer = NULL; - UCHAR srbSenseBufferLength = 0; + STOR_PHYSICAL_ADDRESS dataBufferPhysicalAddress; + ULONG length; + PCDB cdb; + PSCSI_REQUEST_BLOCK senseSrb = &ChannelExtension->Sense.Srb; + PAHCI_SRB_EXTENSION srbExtension = ChannelExtension->Sense.SrbExtension; + PVOID srbSenseBuffer = NULL; + UCHAR srbSenseBufferLength = 0; RequestGetSrbScsiData(FailingSrb, NULL, NULL, &srbSenseBuffer, &srbSenseBufferLength); - //1. validate if senseSrb should be used. - if ( (srbExtension == NULL) || (srbExtension->AtaFunction != 0) || - (srbSenseBuffer == NULL) || (srbSenseBufferLength == 0) ) { + // 1. Validate if senseSrb should be used. + if ((srbExtension == NULL) || (srbExtension->AtaFunction != 0) || + (srbSenseBuffer == NULL) || (srbSenseBufferLength == 0)) { // Request Sense is for ATAPI commands which are single IOs. At the same time there should be only one command failed and needs this Srb. NT_ASSERT(srbExtension && srbExtension->AtaFunction == 0); return NULL; @@ -1362,11 +1444,11 @@ BuildRequestSenseSrb( AhciZeroMemory((PCHAR)srbSenseBuffer, srbSenseBufferLength); } - //2. initialize Srb and SrbExtension structures. + // 2. Initialize Srb and SrbExtension structures. AhciZeroMemory((PCHAR)senseSrb, sizeof(SCSI_REQUEST_BLOCK)); AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); - //3. setup Srb and CDB. Note that Sense Srb uses SCSI_REQUEST_BLOCK type. + // 3. Setup Srb and CDB. Note that Sense Srb uses SCSI_REQUEST_BLOCK type. senseSrb->Length = sizeof(SCSI_REQUEST_BLOCK); senseSrb->Function = SRB_FUNCTION_EXECUTE_SCSI; senseSrb->PathId = (UCHAR)ChannelExtension->PortNumber; @@ -1383,11 +1465,12 @@ BuildRequestSenseSrb( cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE; cdb->CDB6INQUIRY.AllocationLength = (UCHAR)senseSrb->DataTransferLength; - //4. fill in the srbExtension fields + // 4. Fill in the srbExtension fields srbExtension->AtaFunction = ATA_FUNCTION_REQUEST_SENSE; srbExtension->Flags = ATA_FLAGS_DATA_IN; - dataBufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, srbSenseBuffer, &length); + // SRB is needed for StorPortGetPhysicalAddress to calculate logical address when DMAR is enabled. + dataBufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, (PSCSI_REQUEST_BLOCK)FailingSrb, srbSenseBuffer, &length); if (dataBufferPhysicalAddress.QuadPart == 0) { NT_ASSERT(FALSE); //Shall Not Pass return NULL; @@ -1411,7 +1494,7 @@ AhciPortFailAllIos( { UCHAR i; - // complete all requests in slots + // Complete all requests in slots for (i = 0; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) { if (ChannelExtension->Slot[i].Srb != NULL) { ChannelExtension->Slot[i].Srb->SrbStatus = SrbStatus; @@ -1419,7 +1502,7 @@ AhciPortFailAllIos( } } - // requests in completion queue will be completed by AhciPortSrbCompletionDpcRoutine. + // Requests in completion queue will be completed by AhciPortSrbCompletionDpcRoutine. return; } @@ -1433,12 +1516,13 @@ AhciPortSrbCompletionDpcRoutine( ) { PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)SystemArgument1; - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - PSTORAGE_REQUEST_BLOCK srb = NULL; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + PSTORAGE_REQUEST_BLOCK srb = NULL; PSRB_COMPLETION_ROUTINE completionRoutine = NULL; - BOOLEAN reservedSlotInUse = FALSE; - BOOLEAN sendCommand = FALSE; - ULONGLONG count = 0; // record the number of Srb being completed in this routine. It's for diagnostic purpose. + BOOLEAN reservedSlotInUse = FALSE; + BOOLEAN sendCommand = FALSE; + BOOLEAN sendCommandForLocalSrb = FALSE; + ULONGLONG count = 0; // record the number of Srb being completed in this routine. It's for diagnostic purpose. UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument2); @@ -1452,6 +1536,8 @@ AhciPortSrbCompletionDpcRoutine( RecordExecutionHistory(channelExtension, 0x0000001e);//Enter AhciPortSrbCompletionDpcRoutine } + StorPortQuerySystemTime(&(channelExtension->LastTimeStampDpcStart)); + // // Preserve the use status about Reserved Slot. // @@ -1463,7 +1549,7 @@ AhciPortSrbCompletionDpcRoutine( AhciInterruptSpinlockRelease(channelExtension->AdapterExtension, channelExtension->PortNumber, &lockhandle); if (srb != NULL) { - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(srb); + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(srb); BOOLEAN completeSrb = TRUE; @@ -1476,35 +1562,85 @@ AhciPortSrbCompletionDpcRoutine( completionRoutine(channelExtension, srb); - // a request with STATUS_BUS_RESET should be completed after running completion routine. - if ( (srbExtension->AtaFunction != 0) && - (!SrbShouldBeCompleted(srbExtension->Flags)) && - (srb->SrbStatus != SRB_STATUS_BUS_RESET) ) { + // A request with STATUS_BUS_RESET should be completed after running completion routine. + if ((srbExtension->AtaFunction != 0) && + (!SrbShouldBeCompleted(srbExtension->Flags)) && + (srb->SrbStatus != SRB_STATUS_BUS_RESET)) { // new command associated needs to be processed, do not complete the request. AhciProcessIo(channelExtension, srb, FALSE); - // this Srb should not be completed yet + // This Srb should not be completed yet completeSrb = FALSE; sendCommand = TRUE; + if (srb == (PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb) { + sendCommandForLocalSrb = TRUE; + } + } else if (SrbShouldBeCompleted(srbExtension->Flags)) { - // clear the flag + // Clear the flag CLRMASK(srbExtension->Flags, ATA_FLAGS_COMPLETE_SRB); } - } else { - // a Srb without completion routine should be completed. + // An Srb without completion routine should be completed. + } + + if ((srb == (PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb) && + reservedSlotInUse && + channelExtension->StateFlags.ReservedSlotInUse && + (!sendCommandForLocalSrb)) { + + // + // This should never happen. ReservedSlotInUse flag will stuck. + // + + NT_ASSERT(FALSE); + + RecordExecutionHistory(channelExtension, 0x1001001e); //AhciPortSrbCompletionDpcRoutine, ReservedSlotInUse starts to stuck + + AhciTelemetryLog(channelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress), + AHCI_TELEMETRY_DRIVER_VERSION, + AhciTelemetryEventIdReservedSlotStuck, + "Reserved Slot Stuck", + AHCI_TELEMETRY_EVENT_VERSION, + 0, + 0, + NULL, + "CommandsIssued", + channelExtension->SlotManager.CommandsIssued, + "NCQueueSlice", + channelExtension->SlotManager.NCQueueSlice, + "NormalQueueSlice", + channelExtension->SlotManager.NormalQueueSlice, + "SingleIoSlice", + channelExtension->SlotManager.SingleIoSlice, + "CommandsToComplete", + channelExtension->SlotManager.CommandsToComplete, + "StateFlags", + *(ULONGLONG *)&(channelExtension->StateFlags), + "StartState", + *(ULONGLONG *)&(channelExtension->StartState), + "Flags|Function|Status", + (ULONGLONG)((((ULONGLONG)srbExtension->Flags) << 32) | + (((ULONGLONG)srbExtension->AtaFunction) << 16) | + (ULONGLONG)(srb->SrbStatus)) + ); } if (completeSrb) { - // release active reference for port/device and adapter. + // Release active reference for port/device and adapter. if ((srbExtension->Flags & ATA_FLAGS_ACTIVE_REFERENCE) != 0) { PortReleaseActiveReference(channelExtension, srb); } if (!IsMiniportInternalSrb(channelExtension, srb)) { + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + } + NT_ASSERT(srb->SrbStatus != SRB_STATUS_PENDING); StorPortNotification(RequestComplete, AdapterExtension, srb); } @@ -1523,9 +1659,11 @@ AhciPortSrbCompletionDpcRoutine( } if (sendCommand) { - ActivateQueue(channelExtension, TRUE); + ActivateQueue(channelExtension, (IsDumpMode(channelExtension->AdapterExtension) ? (TRUE) : (FALSE))); } + StorPortQuerySystemTime(&(channelExtension->LastTimeStampDpcCompletion)); + if (LogExecuteFullDetail(channelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(channelExtension, 0x1000001e);//Exit AhciPortSrbCompletionDpcRoutine } @@ -1539,4 +1677,3 @@ AhciPortSrbCompletionDpcRoutine( #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/io.h b/storage/miniports/storahci/src/io.h index 00b3a44f..8e9df9b5 100644 --- a/storage/miniports/storahci/src/io.h +++ b/storage/miniports/storahci/src/io.h @@ -122,4 +122,3 @@ HW_DPC_ROUTINE AhciPortSrbCompletionDpcRoutine; HW_DPC_ROUTINE AhciPortBusChangeDpcRoutine; - diff --git a/storage/miniports/storahci/src/pnppower.c b/storage/miniports/storahci/src/pnppower.c index ca9b6ff6..a6b5e3d6 100644 --- a/storage/miniports/storahci/src/pnppower.c +++ b/storage/miniports/storahci/src/pnppower.c @@ -1,4 +1,4 @@ -/*++ +/*++ Copyright (C) Microsoft Corporation, 2009 @@ -19,7 +19,7 @@ Revision History: #pragma warning(push) #pragma warning(disable:26015) //26015: "Potential overflow using expression 'outParams->DriverStatus.bDriverError'. Buffer access is apparently unbounded by the buffer size. - //Output buffer cannot be checked for size. ATAport provides this validation check as the input buffer size and output buffer size are 2 of the 4 parameters passed in on the SMART IRP. Storport doesn�t do this and the miniport doesn�t get the IRP so it cannot do this for itself. This is just the condition of a legacy IOCTL. + //Output buffer cannot be checked for size. ATAport provides this validation check as the input buffer size and output buffer size are 2 of the 4 parameters passed in on the SMART IRP. Storport doesn’t do this and the miniport doesn’t get the IRP so it cannot do this for itself. This is just the condition of a legacy IOCTL. //26015: "Potential overflow using expression 'nRB->NRBStatus' Buffer access is apparently unbounded by the buffer size. //The same is true for the NVCache IOCTL. Instead of the output buffer, this time it is the NVCache_Request_Block. #pragma warning(disable:4214) // bit field types other than int @@ -79,13 +79,13 @@ It performs: 3.1 Enable the AHCI interface AHCI 1.1 Section 10.1.2 - 5. "For each implemented port, system software shall allocate memory for and program: - � PxCLB and PxCLBU (if CAP.S64A is set to �1�) - � PxFB and PxFBU (if CAP.S64A is set to �1�) - It is good practice for system software to �zero-out� the memory allocated and referenced by PxCLB and PxFB. After setting PxFB and PxFBU to the physical address of the FIS receive area, system software shall set PxCMD.FRE to �1�." + • PxCLB and PxCLBU (if CAP.S64A is set to ‘1’) + • PxFB and PxFBU (if CAP.S64A is set to ‘1’) + It is good practice for system software to ‘zero-out’ the memory allocated and referenced by PxCLB and PxFB. After setting PxFB and PxFBU to the physical address of the FIS receive area, system software shall set PxCMD.FRE to ‘1’." 3.2 Enable Interrupts on the Channel AHCI 1.1 Section 10.1.2 - 7. - "Determine which events should cause an interrupt, and set each implemented port�s PxIE register with the appropriate enables." - Note: Due to the multi-tiered nature of the AHCI HBA�s interrupt architecture, system software must always ensure that the PxIS (clear this first) and IS.IPS (clear this second) registers are cleared to �0� before programming the PxIE and GHC.IE registers. This will prevent any residual bits set in these registers from causing an interrupt to be asserted. + "Determine which events should cause an interrupt, and set each implemented port’s PxIE register with the appropriate enables." + Note: Due to the multi-tiered nature of the AHCI HBA’s interrupt architecture, system software must always ensure that the PxIS (clear this first) and IS.IPS (clear this second) registers are cleared to ‘0’ before programming the PxIE and GHC.IE registers. This will prevent any residual bits set in these registers from causing an interrupt to be asserted. 4.1 Allocate memory for the CommandList, the Receive FIS buffer and SRB Extension Now is the time to allocate memory that will be used for controller and per request structures. @@ -193,7 +193,6 @@ NOTE: as this routine is invoked from FindAdapter where the adapter might not be ChannelExtension->DeviceExtension[0].IdentifyDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->DeviceExtension[0].IdentifyDeviceData, &mappedLength); ChannelExtension->DeviceExtension[0].InquiryDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->DeviceExtension[0].InquiryData, &mappedLength); - //4.8 Setup STOR_ADDRESS for the device. StorAHCI uses Bus/Target/Lun addressing model, thus uses STOR_ADDRESS_TYPE_BTL8. // Port - not need to be set by miniport, Storport has this knowledge. miniport can get the value by calling StorPortGetSystemPortNumber(). // Path - StorAHCI reports (highest implemented port number + 1) as bus number, thus "port number" will be "Path" value. @@ -261,11 +260,11 @@ Affected Variables/Registers: GHC.AE, GHC.IE Return Values: TRUE always. ---*/ //Used to enable the AHCI interface +--*/ { - ULONG i; + ULONG i; AHCI_Global_HBA_CONTROL ghc; - PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; + PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; AdapterExtension->StateFlags.PowerDown = 0; @@ -286,9 +285,9 @@ Return Values: // Power up all ports that don't have a device present. // There is protection method in AhciPortPowerUp() to only allow it run once. for (i = 0; i <= AdapterExtension->HighestPort; i++) { - if ( (AdapterExtension->PortExtension[i] != NULL) && - (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == TRUE) && - (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) { + if ((AdapterExtension->PortExtension[i] != NULL) && + (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == TRUE) && + (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) { AhciPortPowerUp(AdapterExtension->PortExtension[i]); } } @@ -310,22 +309,22 @@ Called by: It performs: 1. Clear GHC.IE AHCI 1.1 Section 8.3.3 - "Software must disable interrupts (GHC.IE must be cleared to �0�) prior to requesting a transition of the HBA to the D3 state. This precaution by software avoids an interrupt storm if an interrupt occurs during the transition to the D3 state." + "Software must disable interrupts (GHC.IE must be cleared to ‘0’) prior to requesting a transition of the HBA to the D3 state. This precaution by software avoids an interrupt storm if an interrupt occurs during the transition to the D3 state." Affected Variables/Registers: GHC.IE Return Values: TRUE always. --*/ { - ULONG i; + ULONG i; AHCI_Global_HBA_CONTROL ghc; - PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; + PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; // Power down all ports that don't have a device present. for (i = 0; i <= AdapterExtension->HighestPort; i++) { - if ( (AdapterExtension->PortExtension[i] != NULL) && - (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == FALSE) && - (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) { + if ((AdapterExtension->PortExtension[i] != NULL) && + (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == FALSE) && + (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) { AhciPortPowerDown(AdapterExtension->PortExtension[i]); } } @@ -382,13 +381,14 @@ Return Values: if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x00000025);//AhciPortStop } - //1. Stop the channel + + //1. Stop the channel if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ) { // don't need RESET, the port will be tried to start when processing start device request RecordExecutionHistory(ChannelExtension, 0xff08); //AhciPortStop Failed } - //2. Disable Interrupt and disconnect with Port resources + //2. Disable Interrupt and disconnect with Port resources StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IE.AsUlong, 0); //disabling interrupts PortClearPendingInterrupt(ChannelExtension); @@ -432,19 +432,25 @@ Return Values: --*/ AHCI_LPM_POWER_SETTINGS userLpmSettings; - BOOLEAN portPowerDown; + BOOLEAN portPowerDown; + AHCI_INTERRUPT_STATUS pxis = { 0 }; RecordExecutionHistory(ChannelExtension, 0x00000026);//Enter AhciPortPowerUp - //1.0 Reinitialize the StateFlags. e.g. ChannelExtension->StateFlags.PowerDown = FALSE; + ++(ChannelExtension->TotalCountPowerUp); + + pxis.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IS.AsUlong); + + // 1.0 Reinitialize the StateFlags. e.g. ChannelExtension->StateFlags.PowerDown = FALSE; portPowerDown = InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 12); //StateFlags.PownDown field is at bit 12 if (portPowerDown == FALSE) { + RecordExecutionHistory(ChannelExtension, 0x00010026);//AhciPortPowerUp: port already powered up. return; } - //1.1 Reload the CLB,CBU,FLB,FBU + // 1.1 Reload the CLB,CBU,FLB,FBU StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLB.AsUlong, ChannelExtension->CommandListPhysicalAddress.LowPart); if (ChannelExtension->AdapterExtension->CAP.S64A) { //If the controller supports 64 bits, write high part StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLBU, ChannelExtension->CommandListPhysicalAddress.HighPart); @@ -454,26 +460,46 @@ Return Values: StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FBU, ChannelExtension->ReceivedFisPhysicalAddress.HighPart); } - // // If D3 Cold is enabled and we are being powered up from D3, we need to be // a bit heavy-handed with powering up the port due to loss of context. // if (ChannelExtension->DevicePowerState == StorPowerDeviceD3 && IsPortD3ColdEnabled(ChannelExtension)) { - + + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + BOOLEAN powerUpInitializationInProgress = 0; + // 1.4.1 Re-issue init commands (this will also restore preserved settings). AhciPortIssueInitCommands(ChannelExtension); - + + // Set PowerUpInitializationInProgress flag, which will be cleared when preserved settings command done. + powerUpInitializationInProgress = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 22); //PowerUpInitializationInProgress field is at bit 22 + + if (powerUpInitializationInProgress == 1) { + + // PowerUpInitializationInProgress flag was not cleared properly, which is not expected to happen, but not a fatal issue. + RecordExecutionHistory(ChannelExtension, 0x10020026); // AhciPortPowerUp: PowerUpInitializationInProgress flag was not cleared properly. + } + // 1.4.2 Restore LPM settings userLpmSettings.AsUlong = ChannelExtension->LastUserLpmPowerSetting; AhciLpmSettingsModes(ChannelExtension, userLpmSettings); //ignore the returned value, IO will be restarted anyway. - // 1.5 Start the channel by issuing a reset to restore PHY communication. + AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); AhciPortReset(ChannelExtension, FALSE); - + AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); + } else { + // + // If there is change in Current Connect Status(PCS:1), then QDR is needed. + // + if ((pxis.PCS == 1) && + (ChannelExtension->StartState.ChannelNextStartState == StartFailed)) { + ChannelExtension->StateFlags.NeedQDR = TRUE; + } + // 1.4.1 Restore Preserved Settings if (NeedToSetTransferMode(ChannelExtension)) { RestorePreservedSettings(ChannelExtension, FALSE); @@ -484,6 +510,7 @@ Return Values: } RecordExecutionHistory(ChannelExtension, 0x10000026);//Exit AhciPortPowerUp + return; } @@ -514,6 +541,8 @@ Return Values: --*/ ChannelExtension->StateFlags.PowerDown = TRUE; + ++(ChannelExtension->TotalCountPowerDown); + // // Cancel the StartPortTimer since we're going into a lower power state. // @@ -528,6 +557,7 @@ Return Values: // the link will be inactive, ignore the hot plug noise. ChannelExtension->StateFlags.IgnoreHotplugInterrupt = TRUE; } + } RecordExecutionHistory(ChannelExtension, 0x10000027);//Exit AhciPortPowerDown @@ -564,7 +594,6 @@ ReportLunsComplete( return; } - Srb->SrbStatus = SRB_STATUS_SUCCESS; SrbSetScsiStatus(Srb, SCSISTAT_GOOD); @@ -609,24 +638,181 @@ ReportLunsComplete( return; } +__inline +VOID +GetLogInfoRegValueName( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _Out_writes_(ValueNameLength) PCHAR ValueName, + _In_ ULONG ValueNameLength + ) +/*++ + +Routine Description: + + This function append Port Number to "LogPageInfo" as name of registry value. + This is a work around as StorAHCI doesn't have access to RtlStringCbPrintfA(). + +Arguments: + + ChannelExtension - Pointer to the device extension for channel. + + ValueName - Registry name to be written. + + ValueNameLength - Registry name length. + +Return Value: + + None. + +--*/ +{ + NT_ASSERT(ChannelExtension->PortNumber <= 255); + + if (ValueNameLength >= 14) { + + ULONG portNumber = ChannelExtension->PortNumber; + ULONG remainder = 0; + + StorPortCopyMemory(ValueName, "LogPageInfo", 12); + ValueName[13] = '\0'; + + remainder = portNumber % 16; // use HEX value, base is 16. + ValueName[12] = (CHAR)((remainder < 10) ? (remainder + '0') : (remainder - 10 + 'A')); + + portNumber /= 16; + remainder = portNumber % 16; + ValueName[11] = (CHAR)((remainder < 10) ? (remainder + '0') : (remainder - 10 + 'A')); + } + + return; +} + +_Function_class_(HW_WORKITEM) +VOID +AhciRegistryWriteWorker( + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension, + _In_ PVOID Context, + _In_ PVOID WorkItem + ) +/*++ + +Routine Description: + + This function runs in the context of a work item. It performs writting registry with data + from Log Pages that needs to be cached. + +Arguments: + + AdapterExtension - Pointer to the device extension for adapter. + + Context - The AHCI_CHANNEL_EXTENSION we specified when we queued this work item. + + WorkItem - The work item object. + +Return Value: + + None. + +--*/ +{ + PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)Context; + ULONG storStatus; + CHAR valueName[16] = { 0 }; + AHCI_DEVICE_LOG_PAGE_INFO logPageInfo = { 0 }; + + NT_ASSERT(Context != NULL); + NT_ASSERT(WorkItem != NULL); + + GetLogInfoRegValueName(channelExtension, valueName, sizeof(valueName)); + + StorPortCopyMemory((PVOID)&logPageInfo.QueryLogPages, &channelExtension->DeviceExtension[0].QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY)); + StorPortCopyMemory((PVOID)&logPageInfo.SupportedGPLPages, &channelExtension->DeviceExtension[0].SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES)); + StorPortCopyMemory((PVOID)&logPageInfo.SupportedCommands, &channelExtension->DeviceExtension[0].SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED)); + StorPortCopyMemory((PVOID)&logPageInfo.FirmwareUpdate, &channelExtension->DeviceExtension[0].FirmwareUpdate, sizeof(DOWNLOAD_MICROCODE_CAPABILITIES)); + + storStatus = StorPortRegistryWriteAdapterKey(AdapterExtension, + (PUCHAR)"StorAHCI", + (PUCHAR)valueName, + MINIPORT_REG_BINARY, + &logPageInfo, + sizeof(AHCI_DEVICE_LOG_PAGE_INFO)); + + if (storStatus == STOR_STATUS_SUCCESS) { + channelExtension->DeviceExtension[0].UpdateCachedLogPageInfo = FALSE; + } else { + NT_ASSERT(FALSE); + } + + // + // Call the callback routine if there is one. + // + + if (WorkItem != NULL) { + StorPortFreeWorker(AdapterExtension, WorkItem); + } + + return; +} + +VOID +PreserveLogPageInformation( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension + ) +/*++ + + This routine allocates a work item and schedule it for saving information retrieved from Log Pages. + +Parameters: + + ChannelExtension - port and device that log pages are retrieved from. + +Return Values: + + None + +--*/ +{ + ULONG storStatus; + PVOID workItem = NULL; + + storStatus = StorPortInitializeWorker(ChannelExtension->AdapterExtension, &workItem); + + if (storStatus == STOR_STATUS_SUCCESS) { + storStatus = StorPortQueueWorkItem(ChannelExtension->AdapterExtension, AhciRegistryWriteWorker, workItem, ChannelExtension); + } + + NT_ASSERT(storStatus == STOR_STATUS_SUCCESS); + + if ((storStatus != STOR_STATUS_SUCCESS) && (workItem != NULL)) { + // Free the work item as it cannot be scheduled to run. + StorPortFreeWorker(ChannelExtension->AdapterExtension, workItem); + } + + return; +} + VOID -InitQueryLogPages ( +InitQueryLogPages( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) /*++ + Initialize Log Pages to Read. Note that this functions should only be called after Identify Device Data is retrieved. Parameters: - ChannelExtension - port that log-pages-to-query should be inited. + + ChannelExtension - port that log-pages-to-query should be inited. Return Values: + None --*/ { PUSHORT index = &ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount; + // // Log Page only applies to ATA device; General Purpose Logging feature should be supported; // 48bit command should be supported as READ LOG EXT is a 48bit command. @@ -637,7 +823,7 @@ Return Values: AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY)); - // read Log Directory + // Read Log Directory ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DIRECTORY_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0; @@ -645,7 +831,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Device Statistics log - supported page + // Read Device Statistics log - supported page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_SUPPORTED_PAGES; @@ -653,7 +839,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Device Statistics log - general page + // Read Device Statistics log - general page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE; @@ -661,7 +847,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Identify Device Data log - supported page + // Read Identify Device Data log - supported page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_SUPPORTED_PAGES; @@ -669,7 +855,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Identify Device Data log - Supported Capabilities page + // Read Identify Device Data log - Supported Capabilities page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE; @@ -677,7 +863,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Identify Device Data log - SATA page + // Read Identify Device Data log - SATA page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE; @@ -685,7 +871,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Saved Device Internal log + // Read Saved Device Internal log ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_SAVED_DEVICE_INTERNAL_STATUS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0; @@ -693,7 +879,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read NCQ non-Data log + // Read NCQ non-Data log if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NcqQueueMgmt == 1)) { ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; @@ -706,7 +892,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read NCQ Send Receive log + // Read NCQ Send Receive log if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NcqReceiveSend == 1)) { ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; @@ -788,18 +974,20 @@ Return Values: Srb->SrbStatus = SRB_STATUS_PENDING; srbExtension->DataBuffer = DataBuffer; + if ( PhysicalAddress ) { + srbExtension->DataBufferPhysicalAddress.QuadPart = PhysicalAddress->QuadPart; + } //setup SGL if ( PhysicalAddress ) { srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = PhysicalAddress->LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = PhysicalAddress->HighPart; - srbExtension->LocalSgl.List[0].Length = ATA_BLOCK_SIZE * BlockCount; + srbExtension->LocalSgl.List[0].Length = ATA_BLOCK_SIZE * (ULONG)BlockCount; srbExtension->Sgl = &srbExtension->LocalSgl; - srbExtension->DataTransferLength = ATA_BLOCK_SIZE * BlockCount; + srbExtension->DataTransferLength = ATA_BLOCK_SIZE * (ULONG)BlockCount; } - return; } @@ -845,15 +1033,15 @@ ReadQueryLogPage ( ) { IssueReadLogExtCommand( ChannelExtension, - Srb, - ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].LogAddress, - ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].PageNumber, - ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].BlockCount, - ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].FeatureField, - &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress, - (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData, - LogPageDiscoveryCompletion - ); + Srb, + ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].LogAddress, + ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].PageNumber, + ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].BlockCount, + ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].FeatureField, + &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress, + (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData, + LogPageDiscoveryCompletion + ); } @@ -893,6 +1081,168 @@ Return Values: return; } +VOID +UpdateDownloadMicrocodeSupport( + _Inout_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PIDENTIFY_DEVICE_DATA_LOG_PAGE_SUPPORTED_CAPABILITIES Capabilities +) +/*++ + +Routine Description: + + Takes the given Supported Capabilities log page and updates the firmware + update (download microcode) support information that is cached in the given + Channel Extension. + +Arguments: + + Channel Extension + Supported Capabilities + +--*/ +{ + NT_ASSERT(Capabilities->Header.RevisionNumber == IDE_GP_LOG_VERSION); + NT_ASSERT(Capabilities->Header.PageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE); + + // + // Set it to unsupported by default. + // + ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported = 0; + + // + // If the Download Microcode capabilites are valid then fill in our cached copy. + // + if (Capabilities->DownloadMicrocodeCapabilities.Valid == 1) { + + if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.DownloadMicrocodeDmaSupported == 1) || + (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetSupport.DownloadMicrocode == 1)) { + + ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported = (Capabilities->DownloadMicrocodeCapabilities.DmOffsetsDeferredSupported == 1); + + if (ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported) { + + if ((Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize > 0) && + (Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize < 0xFFFF)) { + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE); + } else { + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = 1; + } + + if ((Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize > 0) && + (Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize < 0xFFFF)) { + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE); + } else { + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE; + } + } + } + } +} + +VOID +GetDownloadMicrocodeSupportCompletion( + _Inout_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb +) +/*++ + +Routine Description: + + Called when the Supported Capabilities log page has been obtained from the + device. The log page is parsed for the Download Microcode support and the + cached copy of this data is updated in the ChannelExtension. + +Arguments: + + Channel Extension + SRB + +--*/ +{ + PAHCI_SRB_EXTENSION srbExtension; + + srbExtension = GetSrbExtension(Srb); + + // + // If the request succeeded then update the cached copy of the Download + // Microcode support information. + // + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + UpdateDownloadMicrocodeSupport(ChannelExtension, srbExtension->DataBuffer); + } + + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); + + // + // The SRB will be completed after this completion routine returns so + // there's no need to do it here. + // +} + +ULONG +GetDownloadMicrocodeSupport( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb +) +/*++ +Routine Description: + + Queries the Download Microcode support from the Identify Log Page and + updates our cached copy of it. + +Arguments: + ChannelExtension + SRB + +Return Value: + + Status code. + +--*/ +{ + ULONG status = STOR_STATUS_SUCCESS; + PVOID buffer = NULL; + STOR_PHYSICAL_ADDRESS bufferPhysicalAddress = { 0 }; + + // + // Fail the request if it's not supported by device. + // + if (IsDeviceGeneralPurposeLoggingSupported(ChannelExtension) == FALSE) { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + return STOR_STATUS_INVALID_PARAMETER; + } + + // + // Allocate DMA buffer to the log page. + // + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, (PVOID*)&buffer, &bufferPhysicalAddress); + + if ((status != STOR_STATUS_SUCCESS) || (buffer == NULL)) { + if (buffer != NULL) { + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, buffer, bufferPhysicalAddress); + } + Srb->SrbStatus = SRB_STATUS_ERROR; + return STOR_STATUS_INSUFFICIENT_RESOURCES; + } + + AhciZeroMemory((PCHAR)buffer, IDE_GP_LOG_SECTOR_SIZE); + + // + // Issue the command. The completion routine will update the cached + // firmware update information with the info from the log page. + // The completion routine will also free the DMA buffer. + // + IssueReadLogExtCommand(ChannelExtension, + Srb, + IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, + IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE, + 1, + 0, // feature field + &bufferPhysicalAddress, + buffer, + GetDownloadMicrocodeSupportCompletion); + return status; +} VOID LogPageDiscoveryCompletion ( @@ -903,7 +1253,6 @@ LogPageDiscoveryCompletion ( This process is to discover all needed information from general log pages. The process is initiated by reading log directory when Identify Device command completes. - */ { USHORT nextPageIndex = ATA_GPL_PAGES_INVALID_INDEX; @@ -957,7 +1306,6 @@ LogPageDiscoveryCompletion ( ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.HybridInfo = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_HYBRID_INFO_ADDRESS] > 0) ? 1 : 0; - } else { // Log Directory can be optional. Preset supportive info, they will be updated if the actual command fails later. // In case of the disk doesn't support NCQ and doesn't support Log Directory, still try to discover some log pages. @@ -1070,31 +1418,10 @@ LogPageDiscoveryCompletion ( // The value of revision number word shall be 0001h. if ((supportedCapabilities->Header.RevisionNumber == IDE_GP_LOG_VERSION) && - (supportedCapabilities->Header.PageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE) && - (supportedCapabilities->DownloadMicrocodeCapabilities.Valid == 1)) { + (supportedCapabilities->Header.PageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE)) { - if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.DownloadMicrocodeDmaSupported == 1) || - (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetSupport.DownloadMicrocode == 1)) { - - ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported = (supportedCapabilities->DownloadMicrocodeCapabilities.DmOffsetsDeferredSupported == 1); - - if (ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported) { - - if ((supportedCapabilities->DownloadMicrocodeCapabilities.DmMinTransferSize > 0) && - (supportedCapabilities->DownloadMicrocodeCapabilities.DmMinTransferSize < 0xFFFF)) { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(supportedCapabilities->DownloadMicrocodeCapabilities.DmMinTransferSize, AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE); - } else { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = 1; - } - - if ((supportedCapabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize > 0) && - (supportedCapabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize < 0xFFFF)) { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(supportedCapabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize, AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE); - } else { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE; - } - } - } + UpdateDownloadMicrocodeSupport(ChannelExtension, supportedCapabilities); + } } else { @@ -1105,6 +1432,7 @@ LogPageDiscoveryCompletion ( // the issued command was for getting SATA log page of identify device data log if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + } else { ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SATA = 0; } @@ -1153,7 +1481,11 @@ LogPageDiscoveryCompletion ( if (nextPageIndex != ATA_GPL_PAGES_INVALID_INDEX) { ReadQueryLogPage(ChannelExtension, Srb, nextPageIndex); } else { + ReportLunsComplete(ChannelExtension, Srb); + + // Use a work itme to preserve information from Log Pages into registry. + PreserveLogPageInformation(ChannelExtension); } return; @@ -1161,6 +1493,49 @@ LogPageDiscoveryCompletion ( VOID +UpdateDeviceType( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension + ) +/*++ + +Routine Description: + + Update device type information after device enumeration. + + Assumption - PxSIG is ready to be accessed. + +Arguments: + + ChannelExtension + +Return Value: + + None. + +--*/ +{ + PATA_DEVICE_PARAMETERS deviceParameters = &ChannelExtension->DeviceExtension->DeviceParameters; + + // There is chance that during device enumeration, port is not started yet and signature register is 0xffffffff, + // which will result in the AtaDeviceType is set to DeviceUnknown. + // Update it here if above situation is true. + if (IsUnknownDevice(deviceParameters)) { + + ULONG sig = 0; + sig = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong); + + if (sig == ATA_DEVICE_SIGNATURE_ATA) { + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAta; + } else if (sig == ATA_DEVICE_SIGNATURE_ATAPI) { + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAtapi; + } else { + NT_ASSERT(FALSE); + } + } +} + + +VOID AhciPortIdentifyDevice( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb @@ -1175,18 +1550,16 @@ AhciPortIdentifyDevice( return; } - if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { - if (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_ATAPI_IDENTIFY) { - ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType = DeviceIsAtapi; - } else { - ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType = DeviceIsAta; - } - } else if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE) { + if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE) { // command failed consider as no device ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType = DeviceNotExist; } if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + + // Update device ata type if it isn't initialized correctly. + UpdateDeviceType(ChannelExtension); + //Re-initialize device specific information to avoid the values being reused after device switched. ChannelExtension->StateFlags.NCQ_Activated = 0; ChannelExtension->StateFlags.NCQ_Succeeded = 0; @@ -1201,7 +1574,6 @@ AhciPortIdentifyDevice( // identify completes, digest identify data / inquiry data UpdateDeviceParameters(ChannelExtension); - // // Cache if PUIS is enabled or not. We'll look at this later when // powering up the port to determine if we need to send the spin up @@ -1212,34 +1584,85 @@ AhciPortIdentifyDevice( // Initialize port properties ChannelExtension->PortProperties = 0; + if (ChannelExtension->StateFlags.IdentifyDeviceSuccess == 0) { + ChannelExtension->StateFlags.IdentifyDeviceSuccess = 1; + } + if (IsExternalPort(ChannelExtension)) { SETMASK(ChannelExtension->PortProperties, PORT_PROPERTIES_EXTERNAL_PORT); } } // Identify Device can only be triggered from REPORT LUNS command or - // INQUIRY command (for disk in dump environment) + // INQUIRY command (for disk in dump environment) if ((cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS)) { - if ( (Srb->SrbStatus == SRB_STATUS_SUCCESS) && - IsDeviceGeneralPurposeLoggingSupported(ChannelExtension) ) { + BOOLEAN cachedLogPageInfoUsable = FALSE; + + if ((Srb->SrbStatus == SRB_STATUS_SUCCESS) && + IsDeviceGeneralPurposeLoggingSupported(ChannelExtension)) { - // READ LOG EXT command is supported. - USHORT index; + ULONG storStatus; - InitQueryLogPages(ChannelExtension); + // + // Check to determine whether use cached device settings. + // + storStatus = StorPortIsDeviceOperationAllowed(ChannelExtension->AdapterExtension, + NULL, + &STORPORT_DEVICEOPERATION_CACHED_SETTINGS_INIT_GUID, + (PULONG)(&cachedLogPageInfoUsable)); + + cachedLogPageInfoUsable = (cachedLogPageInfoUsable && (ChannelExtension->DeviceExtension[0].UpdateCachedLogPageInfo == FALSE)); + + if (cachedLogPageInfoUsable) { + // In case this is not a newly plugged in device and it's allowed to use cached Log Page Information. + CHAR valueName[16] = { 0 }; + AHCI_DEVICE_LOG_PAGE_INFO logPageInfo = { 0 }; + PVOID dataBuffer = &logPageInfo; + ULONG dataLength = sizeof(AHCI_DEVICE_LOG_PAGE_INFO); + + GetLogInfoRegValueName(ChannelExtension, valueName, sizeof(valueName)); - index = GetNextQueryLogPageIndex(ChannelExtension); + storStatus = StorPortRegistryReadAdapterKey(ChannelExtension->AdapterExtension, + (PUCHAR)"StorAHCI", + (PUCHAR)valueName, + MINIPORT_REG_BINARY, + &dataBuffer, + &dataLength); - NT_ASSERT(index == 0); + if ((storStatus == STOR_STATUS_SUCCESS) && (dataLength == sizeof(AHCI_DEVICE_LOG_PAGE_INFO))) { - if (index != ATA_GPL_PAGES_INVALID_INDEX) { - //First page should be log directory. Read it to get pages supported by device. - ReadQueryLogPage(ChannelExtension, Srb, index); - } else { - ReportLunsComplete(ChannelExtension, Srb); + StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].QueryLogPages, &logPageInfo.QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY)); + StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].SupportedGPLPages, &logPageInfo.SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES)); + StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].SupportedCommands, &logPageInfo.SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED)); + StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].FirmwareUpdate, &logPageInfo.FirmwareUpdate, sizeof(DOWNLOAD_MICROCODE_CAPABILITIES)); + + ReportLunsComplete(ChannelExtension, Srb); + } else { + cachedLogPageInfoUsable = FALSE; + } } + // + // Start log page discovery process if decide not use cached log page data. + // + if (!cachedLogPageInfoUsable) { + + USHORT index; + + InitQueryLogPages(ChannelExtension); + + index = GetNextQueryLogPageIndex(ChannelExtension); + + NT_ASSERT(index == 0); + + if (index != ATA_GPL_PAGES_INVALID_INDEX) { + // First page should be log directory. Read it to get pages supported by device. + ReadQueryLogPage(ChannelExtension, Srb, index); + } else { + ReportLunsComplete(ChannelExtension, Srb); + } + } } else { ReportLunsComplete(ChannelExtension, Srb); } @@ -1267,7 +1690,28 @@ AhciPortIdentifyDevice( } else { InquiryComplete(ChannelExtension, Srb); } + } else if ((ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData == 1) && + (cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_INQUIRY)) { + + // + // We are refreshing Identify information because of a firmware update. + // The firmware update support information is contained in the Supported + // Capabilities log page so we need to make sure we query that page as well. + // + ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData = 0; + + // + // Finish processing the Inquiry command before re-using the SRB to get + // the Supported Capabilities log page. + // + InquiryComplete(ChannelExtension, Srb); + + if (IsDeviceGeneralPurposeLoggingSupported(ChannelExtension)) { + GetDownloadMicrocodeSupport(ChannelExtension, Srb); + } + } + return; } @@ -1317,7 +1761,7 @@ AhciPortNVCacheCompletion( // // Free the buffer allocated as mode sense info buffer , holding task file // - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, srbExtension->ResultBufferLength, TaskFile); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, srbExtension->ResultBufferLength, TaskFile, srbExtension->ResultBufferPhysicalAddress); } else { // in case TaskFile is not returned in SenseInfoBuffer, use cached ATA Status and Error register values. @@ -1408,7 +1852,7 @@ Affected Variables/Registers: srb->SrbExtension = (PVOID)ChannelExtension->Local.SrbExtension; srb->TimeOutValue = 1; //as it's sent by miniport, no one monitors the timeout value. - // Fills in the local SRB with the SetFeatures command + // Fills in the local SRB with the SetFeatures command srbExtension = ChannelExtension->Local.SrbExtension; AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); @@ -1448,21 +1892,25 @@ Affected Variables/Registers: none --*/ { - UCHAR i; - ULONG allocated; - ATA_TASK_FILE taskFile = {0}; + UCHAR i; + ULONG allocated; + ATA_TASK_FILE taskFile = {0}; UNREFERENCED_PARAMETER(Srb); - //1 Verify local SRB is not in use + RecordExecutionHistory(ChannelExtension, 0x00000043); // IssuePreservedSettingCommands + + //1 Verify local SRB is not in use allocated = GetOccupiedSlots(ChannelExtension); if ((allocated & (1 << 0)) > 0) { + // Already restoring preserved Settings + RecordExecutionHistory(ChannelExtension, 0x10010043); // IssuePreservedSettingCommands Slot 0 in use return; } - //2 find the next command to send + //2 find the next command to send for (i = 0; i < MAX_SETTINGS_PRESERVED; i++) { if ( (ChannelExtension->PersistentSettings.SlotsToSend & (1 << i)) > 0 ) { ChannelExtension->PersistentSettings.SlotsToSend &= ~(1 << i); @@ -1470,20 +1918,23 @@ Affected Variables/Registers: } } - //perhaps there is none. Done. + // Perhaps there is none. Done. if ( i >= MAX_SETTINGS_PRESERVED) { - // release active reference for process of restore preserved settings + // Release active reference for process of restore preserved settings if (ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced == 1) { PortReleaseActiveReference(ChannelExtension, NULL); ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced = 0; } - InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3 + RecordExecutionHistory(ChannelExtension, 0x10020043); // IssuePreservedSettingCommands done and clear flag + + InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3 + InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 22); //PowerUpInitializationInProgress field is at bit 22 return; } - //3 Otherwise use the LocalSRB to send the command. When it is done, call this routine again + //3 Otherwise use the LocalSRB to send the command. When it is done, call this routine again taskFile.Current.bFeaturesReg = ChannelExtension->PersistentSettings.CommandParams[i].Features; taskFile.Current.bSectorCountReg = ChannelExtension->PersistentSettings.CommandParams[i].SectorCount; taskFile.Current.bDriveHeadReg = 0xA0; @@ -1521,22 +1972,29 @@ Affected Variables/Registers: UNREFERENCED_PARAMETER(Srb); - // Verify local SRB is not in use + RecordExecutionHistory(ChannelExtension, 0x00000044); // IssueInitCommands + + // Verify local SRB is not in use allocated = GetOccupiedSlots(ChannelExtension); if ((allocated & (1 << 0)) > 0) { + // Already restoring preserved Settings + RecordExecutionHistory(ChannelExtension, 0x10010044); // IssueInitCommands slot 0 in use return; } - // if all Init commands have been sent, send Preserved Setting Commands + // if all Init commands have been sent, send Preserved Setting Commands if (ChannelExtension->DeviceInitCommands.CommandToSend >= ChannelExtension->DeviceInitCommands.ValidCommandCount) { + + RecordExecutionHistory(ChannelExtension, 0x10020044); // IssueInitCommands init commands done, send Preserved Setting Commands + ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots; IssuePreservedSettingCommands(ChannelExtension, NULL); return; } - // find the next command to send + // find the next command to send taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + ChannelExtension->DeviceInitCommands.CommandToSend; taskFile->Current.bDriveHeadReg = 0xA0; @@ -1824,7 +2282,6 @@ SetAllowedLpmStates( lpm = 0x03; } - //Set PxSCTL.IPM to 3h to restrict slumber and partial interface power management state transitions. sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong); sctl.IPM = lpm; @@ -1846,14 +2303,16 @@ Return Value: */ { AHCI_COMMAND cmd; - BOOLEAN needStartIo = FALSE; + BOOLEAN needStartIo = FALSE; + UCHAR sctlIpm = 0; + + RecordExecutionHistory(ChannelExtension, 0x00000047); // AhciLpmSettingsModes //Make sure the configuration supports LPM, otherwise, don't touch anything. if (NoLpmSupport(ChannelExtension) || !IsLPMCapablePort(ChannelExtension)) { return needStartIo; } - ChannelExtension->LastUserLpmPowerSetting = (UCHAR)LpmMode.AsUlong; if (LpmMode.AsUlong == 0) { @@ -1869,7 +2328,7 @@ Return Value: } //Set PxSCTL.IPM to 3h to restrict slumber and partial interface power management state transitions. - SetAllowedLpmStates(ChannelExtension); + sctlIpm = SetAllowedLpmStates(ChannelExtension); // Disable DIPM if (IsDeviceSupportsDIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData)) { @@ -1890,7 +2349,7 @@ Return Value: // link power management is allowed. //Set PxSCTL.IPM for LPM allowed states. - UCHAR sctlIpm = SetAllowedLpmStates(ChannelExtension); + sctlIpm = SetAllowedLpmStates(ChannelExtension); // Setting HIPM if it's enabled. if ( (LpmMode.HipmEnabled > 0) && @@ -1954,6 +2413,18 @@ Return Value: } + ++(ChannelExtension->TotalCountPowerSettingNotification); + AhciTelemetryLogPowerSettingChange(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdLpmSettingsModes, + "AhciLpmSettingsModes", + 0, + sctlIpm, + LpmMode.AsUlong + ); + + RecordExecutionHistory(ChannelExtension, 0x10000047); // AhciLpmSettingsModes Exit + return needStartIo; } @@ -1963,7 +2434,7 @@ AhciPortPowerSettingNotification( IN PAHCI_CHANNEL_EXTENSION ChannelExtension, IN PSTOR_POWER_SETTING_INFO PowerInfo ) -{ +{ // do nothing if there is no device connected if ( (ChannelExtension->StartState.ChannelNextStartState == StartFailed) || (ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceNotExist) ) { @@ -1991,10 +2462,22 @@ AhciPortPowerSettingNotification( ULONG interval = (ULONG)*((PULONG)PowerInfo->Value); if (interval <= 300000) { + UCHAR sctlIpm = 0; + ChannelExtension->AutoPartialToSlumberInterval = interval; //Set PxSCTL.IPM register for LPM allowed states. - SetAllowedLpmStates(ChannelExtension); + sctlIpm = SetAllowedLpmStates(ChannelExtension); + + ++(ChannelExtension->TotalCountPowerSettingNotification); + AhciTelemetryLogPowerSettingChange(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdLpmAdaptiveSetting, + "LpmAdaptiveSetting", + 0, + sctlIpm, + ChannelExtension->AutoPartialToSlumberInterval + ); } } else if (IsLpmModeSetting(PowerInfo)) { @@ -2184,7 +2667,6 @@ AhciPortGetInitCommands( // send SECURE_FREEZE_LOCK by default BOOLEAN sendSecureFreezeLock = TRUE; - // clear Init Commands area. need to do this for device removed previously (StorAHCI only knows about adapter removal, not device removal) ChannelExtension->DeviceInitCommands.CommandCount = 0; ChannelExtension->DeviceInitCommands.ValidCommandCount = 0; @@ -2258,7 +2740,6 @@ AhciPortGetInitCommands( // Get Init Command count for devices - // // calculate possible command count for memory allocation // an IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON command will be sent to device anyway. @@ -2529,6 +3010,7 @@ AhciAdapterEvaluateDSMMethod( AdapterExtension->StateFlags.SupportsAcpiDSM = TRUE; } + } Exit: @@ -2639,4 +3121,3 @@ Exit: #pragma warning(pop) // un-sets any local warning changes - diff --git a/storage/miniports/storahci/src/pnppower.h b/storage/miniports/storahci/src/pnppower.h index 366c6bb5..ad5e1253 100644 --- a/storage/miniports/storahci/src/pnppower.h +++ b/storage/miniports/storahci/src/pnppower.h @@ -41,6 +41,8 @@ Revision History: #define ACPI_METHOD_DSM_LINKPOWER_REMOVE_POWER 0x00 #define ACPI_METHOD_DSM_LINKPOWER_APPLY_POWER 0x01 +#define AHCI_DEVICE_IO_COALESCING_IDLE_TIMEOUT_MS (1000) // 1s by default + // // When waiting for the link to change power states, don't wait more // than 100 microseconds. @@ -130,7 +132,6 @@ AhciPortAcpiDSMControl( _In_ BOOLEAN Sleep ); - VOID IssuePreservedSettingCommands( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, @@ -178,4 +179,3 @@ AhciLpmSettingsModes( #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/util.c b/storage/miniports/storahci/src/util.c index e7ed0cba..b95c682c 100644 --- a/storage/miniports/storahci/src/util.c +++ b/storage/miniports/storahci/src/util.c @@ -1,4 +1,4 @@ -/*++ +/*++ Copyright (C) Microsoft Corporation, 2009 @@ -55,14 +55,23 @@ ReturnValue: abar = NULL; - // loop over resource entries + // loop over resource entries if (ConfigInfo->NumberOfAccessRanges > 0) { for (i = 0; i < ConfigInfo->NumberOfAccessRanges; i++) { -#if defined (_ARM_) || defined (_ARM64_) - if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart != 0) { -#else - if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart == AdapterExtension->AhciBaseAddress) { -#endif + BOOLEAN bAccessRangeFound = FALSE; + + // Test to see if this is the needed resource entry, depending on the interface type + if (ConfigInfo->AdapterInterfaceType == PCIBus) { + if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart == AdapterExtension->AhciBaseAddress) { + bAccessRangeFound = TRUE; + } + } else { + if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart != 0) { + bAccessRangeFound = TRUE; + } + } + + if (bAccessRangeFound) { abar = (PAHCI_MEMORY_REGISTERS)StorPortGetDeviceBase(AdapterExtension, ConfigInfo->AdapterInterfaceType, ConfigInfo->SystemIoBusNumber, @@ -104,9 +113,11 @@ Return Value: none --*/ { - int i; + if (ChannelExtension->ExecutionHistory == NULL) { + return; + } - //1.1 Select the next available index + //1.1 Select the next available index // Actually, ExecutionHistoryNextAvailableIndex is used for current index. if (ChannelExtension->ExecutionHistoryNextAvailableIndex >= (MAX_EXECUTION_HISTORY_ENTRY_COUNT - 1)) { ChannelExtension->ExecutionHistoryNextAvailableIndex = 0; @@ -114,10 +125,12 @@ Return Value: ChannelExtension->ExecutionHistoryNextAvailableIndex++; } - //2.1 Copy over the Channel Extension header + StorPortQuerySystemTime(&(ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].TimeStamp)); + + //2.1 Copy over the Channel Extension header ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Function = Function; - if(ChannelExtension->AdapterExtension->IS) { + if (ChannelExtension->AdapterExtension->IS) { // Keep using the old field "IS" to save StateFlags information. StorPortCopyMemory(&ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].IS, &ChannelExtension->StateFlags, sizeof(ULONG)); } else { @@ -126,8 +139,8 @@ Return Value: StorPortCopyMemory(&ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].SlotManager, &ChannelExtension->SlotManager, sizeof(SLOT_MANAGER)); - //2.2 Copy over the AHCI registers - if(ChannelExtension->Px) { + //2.2 Copy over the AHCI registers + if (ChannelExtension->Px) { ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[0] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLB.AsUlong); ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[1] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLBU); ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[2] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FB.AsUlong); @@ -149,7 +162,7 @@ Return Value: ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[15] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SNTF.AsUlong); } else { - for(i=0; i<0x10; i++) { + for(int i = 0; i < 0x10; i++) { ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[i] = (ULONG)~0; } } @@ -178,7 +191,11 @@ Return Value: none --*/ { - //1. Select the next available index + if (ChannelExtension->ExecutionHistory == NULL) { + return; + } + + //1. Select the next available index // Actually, ExecutionHistoryNextAvailableIndex is used for current index. if (ChannelExtension->ExecutionHistoryNextAvailableIndex >= (MAX_EXECUTION_HISTORY_ENTRY_COUNT - 1)) { ChannelExtension->ExecutionHistoryNextAvailableIndex = 0; @@ -186,7 +203,9 @@ Return Value: ChannelExtension->ExecutionHistoryNextAvailableIndex++; } - //2. Copy data + StorPortQuerySystemTime(&(ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].TimeStamp)); + + //2. Copy data ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Function = Function; if (ChannelExtension->AdapterExtension->IS) { @@ -223,22 +242,22 @@ Called by: PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension; ie.AsUlong = StorPortReadRegisterUlong(adapterExtension, &IE->AsUlong); - ie.DHRE = 1; //Device to Host Register FIS Interrupt (DHRS): A D2H Register FIS has been received with the �I� bit set, and has been copied into system memory. - ie.PSE = 1; //PIO Setup FIS Interrupt (PSS): A PIO Setup FIS has been received with the �I� bit set, it has been copied into system memory, and the data related to that FIS has been transferred. This bit shall be set even if the data transfer resulted in an error. - ie.DSE = 1; //DMA Setup FIS Interrupt (DSS): A DMA Setup FIS has been received with the �I� bit set and has been copied into system memory. - ie.SDBE = 1; //Set Device Bits Interrupt (SDBS): A Set Device Bits FIS has been received with the �I� bit set and has been copied into system memory. + ie.DHRE = 1; //Device to Host Register FIS Interrupt (DHRS): A D2H Register FIS has been received with the �I� bit set, and has been copied into system memory. + ie.PSE = 1; //PIO Setup FIS Interrupt (PSS): A PIO Setup FIS has been received with the �I� bit set, it has been copied into system memory, and the data related to that FIS has been transferred. This bit shall be set even if the data transfer resulted in an error. + ie.DSE = 1; //DMA Setup FIS Interrupt (DSS): A DMA Setup FIS has been received with the �I� bit set and has been copied into system memory. + ie.SDBE = 1; //Set Device Bits Interrupt (SDBS): A Set Device Bits FIS has been received with the �I� bit set and has been copied into system memory. - ie.UFE = 0; //Unknown FIS Interrupt (UFS): When set to �1�, indicates that an unknown FIS was received and has been copied into system memory. This bit is cleared to �0� by software clearing the PxSERR.DIAG.F bit to �0�. Note that this bit does not directly reflect the PxSERR.DIAG.F bit. PxSERR.DIAG.F is set immediately when an unknown FIS is detected, whereas this bit is set when that FIS is posted to memory. Software should wait to act on an unknown FIS until this bit is set to �1� or the two bits may become out of sync. - ie.DPE = 0; //Descriptor Processed (DPS): A PRD with the �I� bit set has transferred all of its data. Refer to section 5.4.2. + ie.UFE = 0; //Unknown FIS Interrupt (UFS): When set to �1�, indicates that an unknown FIS was received and has been copied into system memory. This bit is cleared to �0� by software clearing the PxSERR.DIAG.F bit to �0�. Note that this bit does not directly reflect the PxSERR.DIAG.F bit. PxSERR.DIAG.F is set immediately when an unknown FIS is detected, whereas this bit is set when that FIS is posted to memory. Software should wait to act on an unknown FIS until this bit is set to �1� or the two bits may become out of sync. + ie.DPE = 0; //Descriptor Processed (DPS): A PRD with the �I� bit set has transferred all of its data. Refer to section 5.4.2. ie.PCE = 1; //Port Connect Change Status (PCS): 1=Change in Current Connect Status. 0=No change in Current Connect Status. This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared when PxSERR.DIAG.X is cleared. if(adapterExtension->CAP.SMPS) { - ie.DMPE = 1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device. This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to �1�. + ie.DMPE = 1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device. This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to �1�. } else { ie.DMPE = 0; } //Reserved :14; - ie.PRCE = 1; //PhyRdy Change Status (PRCS): When set to �1� indicates the internal PhyRdy signal changed state. This bit reflects the state of P0SERR.DIAG.N. To clear this bit, software must clear P0SERR.DIAG.N to �0�. + ie.PRCE = 1; //PhyRdy Change Status (PRCS): When set to �1� indicates the internal PhyRdy signal changed state. This bit reflects the state of P0SERR.DIAG.N. To clear this bit, software must clear P0SERR.DIAG.N to �0�. ie.IPME = 0; //Incorrect Port Multiplier Status (IPMS): Indicates that the HBA received a FIS from a device whose Port Multiplier field did not match what was expected. The IPMS bit may be set during enumeration of devices on a Port Multiplier due to the normal Port Multiplier enumeration process. It is recommended that IPMS only be used after enumeration is complete on the Port Multiplier. ie.OFE = 1; //Overflow Status (OFS): Indicates that the HBA received more bytes from a device than was specified in the PRD table for the command. @@ -250,8 +269,8 @@ Called by: ie.HBFE = 1; //Host Bus Fatal Error Status (HBFS): Indicates that the HBA encountered a host bus error that it cannot recover from, such as a bad software pointer. In PCI, such an indication would be a target or master abort. ie.TFEE = 1; //Task File Error Status (TFES): This bit is set whenever the status register is updated by the device and the error bit (bit 0) is set. cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong); - if(cmd.CPD) { //check for PxCMD.CPD set to �1� before setting CPDE - ie.CPDE = 1; //Cold Port Detect Status (CPDS): When set, a device status has changed as detected by the cold presence detect logic. This bit can either be set due to a non-connected port receiving a device, or a connected port having its device removed. This bit is only valid if the port supports cold presence detect as indicated by PxCMD.CPD set to �1�. + if(cmd.CPD) { //check for PxCMD.CPD set to �1� before setting CPDE + ie.CPDE = 1; //Cold Port Detect Status (CPDS): When set, a device status has changed as detected by the cold presence detect logic. This bit can either be set due to a non-connected port receiving a device, or a connected port having its device removed. This bit is only valid if the port supports cold presence detect as indicated by PxCMD.CPD set to �1�. } else { ie.CPDE = 0; } @@ -451,7 +470,6 @@ Return Value: return matches; } - VOID AhciBusChangeCallback( _In_ PVOID AdapterExtension, @@ -494,38 +512,41 @@ PortBusChangeProcess ( ) { AHCI_SERIAL_ATA_CONTROL sctl; - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - ULONG status = STOR_STATUS_UNSUCCESSFUL; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + ULONG status = STOR_STATUS_UNSUCCESSFUL; - //1 if link speed was limited, restore the supported value. + // 1 if link speed was limited, restore the supported value. sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong); - if ( (sctl.SPD != 0) && - (sctl.SPD != ChannelExtension->AdapterExtension->CAP.ISS) ) { + if ((sctl.SPD != 0) && + (sctl.SPD != ChannelExtension->AdapterExtension->CAP.ISS)) { sctl.SPD = 0; //AHCI 3.3.11 --- 0h No speed negotiation restrictions StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong, sctl.AsUlong); } - //2 Kicks off the Start Channel state machine + // 2 Kicks off the Start Channel state machine AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); AhciPortReset(ChannelExtension, TRUE); // all requests should be completed AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); - //3 Requests rescan on this port. Using STATE_CHANGE_TARGET to make sure a REPORT LUNS command coming from Storport. + // Mark that cached Log Page information need to be updated. + ChannelExtension->DeviceExtension[0].UpdateCachedLogPageInfo = TRUE; + + // 3 Requests rescan on this port. Using STATE_CHANGE_TARGET to make sure a REPORT LUNS command coming from Storport. status = StorPortStateChangeDetected(ChannelExtension->AdapterExtension, - STATE_CHANGE_TARGET, - (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, - 0, - AhciBusChangeCallback, - NULL); + STATE_CHANGE_TARGET, + (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, + 0, + AhciBusChangeCallback, + NULL); - // if the state change notification fails, set a timer to retry the notification after a delay + // If the state change notification fails, set a timer to retry the notification after a delay if (status == STOR_STATUS_UNSUCCESSFUL) { if (ChannelExtension->BusChangeTimer == NULL) { - StorPortInitializeTimer(ChannelExtension->AdapterExtension, &ChannelExtension->BusChangeTimer); } + status = StorPortRequestTimer(ChannelExtension->AdapterExtension, ChannelExtension->BusChangeTimer, AhciBusChangeTimerCallback, @@ -533,22 +554,19 @@ PortBusChangeProcess ( 100000, // 100 milliseconds 0); - if ( status != STOR_STATUS_SUCCESS ) { + if (status != STOR_STATUS_SUCCESS) { PortReleaseActiveReference(ChannelExtension, NULL); } - } else { if (ChannelExtension->BusChangeTimer != NULL) { StorPortFreeTimer(ChannelExtension->AdapterExtension, ChannelExtension->BusChangeTimer); ChannelExtension->BusChangeTimer = NULL; - } - + } + PortReleaseActiveReference(ChannelExtension, NULL); - } - } VOID @@ -676,6 +694,7 @@ Affected Variables/Registers: PSTORAGE_REQUEST_BLOCK srbToRelease; BOOLEAN isSenseSrb; BOOLEAN retrySrb = FALSE; + BOOLEAN isReadLogExtSrbIssuedInNCQErrorRecovery = FALSE; slotContent = &ChannelExtension->Slot[SlotNumber]; if (slotContent->Srb == NULL) { @@ -685,6 +704,12 @@ Affected Variables/Registers: srbExtension = GetSrbExtension(slotContent->Srb); isSenseSrb = IsRequestSenseSrb(srbExtension->AtaFunction); + if ((slotContent->Srb == (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb) && + (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 1)) { + + isReadLogExtSrbIssuedInNCQErrorRecovery = TRUE; + } + if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000053);//ReleaseSlottedCommand } @@ -766,7 +791,6 @@ Affected Variables/Registers: } } - //3.1 Make the slot available again slotContent->CmdHeader = NULL; slotContent->CommandHistoryIndex = 0; @@ -796,6 +820,21 @@ Affected Variables/Registers: } } + // + // There is a bad timing will break the error recovery and cause IO hang eventually, typically happen in SSD but not limited to. + // 1. NCQ error happen first, which will issue the ReadLogExtCommand; + // 2. Then Nonqueued error happen before the ReadLogExtCommand complete. + // In ReleaseSlottedCommand, ReadLogExtCommand may got chance to be retried, which will result in SingleIoSlice set to 1, + // and SingleIoSlice will skip the PreservedSettingCommand(the ReservedSlotInUse is already set). + // 3. Then ReservedSlotInUse never got chance to be cleared because it relies on PreservedSettingCommand execution. + // ReservedSlotInUse will block the queue activation, finally the IO will hang and AhciPortReset will be repeatedly issued by upper layer. + // + // Do not retry ReadLogExtCommand when NCQ error recovery in process, which makes sense since ReadLogExtCommand is sense-like command as well. + // + if (retrySrb && isReadLogExtSrbIssuedInNCQErrorRecovery) { + retrySrb = FALSE; + } + if (retrySrb) { srbToRelease->SrbStatus = SRB_STATUS_PENDING; srbExtension->RetryCount++; @@ -1128,30 +1167,32 @@ Affected Variables/Registers: { BOOLEAN reservedSlotInUse = 0; - if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { - RecordExecutionHistory(ChannelExtension, 0x00000041); //RestorePreservedSettings - } + RecordExecutionHistory(ChannelExtension, 0x00000041); // RestorePreservedSettings - //1 Reinitialize all the commands to send + //1 Reinitialize all the commands to send ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots; reservedSlotInUse = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3 if (reservedSlotInUse == 1) { + // the process has been started. + RecordExecutionHistory(ChannelExtension, 0x10010041); // RestorePreservedSettings process already started return; } - // acquire active reference for process of restore preserved settings + // acquire active reference for process of restore preserved settings if (!AtDIRQL) { - // this is only needed if function is called at lower level than DIRQL. - // the places calling this function at DIRQL already made sure the unit is active. + // this is only needed if function is called at lower level than DIRQL. + // the places calling this function at DIRQL already made sure the unit is active. ULONG restorePreservedSettingsInProcess; BOOLEAN portIdle = FALSE; restorePreservedSettingsInProcess = InterlockedBitTestAndSet((LONG*)&ChannelExtension->PoFxPendingWork, 0); //RestorePreservedSettings is at bit 0 if (restorePreservedSettingsInProcess == 1) { + // it's already in process, return from here + RecordExecutionHistory(ChannelExtension, 0x10020041); // RestorePreservedSettings already in process return; } @@ -1160,21 +1201,24 @@ Affected Variables/Registers: } if (portIdle) { + // Unit Active process will continue on RestorePreservedSettings process + RecordExecutionHistory(ChannelExtension, 0x10030041); // RestorePreservedSettings will continue in Unit Active return; } else { ULONG restorePreservedSettingsPending; restorePreservedSettingsPending = InterlockedBitTestAndReset((LONG*)&ChannelExtension->PoFxPendingWork, 0); //RestorePreservedSettings is at bit 0 if (restorePreservedSettingsPending == 0) { + // Unit Active process already started on RestorePreservedSettings process + RecordExecutionHistory(ChannelExtension, 0x10040041); // RestorePreservedSettings already started in Unit Active return; } } } - //2 Start the first command IssuePreservedSettingCommands(ChannelExtension, NULL); @@ -1209,29 +1253,29 @@ Affected Variables/Registers: { BOOLEAN reservedSlotInUse = 0; - if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { - RecordExecutionHistory(ChannelExtension, 0x00000042); //AhciPortIssueInitCommands - } + RecordExecutionHistory(ChannelExtension, 0x00000042); // AhciPortIssueInitCommands - //1 Reinitialize all the commands to send + //1 Reinitialize all the commands to send ChannelExtension->DeviceInitCommands.CommandToSend = 0; reservedSlotInUse = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3 if (reservedSlotInUse == 1) { + // the process has been started. + RecordExecutionHistory(ChannelExtension, 0x10010042); // AhciPortIssueInitCommands process already started return; } - // acquire active reference for process of issuing init commands - // note: Port should be in Active state during device start. + // acquire active reference for process of issuing init commands + // note: Port should be in Active state during device start. if (PortAcquireActiveReference(ChannelExtension, NULL, NULL)) { ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced = 1; } - //2 Start the first command + //2 Start the first command IssueInitCommands(ChannelExtension, NULL); - //3 Starts processing the command. Only need to do the first command if it exists. all others will be done by processing completion routine. + //3 Starts processing the command. Only need to do the first command if it exists. all others will be done by processing completion routine. if (ChannelExtension->Local.Srb.SrbExtension != NULL) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb); if (srbExtension->AtaFunction != 0) { @@ -1242,6 +1286,164 @@ Affected Variables/Registers: return; } +VOID +AhciPortStartTelemetry( + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension + ) +/*++ + +Routine Description: + + This function logs telemetry for AHCI port start status. + +Arguments: + + AdapterExtension - Pointer to the device extension for adapter. + +Return Value: + + None. + +--*/ +{ + NT_ASSERT(AdapterExtension->InRunningPortsProcess == FALSE); + + for (ULONG i = 0; i <= AdapterExtension->HighestPort; ++i) { + PAHCI_CHANNEL_EXTENSION channelExtension = AdapterExtension->PortExtension[i]; + if (channelExtension != NULL) { + BOOLEAN portStartSuccess = (channelExtension->StartState.ChannelNextStartState == StartComplete) ? (TRUE) : (FALSE); + AhciTelemetryLogPortStart(channelExtension, + (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress), + ((portStartSuccess) ? (AhciTelemetryEventIdPortStartSuccess) : (AhciTelemetryEventIdPortRunningStartFail)), + ((portStartSuccess) ? ("Port Start Success") : ("Port Start Failed")), + ((portStartSuccess) ? (0) : (AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING)), + NULL, + 0 + ); + } + } + + return; +} + +VOID +AhciPortMarkDeviceFailed( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ AHCI_DEVICE_FAILURE_REASON FailureReason, + _In_ ULONG Flags, + _In_ BOOLEAN SuppressFailure + ) +/*++ + +Routine Description: + + This function will call storport API to mark failed device. + + When caller passes TRUE for SuppressFailure, this function will + only call storport API to mark failed device once; otherwise, + this function will always call storport API to mark failed device. + +Arguments: + + ChannelExtension - Pointer to the device extension for channel. + + FailureReason - Indicates device failure reason. + + Flags - Indicates whether remove failed device. + + SuppressFailure - Indicates whether suppress failures. + +Return Value: + + None. + +--*/ +{ + NT_ASSERT(ChannelExtension != NULL); + + // + // Ignore Non-ATA device failures. + // + if (!IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { + return; + } + + switch (FailureReason) { + + case AhciDeviceFailureTooManyBusChange: { + if ((!SuppressFailure) || + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK) == 0)) { + StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + Flags, + L"Too many bus change"); + } + + if (SuppressFailure && + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK) == 0)) { + ChannelExtension->DeviceFailureThrottleFlag |= AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK; + } + + break; + } + + case AhciDeviceFailureTooManyNCQError: { + if ((!SuppressFailure) || + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK) == 0)) { + StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + Flags, + L"Too many NCQ error"); + } + + if (SuppressFailure && + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK) == 0)) { + ChannelExtension->DeviceFailureThrottleFlag |= AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK; + } + + break; + } + + case AhciDeviceFailureTooManyNonQueuedError: { + if ((!SuppressFailure) || + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK) == 0)) { + StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + Flags, + L"Too many NonQueue error"); + } + + if (SuppressFailure && + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK) == 0)) { + ChannelExtension->DeviceFailureThrottleFlag |= AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK; + } + + break; + } + + case AhciDeviceFailureDeviceStuck: { + if ((!SuppressFailure) || + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK) == 0)) { + StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + 0, + L"Device may stuck"); + } + + if (SuppressFailure && + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK) == 0)) { + ChannelExtension->DeviceFailureThrottleFlag |= AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK; + } + + break; + } + + default: { + NT_ASSERT(FALSE); + break; + } + } +} #if _MSC_VER >= 1200 #pragma warning(pop) @@ -1251,4 +1453,3 @@ Affected Variables/Registers: #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/util.h b/storage/miniports/storahci/src/util.h index c05339b1..249b7547 100644 --- a/storage/miniports/storahci/src/util.h +++ b/storage/miniports/storahci/src/util.h @@ -55,6 +55,21 @@ AhciFillMemory( } __inline +VOID +AhciCopyMemory( + _Out_writes_(Length) PCHAR Destination, + _In_reads_(Length) PCHAR Source, + _In_ ULONG Length + ) +{ + ULONG i; + + for (i = 0; i < Length; i++) { + Destination[i] = Source[i]; + } +} + +__inline BOOLEAN IsPortValid( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension, @@ -191,7 +206,7 @@ IsMsnEnabled( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->MsnSupport == 1) ); } @@ -201,7 +216,7 @@ IsRmfEnabled( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.RemovableMediaFeature == 1) && (DeviceExtension->IdentifyDeviceData->GeneralConfiguration.RemovableMedia == 1) ); } @@ -212,7 +227,7 @@ NoFlushDevice( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.WriteCache == 0) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.FlushCache == 0) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.FlushCacheExt == 0) ); @@ -234,7 +249,7 @@ IsSmartFeatureSupported( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.SmartCommands == 1) ); } @@ -264,7 +279,7 @@ IsExtendedPowerConditionsFeatureSupported( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupportExt.ExtendedPowerConditions == 1) ); } @@ -274,7 +289,7 @@ IsStreamingFeatureSupported( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.StreamingFeature == 1) ); } @@ -284,7 +299,7 @@ IsNcqFeatureSupported( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) ); } @@ -533,6 +548,7 @@ IsExternalPort( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + if ( (ChannelExtension->Px->CMD.HPCP) || ((ChannelExtension->AdapterExtension->CAP.SXS) && (ChannelExtension->Px->CMD.ESP)) || ((ChannelExtension->AdapterExtension->CAP.SMPS) && (ChannelExtension->Px->CMD.MPSP)) ) { @@ -551,6 +567,7 @@ IsLPMCapablePort( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + // if a port is marked eSATA port but not having mechanical switch nor supporting Cold Presence Detection, don't turn on LPM on it. if ( (ChannelExtension->Px->CMD.HPCP) || ((ChannelExtension->AdapterExtension->CAP.SXS) && (ChannelExtension->Px->CMD.ESP)) ) { @@ -570,7 +587,8 @@ IsDeviceHybridInfoSupported( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { - if ( (ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceIsAta) && + + if ( IsAtaDevice(&ChannelExtension->DeviceExtension[0].DeviceParameters) && (ChannelExtension->DeviceExtension[0].IdentifyDeviceData->SerialAtaFeaturesSupported.HybridInformation == 1) ) { return TRUE; @@ -627,6 +645,7 @@ _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension // return TRUE; } + } return FALSE; } @@ -647,6 +666,7 @@ _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension // return TRUE; } + } return FALSE; } @@ -661,7 +681,6 @@ IsD3ColdAllowed( UNREFERENCED_PARAMETER(AdapterExtension); - return allowed; } @@ -671,6 +690,7 @@ IsPortD3ColdEnabled( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + // if device is D3Cold capable, return TRUE. if (ChannelExtension->StateFlags.D3ColdEnabled == 1) { return TRUE; @@ -688,13 +708,6 @@ IsReceivingSystemPowerHints( return (AdapterExtension->SystemPowerHintState != RaidSystemPowerUnknown); } - - - - - - - __inline BOOLEAN AdapterPoFxEnabled ( @@ -827,15 +840,23 @@ PortReleaseActiveReference ( __inline BOOLEAN -DeviceIdentificationComplete( +DeviceIdentificationCompletedSuccess( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { ULONG i; + // + // Note: There is possibility that 1st inquiry/identify will fail, then identify device + // data is invalid, but AtaDeviceType is initialized correctly during IO process. + // So it is also needed to check IdentifyDeviceSuccess flag to see whether identify + // command success. + // + for (i = 0; i <= AdapterExtension->HighestPort; i++) { - if ( (AdapterExtension->PortExtension[i] != NULL) && - (AdapterExtension->PortExtension[i]->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceUnknown) ) { + if ((AdapterExtension->PortExtension[i] != NULL) && + ((AdapterExtension->PortExtension[i]->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceUnknown) || + (AdapterExtension->PortExtension[i]->StateFlags.IdentifyDeviceSuccess == 0))) { return FALSE; } } @@ -1018,6 +1039,7 @@ IsFuaSupported( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + return (ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.FuaSupported == 1); } @@ -1045,6 +1067,7 @@ NoLpmSupport( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + if (ChannelExtension->AdapterExtension->StateFlags.StoppedState == 1) { // adapter is stopped, Px registers are not accessible. return TRUE; @@ -1067,6 +1090,7 @@ NeedToSetTransferMode( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + UNREFERENCED_PARAMETER(ChannelExtension); // make WDK sample build clean return FALSE; } @@ -1077,6 +1101,7 @@ IsSingleIoDevice( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { + UNREFERENCED_PARAMETER(AdapterExtension); // make WDK sample build clean return FALSE; } @@ -1087,6 +1112,7 @@ AdapterResetInInit( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { + UNREFERENCED_PARAMETER(AdapterExtension); // make WDK sample build clean return FALSE; } @@ -1097,6 +1123,7 @@ IgnoreHotPlug( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + return (ChannelExtension->StateFlags.IgnoreHotplugInterrupt == 1); } @@ -1106,6 +1133,7 @@ AdapterNoNonQueuedErrorRecovery( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { + UNREFERENCED_PARAMETER(AdapterExtension); // make WDK sample build clean return FALSE; } @@ -1116,6 +1144,7 @@ CloResetEnabled( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { + UNREFERENCED_PARAMETER(AdapterExtension); // make WDK sample build clean return (AdapterExtension->CAP.SCLO == 1); } @@ -1126,6 +1155,7 @@ IsNCQSupported( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + if ( IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters) && (ChannelExtension->AdapterExtension->CAP.SNCQ == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) && @@ -1145,6 +1175,7 @@ IsFirmwareUpdateSupported( BOOLEAN support; + if (ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported) { support = TRUE; } else { @@ -1252,7 +1283,6 @@ PartialToSlumberTransitionIsAllowed ( return FALSE; } - if ( ((ChannelExtension->LastUserLpmPowerSetting & 0x2) != 0) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.DeviceAutoPS == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaFeaturesEnabled.DeviceAutoPS == 1) ) { @@ -1296,6 +1326,7 @@ GetHybridMaxLbaRangeCountForChangeLba ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + // // If CacheBehavior is supported, disk doesn't move data from primary medium to caching medium when // executes HybridChangeByLba command. Use 64 as default MaxLbaRangeCountForChangeLba value. @@ -1314,17 +1345,52 @@ SupportsEnhancedNcqErrorRecovery ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + if ((ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqCommandError == 1) && (ChannelExtension->DeviceExtension->IoRecord.NcqReadLogErrorCount <= 5)) { return TRUE; + } else { return FALSE; } } __inline +BOOLEAN +IsAtaPioDataInCmd( + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + UCHAR command = IDE_COMMAND_NOT_VALID; + + if (IsAtaCfisPayload(srbExtension->AtaFunction)) { + command = srbExtension->Cfis.Command; + } else if (IsAtaCommand(srbExtension->AtaFunction)) { + command = srbExtension->TaskFile.Current.bCommandReg; + } + + // + // Only put together some major PIO Data-In commands here, which should be good enough + // for the PIO Data-In command return status mismatch issue. + // + switch (command) { + case IDE_COMMAND_READ: + case IDE_COMMAND_READ_EXT: + case IDE_COMMAND_READ_MULTIPLE_EXT: + case IDE_COMMAND_READ_LOG_EXT: + case IDE_COMMAND_ATAPI_IDENTIFY: + case IDE_COMMAND_READ_MULTIPLE: + case IDE_COMMAND_IDENTIFY: + return TRUE; + default: + return FALSE; + } +} + +__inline VOID -SetReturnRegisterValues ( +SetReturnRegisterValues( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_opt_ PGP_LOG_NCQ_COMMAND_ERROR NcqErrorLog @@ -1332,6 +1398,7 @@ SetReturnRegisterValues ( { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PCDB cdb = SrbGetCdb(Srb); + BOOLEAN pioDataInCmd = IsAtaPioDataInCmd(Srb); if ((IsReturnResults(srbExtension->Flags) == FALSE) || (srbExtension->ResultBuffer == NULL)) { return; @@ -1354,6 +1421,20 @@ SetReturnRegisterValues ( ataStatus->Header.AdditionalLength = 0x0C; ataStatus->Extend = Is48BitCommand(srbExtension->Flags) ? 1 : 0; + // For PIO Data In command, if it is successful, then the device will not send RD2H FIS. + // Only update the ATA status/error without getting the stale copy of information in D2H FIS. + if (pioDataInCmd && (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)) { + // According to SATA 3.0 specification 11.7: + // - Device will not send a RD2H FIS when PIO data in command success. + // According to AHCI 1.3.1 specification 5.3.9: + // - Device will report the ATA Status/Error register contents in the E_Status/Error field of the PIO Setup FIS. + // For ATA status return descriptor, refer to ATA Pass-Through specification 13.2.4. + ataStatus->Status = ChannelExtension->ReceivedFIS->PioSetupFis.E_Status; + ataStatus->Error = ChannelExtension->ReceivedFIS->PioSetupFis.Error; + + goto Exit; + } + if (NcqErrorLog == NULL) { ataStatus->Error = ChannelExtension->ReceivedFIS->D2hRegisterFis.Error; ataStatus->SectorCount7_0 = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorCount; @@ -1432,6 +1513,7 @@ SetReturnRegisterValues ( } } +Exit: // set flag SRB_STATUS_AUTOSENSE_VALID so that Storport will copy it back to original Sense Buffer Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; @@ -1511,18 +1593,27 @@ AtaFormFactorToStorageFormFactor( __inline BOOLEAN +IsAdapterRemovable( + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension + ) +{ + return (BOOLEAN)AdapterExtension->StateFlags.Removable; +} + +__inline +BOOLEAN IsAdapterRemoved( - _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { // - // When the adapter is removed, its registers are read as - // all 0xF's by the PCI bus. We use the PxSSTS register because - // it has several reserved bits which will unlikely be defined - // in the lifetime of the spec. + // When the adapter is removed, its registers are read as all 0xFs by the + // PCI bus. We use the Version register because all 0xFs is not a valid + // value and it is unlikely to ever be a valid value for the lifetime of + // the AHCI spec. // - ULONG ssts = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong); - return (ssts == MAXULONG); + ULONG version = StorPortReadRegisterUlong(AdapterExtension, &AdapterExtension->ABAR_Address->VS.AsUlong); + return (version == MAXULONG); } @@ -1586,6 +1677,19 @@ AhciPortIssueInitCommands( PAHCI_CHANNEL_EXTENSION ChannelExtension ); +VOID +AhciPortStartTelemetry( + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension + ); + +VOID +AhciPortMarkDeviceFailed( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ AHCI_DEVICE_FAILURE_REASON FailureReason, + _In_ ULONG Flags, + _In_ BOOLEAN SuppressFailure + ); + _Success_(return != FALSE) BOOLEAN CompareId ( @@ -1614,3 +1718,266 @@ AhciUlongIncrement( } } +VOID +FillAtaCommandErrorStruct( + _Out_ PATA_COMMAND_ERROR DataBuffer, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension + ); + + +VOID +FORCEINLINE +AhciTelemetryLog( + _In_ PVOID HwDeviceExtension, + _In_opt_ PSTOR_ADDRESS Address, + _In_ ULONG DriverVersion, + _In_ ULONG EventId, + _In_reads_or_z_(EVENT_NAME_MAX_LENGTH) PSTR EventName, + _In_ ULONG EventVersion, + _In_ ULONG Flags, + _In_ ULONG EventBufferLength, + _In_reads_bytes_opt_(EventBufferLength) PUCHAR EventBuffer, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter0Name, + _In_ ULONGLONG Parameter0Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter1Name, + _In_ ULONGLONG Parameter1Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter2Name, + _In_ ULONGLONG Parameter2Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter3Name, + _In_ ULONGLONG Parameter3Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter4Name, + _In_ ULONGLONG Parameter4Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter5Name, + _In_ ULONGLONG Parameter5Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter6Name, + _In_ ULONGLONG Parameter6Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter7Name, + _In_ ULONGLONG Parameter7Value + ) +{ + STORPORT_TELEMETRY_EVENT event = {0}; + + event.DriverVersion = DriverVersion; + event.EventId = EventId; + StorPortCopyMemory(event.EventName, EventName, GetStringLength(EventName, EVENT_NAME_MAX_LENGTH)); + event.EventVersion = EventVersion; + event.Flags = Flags; + + if ((EventBufferLength > 0) && (EventBuffer != NULL)) { + event.EventBuffer = EventBuffer; + event.EventBufferLength = min(EventBufferLength, EVENT_BUFFER_MAX_LENGTH); + } + + if (Parameter0Name != NULL) { + StorPortCopyMemory(event.ParameterName0, Parameter0Name, GetStringLength(Parameter0Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue0 = Parameter0Value; + } + + if (Parameter1Name != NULL) { + StorPortCopyMemory(event.ParameterName1, Parameter1Name, GetStringLength(Parameter1Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue1 = Parameter1Value; + } + + if (Parameter2Name != NULL) { + StorPortCopyMemory(event.ParameterName2, Parameter2Name, GetStringLength(Parameter2Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue2 = Parameter2Value; + } + + if (Parameter3Name != NULL) { + StorPortCopyMemory(event.ParameterName3, Parameter3Name, GetStringLength(Parameter3Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue3 = Parameter3Value; + } + + if (Parameter4Name != NULL) { + StorPortCopyMemory(event.ParameterName4, Parameter4Name, GetStringLength(Parameter4Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue4 = Parameter4Value; + } + + if (Parameter5Name != NULL) { + StorPortCopyMemory(event.ParameterName5, Parameter5Name, GetStringLength(Parameter5Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue5 = Parameter5Value; + } + + if (Parameter6Name != NULL) { + StorPortCopyMemory(event.ParameterName6, Parameter6Name, GetStringLength(Parameter6Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue6 = Parameter6Value; + } + + if (Parameter7Name != NULL) { + StorPortCopyMemory(event.ParameterName7, Parameter7Name, GetStringLength(Parameter7Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue7 = Parameter7Value; + } + + StorPortLogTelemetry(HwDeviceExtension, Address, &event); + + return; +} + +VOID +FORCEINLINE +AhciTelemetryLogResetErrorRecovery( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_opt_ PSTOR_ADDRESS Address, + _In_ ULONG EventId, + _In_reads_or_z_(EVENT_NAME_MAX_LENGTH) PSTR EventName, + _In_ ULONG Flags, + _In_reads_or_z_(EVENT_MAX_PARAM_NAME_LEN) PSTR AdditionalParameterName, + _In_ ULONGLONG AdditionalParameterValue + ) +{ + LARGE_INTEGER time; + ULONGLONG elapsedTime; + + if (ChannelExtension != NULL) { + + StorPortQuerySystemTime(&time); + elapsedTime = time.QuadPart - ChannelExtension->LastLogResetErrorRecoveryTime; + + // + // If AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING is set, always log event; + // otherwise, if this is the first time logging or if it has been 1 hour + // since the last log, log the current counts of reset and error recovery. + // + if (((Flags & AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING) != 0) || + (ChannelExtension->LastLogResetErrorRecoveryTime == 0) || + (elapsedTime >= MS_TO_100NS(60*60*1000))) { + + ChannelExtension->LastLogResetErrorRecoveryTime = time.QuadPart; + + AhciTelemetryLog(ChannelExtension->AdapterExtension, + Address, + AHCI_TELEMETRY_DRIVER_VERSION, + EventId, + EventName, + AHCI_TELEMETRY_EVENT_VERSION, + Flags, + 0, + NULL, + "PortReset|PortErrorRecovery", + (((ULONGLONG)ChannelExtension->TotalCountPortReset << 32) | + ((ULONGLONG)ChannelExtension->TotalCountPortErrorRecovery)), + "StartFail|NonQErrorRecovery", + (((ULONGLONG)ChannelExtension->TotalCountRunningStartFailed << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNonQueuedErrorRecovery)), + "NCQError|NCQErrorComplete", + (((ULONGLONG)ChannelExtension->TotalCountNCQError << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNCQErrorRecoveryComplete)), + "PortDriverResetCount", + ChannelExtension->DeviceExtension[0].IoRecord.PortDriverResetCount, + "StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + "StartState", + *(ULONGLONG *)&(ChannelExtension->StartState), + "TotalCountSurpriseRemove", + ChannelExtension->TotalCountSurpriseRemove, + AdditionalParameterName, + AdditionalParameterValue + ); + } + } + + return; +} + +VOID +FORCEINLINE +AhciTelemetryLogPowerSettingChange( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_opt_ PSTOR_ADDRESS Address, + _In_ ULONG EventId, + _In_reads_or_z_(EVENT_NAME_MAX_LENGTH) PSTR EventName, + _In_ ULONG Flags, + _In_ ULONG Ipm, + _In_ ULONG LpmMode + ) +{ + if (ChannelExtension != NULL) { + + AhciTelemetryLog(ChannelExtension->AdapterExtension, + Address, + AHCI_TELEMETRY_DRIVER_VERSION, + EventId, + EventName, + AHCI_TELEMETRY_EVENT_VERSION, + Flags, + 0, + NULL, + "PowerSettingChangeCount", + ChannelExtension->TotalCountPowerSettingNotification, + "StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + "LastUserLpmPowerSetting", + ChannelExtension->LastUserLpmPowerSetting, + "CAP", + ChannelExtension->AdapterExtension->CAP.AsUlong, + "CAP2", + ChannelExtension->AdapterExtension->CAP2.AsUlong, + "PxCMD", + StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong), + "IPM", + Ipm, + "LpmMode|SlumberInterval", + LpmMode + ); + } + + return; +} + +VOID +FORCEINLINE +AhciTelemetryLogPortStart( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_opt_ PSTOR_ADDRESS Address, + _In_ ULONG EventId, + _In_reads_or_z_(EVENT_NAME_MAX_LENGTH) PSTR EventName, + _In_ ULONG Flags, + _In_reads_or_z_(EVENT_MAX_PARAM_NAME_LEN) PSTR AdditionalParameterName, + _In_ ULONGLONG AdditionalParameterValue + ) +{ + if (ChannelExtension != NULL) { + // + // Log the telemetry event only when at least one of below conditions is true: + // 1. AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING flag is set; + // 2. Current port start time is no less than 1s. + // + if (((Flags && AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING) != 0) || + (ChannelExtension->TotalPortStartTime >= 1000)) { + + AhciTelemetryLog(ChannelExtension->AdapterExtension, + Address, + AHCI_TELEMETRY_DRIVER_VERSION, + EventId, + EventName, + AHCI_TELEMETRY_EVENT_VERSION, + Flags, + 0, + NULL, + "PortReset|PortErrorRecovery", + (((ULONGLONG)ChannelExtension->TotalCountPortReset << 32) | + ((ULONGLONG)ChannelExtension->TotalCountPortErrorRecovery)), + "StartFail|NonQErrorRecovery", + (((ULONGLONG)ChannelExtension->TotalCountRunningStartFailed << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNonQueuedErrorRecovery)), + "NCQError|NCQErrorComplete", + (((ULONGLONG)ChannelExtension->TotalCountNCQError << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNCQErrorRecoveryComplete)), + "PortDriverResetCount", + ChannelExtension->DeviceExtension[0].IoRecord.PortDriverResetCount, + "StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + "StartState", + *(ULONGLONG *)&(ChannelExtension->StartState), + "PortStartTime(ms)", + ChannelExtension->TotalPortStartTime, + AdditionalParameterName, + AdditionalParameterValue + ); + } + } + + return; +} + diff --git a/storage/msdsm/src/SampleDSM.inf b/storage/msdsm/src/SampleDSM.inf Binary files differindex f632f6e5..5617fa4e 100644 --- a/storage/msdsm/src/SampleDSM.inf +++ b/storage/msdsm/src/SampleDSM.inf diff --git a/storage/msdsm/src/intrface.c b/storage/msdsm/src/intrface.c index 925252be..b282600f 100644 --- a/storage/msdsm/src/intrface.c +++ b/storage/msdsm/src/intrface.c @@ -101,6 +101,11 @@ Return Value: // + // Must call this function before using pool allocation functions. + // + ExInitializeDriverRuntime(0); + + // // Initialize the tracing subsystem. // Any failure is handled by ETW itself. // diff --git a/storage/msdsm/src/precomp.h b/storage/msdsm/src/precomp.h index c199724e..12f1a7aa 100644 --- a/storage/msdsm/src/precomp.h +++ b/storage/msdsm/src/precomp.h @@ -18,6 +18,7 @@ Revision History: #pragma once #define DEBUG_MAIN_SOURCE 1 +#define POOL_ZERO_DOWN_LEVEL_SUPPORT #include <stdio.h> #include <stdarg.h> diff --git a/storage/msdsm/src/prototypes.h b/storage/msdsm/src/prototypes.h index 1df58197..371315ba 100644 --- a/storage/msdsm/src/prototypes.h +++ b/storage/msdsm/src/prototypes.h @@ -1336,8 +1336,8 @@ Description: Arguments: Srb - A pointer to either a STORAGE_REQUEST_BLOCK or a SCSI_REQUEST_BLOCK. - PoolType - The pool type to use. See documentation for ExAllocatePoolWithTag(). - Tag - The allocation tag to use. See documentation for ExAllocatePoolWithTag(). + PoolType - The pool type to use. See documentation for ExAllocatePoolZero(). + Tag - The allocation tag to use. See documentation for ExAllocatePoolZero(). Returns: NULL, if the copy could not be allocated; or diff --git a/storage/msdsm/src/utils.c b/storage/msdsm/src/utils.c index 2f1bcbe1..1d966fd5 100644 --- a/storage/msdsm/src/utils.c +++ b/storage/msdsm/src/utils.c @@ -93,10 +93,7 @@ Return Value: Tag)); #pragma warning(suppress: 28118) // False-positive; PoolType is simply passed through - Block = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); - if (Block) { - RtlZeroMemory(Block, NumberOfBytes); - } + Block = ExAllocatePoolZero(PoolType, NumberOfBytes, Tag); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, @@ -176,7 +173,7 @@ Return Value: if (NT_SUCCESS(status)) { #pragma warning(suppress: 6014 28118) // Block isn't leaked, this function is marked as an allocator; PoolType is simply passed through - Block = ExAllocatePoolWithTag(PoolType, totalSize, Tag); + Block = ExAllocatePoolZero(PoolType, totalSize, Tag); if (Block != NULL) { @@ -194,7 +191,6 @@ __Exit: if (NT_SUCCESS(status)) { - RtlZeroMemory(Block, totalSize); *BytesAllocated = totalSize; } diff --git a/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.c b/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.c index 786994bf..3ce17bf1 100644 --- a/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.c +++ b/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.c @@ -1243,7 +1243,7 @@ Return Value: // Enable data phase mismatch processing internal to 1010 WRITE_SIOP_UCHAR( CCNTL0, CCNTL0_DISABLE_PIPE_REQ + CCNTL0_ENA_PM_JUMP); - // Disable 64-bit slave cycles + // Disable 64-bit subordinate cycles WRITE_SIOP_UCHAR( CCNTL1, 0x10); // Set phase mismatch jump registers WRITE_SIOP_ULONG( PMJAD1, DeviceExtension->PhaseMisJump1Phys); diff --git a/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj b/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj index a4209763..ac9dc9b7 100644 --- a/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj +++ b/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj @@ -130,6 +130,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\WppRecorderUM.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -151,6 +154,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\WppRecorderUM.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -172,6 +178,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\WppRecorderUM.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -193,6 +202,9 @@ <Link> <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\WppRecorderUM.lib</AdditionalDependencies> </Link> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> </ItemDefinitionGroup> <ItemGroup> <Inf Include="osrusbfx2um.inx" /> @@ -207,4 +219,4 @@ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project> +</Project>
\ No newline at end of file diff --git a/video/IndirectDisplay/IddSampleApp/IddSampleApp.vcxproj b/video/IndirectDisplay/IddSampleApp/IddSampleApp.vcxproj index 0e8fbdba..a72a2853 100644 --- a/video/IndirectDisplay/IddSampleApp/IddSampleApp.vcxproj +++ b/video/IndirectDisplay/IddSampleApp/IddSampleApp.vcxproj @@ -1,10 +1,26 @@ <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|ARM"> + <Configuration>Debug</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <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|ARM"> + <Configuration>Release</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> @@ -31,6 +47,18 @@ <PlatformToolset>v142</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> @@ -38,6 +66,20 @@ <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> @@ -59,9 +101,21 @@ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </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')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </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')" Label="LocalAppDataPlatform" /> </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')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </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')" Label="LocalAppDataPlatform" /> </ImportGroup> @@ -72,12 +126,24 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <LinkIncremental>true</LinkIncremental> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <LinkIncremental>false</LinkIncremental> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> </PropertyGroup> @@ -94,6 +160,32 @@ <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <WarningLevel>Level3</WarningLevel> @@ -124,6 +216,40 @@ <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> <WarningLevel>Level3</WarningLevel> @@ -147,4 +273,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/video/IndirectDisplay/IddSampleDriver.sln b/video/IndirectDisplay/IddSampleDriver.sln index ec38923b..c075ebc1 100644 --- a/video/IndirectDisplay/IddSampleDriver.sln +++ b/video/IndirectDisplay/IddSampleDriver.sln @@ -43,6 +43,22 @@ Global {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.ActiveCfg = Release|Win32 {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Build.0 = Release|Win32 {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Deploy.0 = Release|Win32 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|ARM.ActiveCfg = Debug|ARM + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|ARM.Build.0 = Debug|ARM + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|ARM64.Build.0 = Debug|ARM64 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|x64.ActiveCfg = Debug|x64 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|x64.Build.0 = Debug|x64 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|x86.ActiveCfg = Debug|Win32 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|x86.Build.0 = Debug|Win32 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|ARM.ActiveCfg = Release|ARM + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|ARM.Build.0 = Release|ARM + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|ARM64.ActiveCfg = Release|ARM64 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|ARM64.Build.0 = Release|ARM64 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|x64.ActiveCfg = Release|x64 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|x64.Build.0 = Release|x64 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|x86.ActiveCfg = Release|Win32 + {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/video/IndirectDisplay/IddSampleDriver/Driver.cpp b/video/IndirectDisplay/IddSampleDriver/Driver.cpp index a9bd6108..ae060298 100644 --- a/video/IndirectDisplay/IddSampleDriver/Driver.cpp +++ b/video/IndirectDisplay/IddSampleDriver/Driver.cpp @@ -22,6 +22,105 @@ using namespace std; using namespace Microsoft::IndirectDisp; using namespace Microsoft::WRL; +#pragma region SampleMonitors + +static constexpr DWORD IDD_SAMPLE_MONITOR_COUNT = 3; // If monitor count > ARRAYSIZE(s_SampleMonitors), we create edid-less monitors + +// Default modes reported for edid-less monitors. The first mode is set as preferred +static const struct IndirectSampleMonitor::SampleMonitorMode s_SampleDefaultModes[] = +{ + { 1920, 1080, 60 }, + { 1600, 900, 60 }, + { 1024, 768, 75 }, +}; + +// FOR SAMPLE PURPOSES ONLY, Static info about monitors that will be reported to OS +static const struct IndirectSampleMonitor s_SampleMonitors[] = +{ + // Modified EDID from Dell S2719DGF + { + { + 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x10,0xAC,0xE6,0xD0,0x55,0x5A,0x4A,0x30,0x24,0x1D,0x01, + 0x04,0xA5,0x3C,0x22,0x78,0xFB,0x6C,0xE5,0xA5,0x55,0x50,0xA0,0x23,0x0B,0x50,0x54,0x00,0x02,0x00, + 0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x58,0xE3,0x00, + 0xA0,0xA0,0xA0,0x29,0x50,0x30,0x20,0x35,0x00,0x55,0x50,0x21,0x00,0x00,0x1A,0x00,0x00,0x00,0xFF, + 0x00,0x37,0x4A,0x51,0x58,0x42,0x59,0x32,0x0A,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,0x00, + 0x53,0x32,0x37,0x31,0x39,0x44,0x47,0x46,0x0A,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFD,0x00,0x28, + 0x9B,0xFA,0xFA,0x40,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x2C + }, + { + { 2560, 1440, 144 }, + { 1920, 1080, 60 }, + { 1024, 768, 60 }, + }, + 0 + }, + // Modified EDID from Lenovo Y27fA + { + { + 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x30,0xAE,0xBF,0x65,0x01,0x01,0x01,0x01,0x20,0x1A,0x01, + 0x04,0xA5,0x3C,0x22,0x78,0x3B,0xEE,0xD1,0xA5,0x55,0x48,0x9B,0x26,0x12,0x50,0x54,0x00,0x08,0x00, + 0xA9,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x68,0xD8,0x00, + 0x18,0xF1,0x70,0x2D,0x80,0x58,0x2C,0x45,0x00,0x53,0x50,0x21,0x00,0x00,0x1E,0x00,0x00,0x00,0x10, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFD,0x00, + 0x30,0x92,0xB4,0xB4,0x22,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,0x00,0x4C, + 0x45,0x4E,0x20,0x59,0x32,0x37,0x66,0x41,0x0A,0x20,0x20,0x20,0x00,0x11 + }, + { + { 3840, 2160, 60 }, + { 1600, 900, 60 }, + { 1024, 768, 60 }, + }, + 0 + } +}; + +#pragma endregion + +#pragma region helpers + +static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, DWORD Width, DWORD Height, DWORD VSync, bool bMonitorMode) +{ + Mode.totalSize.cx = Mode.activeSize.cx = Width; + Mode.totalSize.cy = Mode.activeSize.cy = Height; + + // See https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-displayconfig_video_signal_info + Mode.AdditionalSignalInfo.vSyncFreqDivider = bMonitorMode ? 0 : 1; + Mode.AdditionalSignalInfo.videoStandard = 255; + + Mode.vSyncFreq.Numerator = VSync; + Mode.vSyncFreq.Denominator = 1; + Mode.hSyncFreq.Numerator = VSync * Height; + Mode.hSyncFreq.Denominator = 1; + + Mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; + + Mode.pixelRate = ((UINT64) VSync) * ((UINT64) Width) * ((UINT64) Height); +} + +static IDDCX_MONITOR_MODE CreateIddCxMonitorMode(DWORD Width, DWORD Height, DWORD VSync, IDDCX_MONITOR_MODE_ORIGIN Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER) +{ + IDDCX_MONITOR_MODE Mode = {}; + + Mode.Size = sizeof(Mode); + Mode.Origin = Origin; + FillSignalInfo(Mode.MonitorVideoSignalInfo, Width, Height, VSync, true); + + return Mode; +} + +static IDDCX_TARGET_MODE CreateIddCxTargetMode(DWORD Width, DWORD Height, DWORD VSync) +{ + IDDCX_TARGET_MODE Mode = {}; + + Mode.Size = sizeof(Mode); + FillSignalInfo(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync, false); + + return Mode; +} + +#pragma endregion + extern "C" DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD IddSampleDeviceAdd; @@ -48,9 +147,22 @@ struct IndirectDeviceContextWrapper } }; +struct IndirectMonitorContextWrapper +{ + IndirectMonitorContext* pContext; + + void Cleanup() + { + delete pContext; + pContext = nullptr; + } +}; + // This macro creates the methods for accessing an IndirectDeviceContextWrapper as a context for a WDF object WDF_DECLARE_CONTEXT_TYPE(IndirectDeviceContextWrapper); +WDF_DECLARE_CONTEXT_TYPE(IndirectMonitorContextWrapper); + extern "C" BOOL WINAPI DllMain( _In_ HINSTANCE hInstance, _In_ UINT dwReason, @@ -241,7 +353,7 @@ void SwapChainProcessor::Run() // For improved performance, make use of the Multimedia Class Scheduler Service, which will intelligently // prioritize this thread for improved throughput in high CPU-load scenarios. DWORD AvTask = 0; - HANDLE AvTaskHandle = AvSetMmThreadCharacteristics(L"Distribution", &AvTask); + HANDLE AvTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &AvTask); RunCore(); @@ -310,6 +422,7 @@ void SwapChainProcessor::RunCore() } else if (SUCCEEDED(hr)) { + // We have new frame to process, the surface has a reference on it that the driver has to release AcquiredBuffer.Attach(Buffer.MetaData.pSurface); // ============================== @@ -323,7 +436,17 @@ void SwapChainProcessor::RunCore() // * a GPU custom compute shader encode operation // ============================== + // We have finished processing this frame hence we release the reference on it. + // If the driver forgets to release the reference to the surface, it will be leaked which results in the + // surfaces being left around after swapchain is destroyed. + // NOTE: Although in this sample we release reference to the surface here; the driver still + // owns the Buffer.MetaData.pSurface surface until IddCxSwapChainReleaseAndAcquireBuffer returns + // S_OK and gives us a new frame, a driver may want to use the surface in future to re-encode the desktop + // for better quality if there is no new frame for a while AcquiredBuffer.Reset(); + + // Indicate to OS that we have finished inital processing of the frame, it is a hint that + // OS could start preparing another frame hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); if (FAILED(hr)) { @@ -349,55 +472,14 @@ void SwapChainProcessor::RunCore() #pragma region IndirectDeviceContext -const UINT64 MHZ = 1000000; -const UINT64 KHZ = 1000; - -// A list of modes exposed by the sample monitor EDID - FOR SAMPLE PURPOSES ONLY -const DISPLAYCONFIG_VIDEO_SIGNAL_INFO IndirectDeviceContext::s_KnownMonitorModes[] = -{ - // 800 x 600 @ 60Hz - { - 40 * MHZ, // pixel clock rate [Hz] - { 40 * MHZ, 800 + 256 }, // fractional horizontal refresh rate [Hz] - { 40 * MHZ, (800 + 256) * (600 + 28) }, // fractional vertical refresh rate [Hz] - { 800, 600 }, // (horizontal, vertical) active pixel resolution - { 800 + 256, 600 + 28 }, // (horizontal, vertical) total pixel resolution - { { 255, 0 }}, // video standard and vsync divider - DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE - }, - // 640 x 480 @ 60Hz - { - 25175 * KHZ, // pixel clock rate [Hz] - { 25175 * KHZ, 640 + 160 }, // fractional horizontal refresh rate [Hz] - { 25175 * KHZ, (640 + 160) * (480 + 46) }, // fractional vertical refresh rate [Hz] - { 640, 480 }, // (horizontal, vertical) active pixel resolution - { 640 + 160, 480 + 46 }, // (horizontal, vertical) blanking pixel resolution - { { 255, 0 } }, // video standard and vsync divider - DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE - }, -}; - -// This is a sample monitor EDID - FOR SAMPLE PURPOSES ONLY -const BYTE IndirectDeviceContext::s_KnownMonitorEdid[] = -{ - 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x79,0x5E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xA6,0x01,0x03,0x80,0x28, - 0x1E,0x78,0x0A,0xEE,0x91,0xA3,0x54,0x4C,0x99,0x26,0x0F,0x50,0x54,0x20,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0xA0,0x0F,0x20,0x00,0x31,0x58,0x1C,0x20,0x28,0x80,0x14,0x00, - 0x90,0x2C,0x11,0x00,0x00,0x1E,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E -}; - IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) : m_WdfDevice(WdfDevice) { m_Adapter = {}; - m_Monitor = {}; } IndirectDeviceContext::~IndirectDeviceContext() { - m_ProcessingThread.reset(); } void IndirectDeviceContext::InitAdapter() @@ -413,7 +495,7 @@ void IndirectDeviceContext::InitAdapter() AdapterCaps.Size = sizeof(AdapterCaps); // Declare basic feature support for the adapter (required) - AdapterCaps.MaxMonitorsSupported = 1; + AdapterCaps.MaxMonitorsSupported = IDD_SAMPLE_MONITOR_COUNT; AdapterCaps.EndPointDiagnostics.Size = sizeof(AdapterCaps.EndPointDiagnostics); AdapterCaps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE; AdapterCaps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_WIRED_OTHER; @@ -454,27 +536,37 @@ void IndirectDeviceContext::InitAdapter() } } -void IndirectDeviceContext::FinishInit() +void IndirectDeviceContext::FinishInit(UINT ConnectorIndex) { // ============================== - // TODO: In a real driver, the EDID should be retrieved dynamically from a connected physical monitor. The EDID - // provided here is purely for demonstration, as it describes only 640x480 @ 60 Hz and 800x600 @ 60 Hz. Monitor - // manufacturers are required to correctly fill in physical monitor attributes in order to allow the OS to optimize - // settings like viewing distance and scale factor. Manufacturers should also use a unique serial number every - // single device to ensure the OS can tell the monitors apart. + // TODO: In a real driver, the EDID should be retrieved dynamically from a connected physical monitor. The EDIDs + // provided here are purely for demonstration. + // Monitor manufacturers are required to correctly fill in physical monitor attributes in order to allow the OS + // to optimize settings like viewing distance and scale factor. Manufacturers should also use a unique serial + // number every single device to ensure the OS can tell the monitors apart. // ============================== WDF_OBJECT_ATTRIBUTES Attr; - WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectMonitorContextWrapper); + // In the sample driver, we report a monitor right away but a real driver would do this when a monitor connection event occurs IDDCX_MONITOR_INFO MonitorInfo = {}; MonitorInfo.Size = sizeof(MonitorInfo); MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI; - MonitorInfo.ConnectorIndex = 0; + MonitorInfo.ConnectorIndex = ConnectorIndex; + MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription); MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID; - MonitorInfo.MonitorDescription.DataSize = sizeof(s_KnownMonitorEdid); - MonitorInfo.MonitorDescription.pData = const_cast<BYTE*>(s_KnownMonitorEdid); + if (ConnectorIndex >= ARRAYSIZE(s_SampleMonitors)) + { + MonitorInfo.MonitorDescription.DataSize = 0; + MonitorInfo.MonitorDescription.pData = nullptr; + } + else + { + MonitorInfo.MonitorDescription.DataSize = IndirectSampleMonitor::szEdidBlock; + MonitorInfo.MonitorDescription.pData = const_cast<BYTE*>(s_SampleMonitors[ConnectorIndex].pEdidBlock); + } // ============================== // TODO: The monitor's container ID should be distinct from "this" device's container ID if the monitor is not @@ -496,19 +588,27 @@ void IndirectDeviceContext::FinishInit() NTSTATUS Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut); if (NT_SUCCESS(Status)) { - m_Monitor = MonitorCreateOut.MonitorObject; - - // Associate the monitor with this device context - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorCreateOut.MonitorObject); - pContext->pContext = this; + // Create a new monitor context object and attach it to the Idd monitor object + auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorCreateOut.MonitorObject); + pMonitorContextWrapper->pContext = new IndirectMonitorContext(MonitorCreateOut.MonitorObject); // Tell the OS that the monitor has been plugged in IDARG_OUT_MONITORARRIVAL ArrivalOut; - Status = IddCxMonitorArrival(m_Monitor, &ArrivalOut); + Status = IddCxMonitorArrival(MonitorCreateOut.MonitorObject, &ArrivalOut); } } -void IndirectDeviceContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent) +IndirectMonitorContext::IndirectMonitorContext(_In_ IDDCX_MONITOR Monitor) : + m_Monitor(Monitor) +{ +} + +IndirectMonitorContext::~IndirectMonitorContext() +{ + m_ProcessingThread.reset(); +} + +void IndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent) { m_ProcessingThread.reset(); @@ -526,7 +626,7 @@ void IndirectDeviceContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID Rend } } -void IndirectDeviceContext::UnassignSwapChain() +void IndirectMonitorContext::UnassignSwapChain() { // Stop processing the last swap-chain m_ProcessingThread.reset(); @@ -542,10 +642,13 @@ NTSTATUS IddSampleAdapterInitFinished(IDDCX_ADAPTER AdapterObject, const IDARG_I // This is called when the OS has finished setting up the adapter for use by the IddCx driver. It's now possible // to report attached monitors. - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject); + auto* pDeviceContextWrapper = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject); if (NT_SUCCESS(pInArgs->AdapterInitStatus)) { - pContext->pContext->FinishInit(); + for (DWORD i = 0; i < IDD_SAMPLE_MONITOR_COUNT; i++) + { + pDeviceContextWrapper->pContext->FinishInit(i); + } } return STATUS_SUCCESS; @@ -576,27 +679,47 @@ NTSTATUS IddSampleParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION // this sample driver, we hard-code the EDID, so this function can generate known modes. // ============================== - pOutArgs->MonitorModeBufferOutputCount = ARRAYSIZE(IndirectDeviceContext::s_KnownMonitorModes); + pOutArgs->MonitorModeBufferOutputCount = IndirectSampleMonitor::szModeList; - if (pInArgs->MonitorModeBufferInputCount < ARRAYSIZE(IndirectDeviceContext::s_KnownMonitorModes)) + if (pInArgs->MonitorModeBufferInputCount < IndirectSampleMonitor::szModeList) { // Return success if there was no buffer, since the caller was only asking for a count of modes return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; } else { - // Copy the known modes to the output buffer - for (DWORD ModeIndex = 0; ModeIndex < ARRAYSIZE(IndirectDeviceContext::s_KnownMonitorModes); ModeIndex++) + // In the sample driver, we have reported some static information about connected monitors + // Check which of the reported monitors this call is for by comparing it to the pointer of + // our known EDID blocks. + + if (pInArgs->MonitorDescription.DataSize != IndirectSampleMonitor::szEdidBlock) + return STATUS_INVALID_PARAMETER; + + DWORD SampleMonitorIdx = 0; + for(; SampleMonitorIdx < ARRAYSIZE(s_SampleMonitors); SampleMonitorIdx++) { - pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE); - pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; - pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = IndirectDeviceContext::s_KnownMonitorModes[ModeIndex]; - } + if (memcmp(pInArgs->MonitorDescription.pData, s_SampleMonitors[SampleMonitorIdx].pEdidBlock, IndirectSampleMonitor::szEdidBlock) == 0) + { + // Copy the known modes to the output buffer + for (DWORD ModeIndex = 0; ModeIndex < IndirectSampleMonitor::szModeList; ModeIndex++) + { + pInArgs->pMonitorModes[ModeIndex] = CreateIddCxMonitorMode( + s_SampleMonitors[SampleMonitorIdx].pModeList[ModeIndex].Width, + s_SampleMonitors[SampleMonitorIdx].pModeList[ModeIndex].Height, + s_SampleMonitors[SampleMonitorIdx].pModeList[ModeIndex].VSync, + IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR + ); + } - // Set the preferred mode as represented in the EDID - pOutArgs->PreferredMonitorModeIdx = 0; + // Set the preferred mode as represented in the EDID + pOutArgs->PreferredMonitorModeIdx = s_SampleMonitors[SampleMonitorIdx].ulPreferredModeIdx; + + return STATUS_SUCCESS; + } + } - return STATUS_SUCCESS; + // This EDID block does not belong to the monitors we reported earlier + return STATUS_INVALID_PARAMETER; } } @@ -604,10 +727,6 @@ _Use_decl_annotations_ NTSTATUS IddSampleMonitorGetDefaultModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* pInArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* pOutArgs) { UNREFERENCED_PARAMETER(MonitorObject); - UNREFERENCED_PARAMETER(pInArgs); - UNREFERENCED_PARAMETER(pOutArgs); - - // Should never be called since we create a single monitor with a known EDID in this sample driver. // ============================== // TODO: In a real driver, this function would be called to generate monitor modes for a monitor with no EDID. @@ -616,29 +735,27 @@ NTSTATUS IddSampleMonitorGetDefaultModes(IDDCX_MONITOR MonitorObject, const IDAR // than an EDID, those modes would also be reported here. // ============================== - return STATUS_NOT_IMPLEMENTED; -} + if (pInArgs->DefaultMonitorModeBufferInputCount == 0) + { + pOutArgs->DefaultMonitorModeBufferOutputCount = ARRAYSIZE(s_SampleDefaultModes); + } + else + { + for (DWORD ModeIndex = 0; ModeIndex < ARRAYSIZE(s_SampleDefaultModes); ModeIndex++) + { + pInArgs->pDefaultMonitorModes[ModeIndex] = CreateIddCxMonitorMode( + s_SampleDefaultModes[ModeIndex].Width, + s_SampleDefaultModes[ModeIndex].Height, + s_SampleDefaultModes[ModeIndex].VSync, + IDDCX_MONITOR_MODE_ORIGIN_DRIVER + ); + } -/// <summary> -/// Creates a target mode from the fundamental mode attributes. -/// </summary> -void CreateTargetMode(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, UINT Width, UINT Height, UINT VSync) -{ - Mode.totalSize.cx = Mode.activeSize.cx = Width; - Mode.totalSize.cy = Mode.activeSize.cy = Height; - Mode.AdditionalSignalInfo.vSyncFreqDivider = 1; - Mode.AdditionalSignalInfo.videoStandard = 255; - Mode.vSyncFreq.Numerator = VSync; - Mode.vSyncFreq.Denominator = Mode.hSyncFreq.Denominator = 1; - Mode.hSyncFreq.Numerator = VSync * Height; - Mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; - Mode.pixelRate = ((UINT64) VSync) * ((UINT64) Width) * ((UINT64) Height); -} + pOutArgs->DefaultMonitorModeBufferOutputCount = ARRAYSIZE(s_SampleDefaultModes); + pOutArgs->PreferredMonitorModeIdx = 0; + } -void CreateTargetMode(IDDCX_TARGET_MODE& Mode, UINT Width, UINT Height, UINT VSync) -{ - Mode.Size = sizeof(Mode); - CreateTargetMode(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync); + return STATUS_SUCCESS; } _Use_decl_annotations_ @@ -646,18 +763,24 @@ NTSTATUS IddSampleMonitorQueryModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_ { UNREFERENCED_PARAMETER(MonitorObject); - vector<IDDCX_TARGET_MODE> TargetModes(4); + vector<IDDCX_TARGET_MODE> TargetModes; // Create a set of modes supported for frame processing and scan-out. These are typically not based on the // monitor's descriptor and instead are based on the static processing capability of the device. The OS will // report the available set of modes for a given output as the intersection of monitor modes with target modes. - CreateTargetMode(TargetModes[0], 1920, 1080, 60); - CreateTargetMode(TargetModes[1], 1024, 768, 60); - CreateTargetMode(TargetModes[2], 800, 600, 60); - CreateTargetMode(TargetModes[3], 640, 480, 60); + TargetModes.push_back(CreateIddCxTargetMode(3840, 2160, 60)); + TargetModes.push_back(CreateIddCxTargetMode(2560, 1440, 144)); + TargetModes.push_back(CreateIddCxTargetMode(2560, 1440, 90)); + TargetModes.push_back(CreateIddCxTargetMode(2560, 1440, 60)); + TargetModes.push_back(CreateIddCxTargetMode(1920, 1080, 144)); + TargetModes.push_back(CreateIddCxTargetMode(1920, 1080, 90)); + TargetModes.push_back(CreateIddCxTargetMode(1920, 1080, 60)); + TargetModes.push_back(CreateIddCxTargetMode(1600, 900, 60)); + TargetModes.push_back(CreateIddCxTargetMode(1024, 768, 75)); + TargetModes.push_back(CreateIddCxTargetMode(1024, 768, 60)); - pOutArgs->TargetModeBufferOutputCount = (UINT)TargetModes.size(); + pOutArgs->TargetModeBufferOutputCount = (UINT) TargetModes.size(); if (pInArgs->TargetModeBufferInputCount >= TargetModes.size()) { @@ -670,16 +793,16 @@ NTSTATUS IddSampleMonitorQueryModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_ _Use_decl_annotations_ NTSTATUS IddSampleMonitorAssignSwapChain(IDDCX_MONITOR MonitorObject, const IDARG_IN_SETSWAPCHAIN* pInArgs) { - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject); - pContext->pContext->AssignSwapChain(pInArgs->hSwapChain, pInArgs->RenderAdapterLuid, pInArgs->hNextSurfaceAvailable); + auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject); + pMonitorContextWrapper->pContext->AssignSwapChain(pInArgs->hSwapChain, pInArgs->RenderAdapterLuid, pInArgs->hNextSurfaceAvailable); return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObject) { - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject); - pContext->pContext->UnassignSwapChain(); + auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject); + pMonitorContextWrapper->pContext->UnassignSwapChain(); return STATUS_SUCCESS; } diff --git a/video/IndirectDisplay/IddSampleDriver/Driver.h b/video/IndirectDisplay/IddSampleDriver/Driver.h index df69b0e4..373b5a25 100644 --- a/video/IndirectDisplay/IddSampleDriver/Driver.h +++ b/video/IndirectDisplay/IddSampleDriver/Driver.h @@ -36,6 +36,23 @@ namespace Microsoft /// <summary> /// Manages the creation and lifetime of a Direct3D render device. /// </summary> + struct IndirectSampleMonitor + { + static constexpr size_t szEdidBlock = 128; + static constexpr size_t szModeList = 3; + + const BYTE pEdidBlock[szEdidBlock]; + const struct SampleMonitorMode { + DWORD Width; + DWORD Height; + DWORD VSync; + } pModeList[szModeList]; + const DWORD ulPreferredModeIdx; + }; + + /// <summary> + /// Manages the creation and lifetime of a Direct3D render device. + /// </summary> struct Direct3DDevice { Direct3DDevice(LUID AdapterLuid); @@ -64,7 +81,6 @@ namespace Microsoft void Run(); void RunCore(); - public: IDDCX_SWAPCHAIN m_hSwapChain; std::shared_ptr<Direct3DDevice> m_Device; HANDLE m_hAvailableBufferEvent; @@ -82,22 +98,25 @@ namespace Microsoft virtual ~IndirectDeviceContext(); void InitAdapter(); - void FinishInit(); - - void AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent); - void UnassignSwapChain(); + void FinishInit(UINT ConnectorIndex); protected: - WDFDEVICE m_WdfDevice; IDDCX_ADAPTER m_Adapter; - IDDCX_MONITOR m_Monitor; - - std::unique_ptr<SwapChainProcessor> m_ProcessingThread; + }; + class IndirectMonitorContext + { public: - static const DISPLAYCONFIG_VIDEO_SIGNAL_INFO s_KnownMonitorModes[]; - static const BYTE s_KnownMonitorEdid[]; - }; + IndirectMonitorContext(_In_ IDDCX_MONITOR Monitor); + virtual ~IndirectMonitorContext(); + + void AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent); + void UnassignSwapChain(); + + private: + IDDCX_MONITOR m_Monitor; + std::unique_ptr<SwapChainProcessor> m_ProcessingThread; + } ; } } diff --git a/video/IndirectDisplay/IddSampleDriver/IddSampleDriver.vcxproj b/video/IndirectDisplay/IddSampleDriver/IddSampleDriver.vcxproj index 8a484288..8a21c629 100644 --- a/video/IndirectDisplay/IddSampleDriver/IddSampleDriver.vcxproj +++ b/video/IndirectDisplay/IddSampleDriver/IddSampleDriver.vcxproj @@ -293,6 +293,7 @@ <WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData> <ExceptionHandling>Async</ExceptionHandling> <EnablePREfast>true</EnablePREfast> + <AdditionalOptions>/DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib</AdditionalDependencies> @@ -305,6 +306,7 @@ <WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData> <ExceptionHandling>Async</ExceptionHandling> <EnablePREfast>true</EnablePREfast> + <AdditionalOptions>/DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib</AdditionalDependencies> diff --git a/wil b/wil new file mode 160000 +Subproject 3c00e7f1d8cf9930bbb8e5be3ef0df65c84e892 |
