summaryrefslogtreecommitdiff
path: root/SPB/SpbTestTool
diff options
context:
space:
mode:
authorWei Mao <[email protected]>2017-03-17 19:47:04 -0700
committerWei Mao <[email protected]>2017-03-17 19:47:04 -0700
commit1a3e0d580380e58bf336a242d2affc8a1e2d1ddf (patch)
treebf5d9c5b0b4cba1b81726b9f78c4d5ff5c636fea /SPB/SpbTestTool
parentda21c8784c83c5fd614f3030323e229d6a5fb10e (diff)
Fix cases
Diffstat (limited to 'SPB/SpbTestTool')
-rw-r--r--SPB/SpbTestTool/README.md112
-rw-r--r--SPB/SpbTestTool/SpbTestTool.sln46
-rw-r--r--SPB/SpbTestTool/exe/SpbTestTool.vcxproj204
-rw-r--r--SPB/SpbTestTool/exe/SpbTestTool.vcxproj.Filters28
-rw-r--r--SPB/SpbTestTool/exe/command.cpp660
-rw-r--r--SPB/SpbTestTool/exe/command.h638
-rw-r--r--SPB/SpbTestTool/exe/internal.h99
-rw-r--r--SPB/SpbTestTool/exe/main.cpp633
-rw-r--r--SPB/SpbTestTool/exe/util.cpp250
-rw-r--r--SPB/SpbTestTool/sys/SpbTestTool.vcxproj197
-rw-r--r--SPB/SpbTestTool/sys/SpbTestTool.vcxproj.Filters42
-rw-r--r--SPB/SpbTestTool/sys/device.cpp848
-rw-r--r--SPB/SpbTestTool/sys/device.h43
-rw-r--r--SPB/SpbTestTool/sys/driver.cpp414
-rw-r--r--SPB/SpbTestTool/sys/driver.h36
-rw-r--r--SPB/SpbTestTool/sys/internal.h144
-rw-r--r--SPB/SpbTestTool/sys/peripheral.cpp1683
-rw-r--r--SPB/SpbTestTool/sys/peripheral.h104
-rw-r--r--SPB/SpbTestTool/sys/resource.rc11
-rw-r--r--SPB/SpbTestTool/sys/spbtestioctl.h50
-rw-r--r--SPB/SpbTestTool/sys/spbtesttool.asl26
-rw-r--r--SPB/SpbTestTool/sys/spbtesttool.inxbin0 -> 4688 bytes
-rw-r--r--SPB/SpbTestTool/sys/trace.h54
23 files changed, 6322 insertions, 0 deletions
diff --git a/SPB/SpbTestTool/README.md b/SPB/SpbTestTool/README.md
new file mode 100644
index 00000000..d7c9eb2e
--- /dev/null
+++ b/SPB/SpbTestTool/README.md
@@ -0,0 +1,112 @@
+SpbTestTool
+===========
+
+The SpbTestTool sample serves two purposes. First, it demonstrates how to open a handle to the [SPB controller](http://msdn.microsoft.com/en-us/library/windows/hardware/hh698220), use the SPB interface from a KMDF driver, and employ GPIO [passive-level interrupts](http://msdn.microsoft.com/en-us/library/windows/hardware/hh451035). Second, it implements a set of commands for communicating with a peripheral device to aid in debugging.
+
+This sample is incomplete as a driver and merely demonstrates use of the [SPB I/O request interface](http://msdn.microsoft.com/en-us/library/windows/hardware/hh698224) and [GPIO interrupts](http://msdn.microsoft.com/en-us/library/windows/hardware/hh406467). It is not intended for use in a production environment.
+
+### Run the sample
+
+To install the SpbTestTool peripheral driver, follow these steps:
+
+1. Ensure that the driver builds without errors.
+
+2. Copy the SYS and INF files to a separate folder.
+
+3. Run Devcon.exe. You can find this program in the tools\\devcon folder where you installed the WDK. Type the following command in the command window:
+
+ `devcon.exe update SpbTestTool.inf ACPI\<hwid>`
+
+To launch the SpbTestTool application, follow these steps:
+
+1. Navigate to the directory that contains SpbTestTool.exe.
+
+2. Type the following command in the command window:
+
+ `SpbTestTool.exe`
+
+3. By default, the SpbTestTool application uses the SpbTestTool sample driver. However, an alternate peripheral driver can be used instead. To specify an alternate driver, use the following format for the command line:
+
+ `SpbTestTool.exe /p \\.\<alternate_path>`
+
+4. An input script can used instead of an interactive prompt. The script format requires one command per line. To run the script, use the following format for the command line in the command window:
+
+ `SpbTestTool.exe /i <script.txt>`
+
+### Executing commands
+
+The SpbTestTool application loops indefinitely waiting for one of the following commands. The commands are translated to the appropriate SPB I/O request without any state tracking in the driver. Transfer status, buffer contents, and error codes are returned as necessary. Type `help` at any time to display this command list. Press Ctrl-C at any time to cancel the current command and exit the application.
+
+Command | Description
+--------|------------
+open | Open handle to SPB controller.
+close | Close handle to SPB controller.
+lock | Lock the bus for exclusive access.
+unlock | Unlock the bus.
+lockconn | Lock the shared connection for exclusive access. This command is used to synchronize bus transfers by the sample driver with op-region accesses by the ACPI firmware.
+unlockconn | Unlock the shared connection.
+write {} | Write a byte array to the peripheral device. Example: `> write {01, 02, 03}`
+read <*numBytes*> | Read <*numBytes*> from the peripheral device. Example: `> read 5`
+writeread {} <*numBytes*> | Atomically write a byte array to the peripheral device and read <*numBytes*> back. Example: `> writeread {01, 02, 03} 5`
+signal | Inform the SpbTestTool driver that the interrupt has been handled.
+help | Display the list of supported commands.
+Ctrl-C | Press Ctrl-C at any time to cancel the outstanding command and exit the application.
+
+### Code tour
+
+The following are the relevant functions in the SpbTestTool peripheral driver for using the SPB interface from a KMDF driver.
+
+Function | Description
+---------|------------
+OnPrepareHardware | Traverses the driver's start resources and caches the connection ID of the I2C or SPI resource. This ID will be used to open the SPB controller later on.
+SpbPeripheralOpen | Opens a handle to the underlying SPB controller via the resource hub. This allows the peripheral driver to be developed without any underlying knowledge of the platform or hardware connections. Instead, the dependency between controller and peripheral is described in ACPI.
+SpbPeripheralClose | Sends IOCTL_SPB_LOCK_CONTROLLER to the SPB controller to lock the bus for exclusive access by this peripheral.
+SpbPeripheralLock | Sends IOCTL_SPB_LOCK_CONTROLLER to the SPB controller to lock the bus for exclusive access by this peripheral.
+SpbPeripheralUnlock | Sends IOCTL_SPB_UNLOCK_CONTROLLER to the SPB controller to unlock the bus from exclusive access by this peripheral.
+SpbPeripheralLockConnection | Sends IOCTL_SPB_LOCK_CONNECTION to the SPB controller to lock the shared connection for exclusive access by this target (file handle).
+SpbPeripheralUnlockConnection | Sends IOCTL_SPB_UNLOCK_CONNECTION to the SPB controller to unlock the shared connection from exclusive access by this target (file handle).
+SpbPeripheralRead | Sends a read request to the SPB controller.
+SpbPeripheralWrite | Sends a write request to the SPB controller.
+SpbPeripheralWriteRead | Builds a write-read sequence and sends IOCTL_SPB_EXECUTE_SEQUENCE to the SPB controller.
+SpbPeripheralOnComplete | Completion callback for all I/O requests.
+
+The following are the relevant functions in the SpbTestTool peripheral driver for managing GPIO passive-level interrupts from a KMDF driver.
+
+Function | Description
+---------|------------
+OnPrepareHardware | Traverses the driver's start resources. If "ConnectInterrupt" is set to 1 in the registry, the driver connects the first interrupt resource found and registers an interrupt service routine.
+OnInterruptIsr | The interrupt service routine, which has been configured to run at passive-level. Doing so enables the driver to acknowledge or quiesce the interrupt using the SPB interface, which cannot be called at DIRQL. Typically a driver will clear the hardware interrupt and save any volatile information in its ISR, and then it will queue a workitem to continue processing. Our sample driver instead notifies the SpbTestTool app that an interrupt has occurred and calls KeWaitForSingleObject to wait until the interrupt is handled before returning. A "real" driver should never stall in the ISR like this.
+SpbPeripheralWaitOnInterrupt | Called to pend a WaitOnInterrupt request in the driver, which will be completed when the next interrupt occurs.
+SpbPeripheralInterruptNotify | Completes an outstanding WaitOnInterrupt request to inform the SpbTestTool app that an interrupt has occurred.
+SpbPeripheralSignalInterrupt | Notifies the interrupt service routine that the interrupt has been handled and the ISR should return.
+
+### File manifest
+
+The following source files are in the \\SpbTestTool\\sys folder and are used to build the SpbTestTool.sys and SpbTestTool.inf files.
+
+File | Description
+-----|------------
+driver.h, driver.cpp | Events on the Device Object, and read, write, and IOCTLs from the SpbTestTool application. Implements the driver's interrupt service routine.
+internal.h | Common includes and typedefs
+makefile | Redirects to the real makefile that is shared by all components of the WDK.
+peripheral.h, peripheral.cpp | Reflection of the SpbTestTool IOCTLs to the SPB API, including opening the controller via the resource hub and using lock, unlock, read, write, and sequence.
+resource.rc | Resource descriptor file used for versioning
+sources | Lists source files and build options.
+sources.dep | Defines build dependencies.
+spbtesttool.asl | Sample ASL file for a peripheral device node. It declares I2C and GPIO interrupt resources. Note each macro specifies an ACPI path to describe direct dependencies.
+spbtesttool.h | Private SpbTestTool IOCTLs for use between the application and peripheral driver, and driver path names.
+spbtesttool.inx | Describes the installation of the driver. The build process converts this into an INF.
+trace.h | Sets up WPP tracing.
+
+The following source files are in the \\SpbTestTool\\exe folder and are used to build the SpbTestTool.exe file.
+
+File | Description
+-----|------------
+command.h, command.cpp | Classes respresenting each of the SpbTestTool commands. For the list of commands, see Executing commands.
+internal.h | Common includes and function definitions
+main.cpp | Application entry point, input parsing, and main execution loop. Also contains the interrupt notification thread.
+makefile | Redirects to the real makefile that is shared by all components of the WDK.
+sources | Lists source files and build options.
+util.cpp | Helper functions
+
+
diff --git a/SPB/SpbTestTool/SpbTestTool.sln b/SPB/SpbTestTool/SpbTestTool.sln
new file mode 100644
index 00000000..0c0a4c6a
--- /dev/null
+++ b/SPB/SpbTestTool/SpbTestTool.sln
@@ -0,0 +1,46 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0
+MinimumVisualStudioVersion = 12.0
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{7F809CFD-9576-4C0D-8B6C-B76F69897EF1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{3AE6ED66-610E-442E-8D34-6AAFFBDC6BC3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpbTestTool", "exe\SpbTestTool.vcxproj", "{D98724B9-FEB8-4D51-B807-1D83E324A9AD}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpbTestTool", "sys\SpbTestTool.vcxproj", "{028ED946-EE58-43F7-834B-297119CE4720}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD}.Debug|Win32.Build.0 = Debug|Win32
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD}.Release|Win32.ActiveCfg = Release|Win32
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD}.Release|Win32.Build.0 = Release|Win32
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD}.Debug|x64.ActiveCfg = Debug|x64
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD}.Debug|x64.Build.0 = Debug|x64
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD}.Release|x64.ActiveCfg = Release|x64
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD}.Release|x64.Build.0 = Release|x64
+ {028ED946-EE58-43F7-834B-297119CE4720}.Debug|Win32.ActiveCfg = Debug|Win32
+ {028ED946-EE58-43F7-834B-297119CE4720}.Debug|Win32.Build.0 = Debug|Win32
+ {028ED946-EE58-43F7-834B-297119CE4720}.Release|Win32.ActiveCfg = Release|Win32
+ {028ED946-EE58-43F7-834B-297119CE4720}.Release|Win32.Build.0 = Release|Win32
+ {028ED946-EE58-43F7-834B-297119CE4720}.Debug|x64.ActiveCfg = Debug|x64
+ {028ED946-EE58-43F7-834B-297119CE4720}.Debug|x64.Build.0 = Debug|x64
+ {028ED946-EE58-43F7-834B-297119CE4720}.Release|x64.ActiveCfg = Release|x64
+ {028ED946-EE58-43F7-834B-297119CE4720}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {D98724B9-FEB8-4D51-B807-1D83E324A9AD} = {7F809CFD-9576-4C0D-8B6C-B76F69897EF1}
+ {028ED946-EE58-43F7-834B-297119CE4720} = {3AE6ED66-610E-442E-8D34-6AAFFBDC6BC3}
+ EndGlobalSection
+EndGlobal
diff --git a/SPB/SpbTestTool/exe/SpbTestTool.vcxproj b/SPB/SpbTestTool/exe/SpbTestTool.vcxproj
new file mode 100644
index 00000000..88f1e6a5
--- /dev/null
+++ b/SPB/SpbTestTool/exe/SpbTestTool.vcxproj
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{D98724B9-FEB8-4D51-B807-1D83E324A9AD}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{418E967E-17B4-4EA7-9D97-735BA0A2EE70}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType />
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType />
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType />
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType />
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);kernel32.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);kernel32.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);kernel32.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);kernel32.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="command.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="util.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/SPB/SpbTestTool/exe/SpbTestTool.vcxproj.Filters b/SPB/SpbTestTool/exe/SpbTestTool.vcxproj.Filters
new file mode 100644
index 00000000..e2c07bad
--- /dev/null
+++ b/SPB/SpbTestTool/exe/SpbTestTool.vcxproj.Filters
@@ -0,0 +1,28 @@
+<?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>{E51F9248-08FA-42B8-80B9-2B287C503100}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{CC896E5F-EEB8-4743-8F57-FAD8C296E752}</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>{CEA4D39C-0E96-45C5-97EF-3245169C1997}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="command.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/SPB/SpbTestTool/exe/command.cpp b/SPB/SpbTestTool/exe/command.cpp
new file mode 100644
index 00000000..fa559266
--- /dev/null
+++ b/SPB/SpbTestTool/exe/command.cpp
@@ -0,0 +1,660 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ command.cpp
+
+Abstract:
+
+ This module contains the class definitions for the various
+ SpbTestTool commands.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+
+long CCommand::s_Index = 1;
+
+VOID
+PrintCommands()
+{
+ printf("\n");
+ printf("Commands:\n");
+ printf(" open open handle to SPB controller\n");
+ printf(" close close handle to SPB controller\n");
+ printf(" lock lock the bus for exclusive access\n");
+ printf(" unlock unlock the bus\n");
+ printf(" lockconn lock the shared connection for exclusive access -\n");
+ printf(" this primitive is used to synchronize with op-region\n");
+ printf(" accesses by firmware\n");
+ printf(" unlockconn unlock the shared connection\n");
+ printf(" write {} write byte array to peripheral\n");
+ printf(" > write {01 02 03}\n");
+ printf(" read <numBytes> read <numBytes> from peripheral\n");
+ printf(" > read 5\n");
+ printf(" writeread {} <numBytes> atomically write byte array to peripheral\n");
+ printf(" and read <numBytes> back\n");
+ printf(" > writeread {01 02 03} 5\n");
+ printf(" fullduplex {} <numBytes> simultaneously write byte array to peripheral\n");
+ printf(" and read <numBytes> back\n");
+ printf(" > full duplex {01 02 03} 5\n");
+ printf(" signal inform the SpbTestTool driver that the\n");
+ printf(" interrupt has been handled\n");
+ printf(" help print command list\n");
+ printf("\n");
+}
+
+PCCommand
+CCommand::_ParseCommand(
+ _In_ __drv_when(return != nullptr, __drv_aliasesMem) list<string> *Parameters
+ )
+{
+ string tag;
+ string name;
+
+ PCCommand command = nullptr;
+
+ if (Parameters->front()[0] == L'@')
+ {
+ if (PopStringParameter(Parameters, &tag) == false)
+ {
+ printf("Error - could not pop tag\n");
+ return nullptr;
+ }
+ }
+
+ if (PopStringParameter(Parameters, &name) == false)
+ {
+ return nullptr;
+ }
+
+ if(_stricmp(name.c_str(), "open") == 0)
+ {
+ command = new COpenCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "close") == 0)
+ {
+ command = new CCloseCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "lock") == 0)
+ {
+ command = new CLockCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "unlock") == 0)
+ {
+ command = new CUnlockCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "lockconn") == 0)
+ {
+ command = new CLockConnectionCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "unlockconn") == 0)
+ {
+ command = new CUnlockConnectionCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "read") == 0)
+ {
+ command = new CReadCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "write") == 0)
+ {
+ command = new CWriteCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "writeread") == 0)
+ {
+ command = new CWriteReadCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "fullduplex") == 0)
+ {
+ command = new CFullDuplexCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "signal") == 0)
+ {
+ command = new CSignalInterruptCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "help") == 0)
+ {
+ PrintCommands();
+ return nullptr;
+ }
+ else
+ {
+ printf("unrecognized command %s\n", name.c_str());
+ PrintCommands();
+ return nullptr;
+ }
+
+ if (command->Parse() == false)
+ {
+ command->DetachParameter(); //avoid double deletion
+ delete command;
+ return nullptr;
+ }
+
+ return command;
+}
+
+bool
+COpenCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Open peripheral target
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_OPEN,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+COpenCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Peripheral target opened\n");
+ }
+}
+
+bool
+CCloseCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Close peripheral target
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_CLOSE,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CCloseCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Peripheral target closed\n");
+ }
+}
+
+bool
+CLockCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Lock controller for peripheral target
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_LOCK,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CLockCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Controller locked\n");
+ }
+}
+
+bool
+CUnlockCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Unlock controller
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_UNLOCK,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CUnlockCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Controller unlocked\n");
+ }
+}
+
+bool
+CLockConnectionCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Lock connection for peripheral target
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_LOCK_CONNECTION,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CLockConnectionCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Peripheral target locked\n");
+ }
+}
+
+bool
+CUnlockConnectionCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Unlock connection
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_UNLOCK_CONNECTION,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CUnlockConnectionCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Peripheral target unlocked\n");
+ }
+}
+
+bool
+CReadCommand::Execute(
+ VOID
+ )
+{
+ ULONG bytesRead;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((ReadFile(File,
+ Buffer,
+ Length,
+ &bytesRead,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesRead);
+ }
+
+ return true;
+}
+
+void
+CReadCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", Status);
+ }
+ else
+ {
+ printf("%u bytes read\n", Information);
+ PrintBytes(Information, Buffer);
+ }
+}
+
+bool
+CWriteCommand::Execute(
+ VOID
+ )
+{
+ ULONG bytesWritten;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((WriteFile(File,
+ Buffer,
+ Length,
+ &bytesWritten,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesWritten);
+ }
+
+ return true;
+}
+
+
+void
+CWriteCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", Status);
+ }
+ else
+ {
+ printf("%u bytes written\n", Information);
+ }
+}
+
+bool
+CWriteReadCommand::Execute(
+ VOID
+ )
+{
+ ULONG bytesTransferred;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(File,
+ IOCTL_SPBTESTTOOL_WRITEREAD,
+ WriteBuffer,
+ WriteLength,
+ Buffer,
+ ReadLength,
+ &bytesTransferred,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesTransferred);
+ }
+
+ return true;
+}
+
+void
+CWriteReadCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("%u bytes returned\n", Information);
+ PrintBytes(Information, Buffer);
+ }
+}
+
+bool
+CFullDuplexCommand::Execute(
+ VOID
+ )
+{
+ ULONG bytesTransferred;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(File,
+ IOCTL_SPBTESTTOOL_FULL_DUPLEX,
+ WriteBuffer,
+ WriteLength,
+ Buffer,
+ ReadLength,
+ &bytesTransferred,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesTransferred);
+ }
+
+ return true;
+}
+
+void
+CFullDuplexCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("%u bytes returned\n", Information);
+ PrintBytes(Information, Buffer);
+ }
+}
+
+bool
+CSignalInterruptCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Signal interrupt
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_SIGNAL_INTERRUPT,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CSignalInterruptCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Interrupt signalled\n");
+ }
+}
diff --git a/SPB/SpbTestTool/exe/command.h b/SPB/SpbTestTool/exe/command.h
new file mode 100644
index 00000000..5e2633bd
--- /dev/null
+++ b/SPB/SpbTestTool/exe/command.h
@@ -0,0 +1,638 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ command.h
+
+Abstract:
+
+ This module contains the class declarations for the various
+ SpbTestTool commands.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+typedef struct CCommand CCommand, *PCCommand;
+
+pair<ULONG, PBYTE>
+ParseBuffer(
+ _In_ list<string> *Parameters,
+ _In_ list<string>::iterator Start
+ );
+
+VOID
+PrintCommands();
+
+struct CCommand
+{
+public:
+
+ long Index;
+ string Type;
+ list<string> *Parameters;
+
+ //
+ // File handle this command is being run against (if any).
+ //
+
+ HANDLE File;
+
+ //
+ // Overlapped structure for this command to use.
+ //
+
+ OVERLAPPED Overlapped;
+
+ //
+ // Handle to the thread for this command (if run asynchronously)
+ // The thread is signalled when the command is complete.
+ //
+
+ HANDLE Thread;
+
+ //
+ // Common parameters.
+ //
+
+ string Address;
+ PBYTE Buffer;
+
+ static long s_Index;
+
+ CCommand(
+ _In_ string Type,
+ _In_ __drv_aliasesMem list<string> *Tokens
+ ) : Type(Type),
+ Thread(nullptr),
+ Parameters(Tokens),
+ Address(""),
+ Buffer(nullptr),
+ File(nullptr)
+ {
+ ZeroMemory(&Overlapped, sizeof(OVERLAPPED));
+ Index = s_Index;
+ s_Index += 1;
+ return;
+ }
+
+ virtual
+ ~CCommand(
+ void
+ )
+ {
+ delete[] Buffer;
+ delete Parameters;
+ }
+
+ virtual
+ bool
+ Parse(
+ void
+ )
+ {
+ File = g_Peripheral;
+ return true;
+ }
+
+ void
+ DetachParameter(
+ void
+ )
+ {
+ Parameters = nullptr;
+ }
+
+
+ typedef
+ void
+ (FN_PARSE)(
+ _In_ list<string>::const_iterator &Iterator
+ );
+
+public:
+
+ static
+ PCCommand
+ _ParseCommand(
+ _In_ __drv_when(return != nullptr, __drv_aliasesMem) list<string> *Tokens
+ );
+
+public:
+
+ VOID
+ FakeCompletion(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+ {
+ Overlapped.Internal = Status == NO_ERROR ? Status : HRESULT_FROM_WIN32(Status);
+ Overlapped.InternalHigh = Information;
+ SetEvent(Overlapped.hEvent);
+ }
+
+ virtual
+ bool
+ Execute(
+ VOID
+ ) = 0;
+
+ virtual
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+ {
+ printf("%s completed with status %u, information %u\n",
+ Type.c_str(),
+ Status,
+ Information);
+ }
+
+ virtual
+ bool
+ Cancel(
+ VOID
+ )
+ {
+ if (File != nullptr)
+ {
+ return CancelIoEx(File, &Overlapped) ? true : false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+};
+
+class COpenCommand : public CCommand
+{
+private:
+
+public:
+ COpenCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("open", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CCloseCommand : public CCommand
+{
+private:
+
+public:
+ CCloseCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("close", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CLockCommand : public CCommand
+{
+private:
+
+public:
+ CLockCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("lock", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CUnlockCommand : public CCommand
+{
+private:
+
+public:
+ CUnlockCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("unlock", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CLockConnectionCommand : public CCommand
+{
+private:
+
+public:
+ CLockConnectionCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("lockconn", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CUnlockConnectionCommand : public CCommand
+{
+private:
+
+public:
+ CUnlockConnectionCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("unlockconn", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CReadCommand : public CCommand
+{
+private:
+ ULONG Length;
+
+public:
+
+ CReadCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("read", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Parse(
+ void
+ )
+ {
+ if (CCommand::Parse() == false)
+ {
+ return false;
+ }
+
+ if (PopNumberParameter(Parameters, 10, &Length) == false)
+ {
+ printf("Length required\n");
+ return false;
+ }
+
+ if (Length > 0)
+ {
+ Buffer = new BYTE[Length];
+ ZeroMemory(Buffer, Length);
+ }
+ else
+ {
+ Buffer = nullptr;
+ }
+
+ return true;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+
+class CWriteCommand : public CCommand
+{
+private:
+ ULONG Length;
+
+public:
+
+ CWriteCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("write", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Parse(
+ VOID
+ )
+ {
+ if (CCommand::Parse() == false)
+ {
+ return false;
+ }
+
+ pair<ULONG, PBYTE> buf;
+
+ if (PopBufferParameter(Parameters, &buf) == false)
+ {
+ printf("Buffer required\n");
+ return false;
+ }
+
+ Length = buf.first;
+ Buffer = buf.second;
+
+ return true;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CWriteReadCommand : public CCommand
+{
+private:
+ ULONG WriteLength;
+ ULONG ReadLength;
+
+ PBYTE WriteBuffer;
+
+public:
+
+ CWriteReadCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("writeread", Parameters)
+ {
+ WriteBuffer = NULL;
+ return;
+ }
+
+ ~CWriteReadCommand(
+ void
+ )
+ {
+ delete[] WriteBuffer;
+ }
+
+ bool
+ Parse(
+ void
+ )
+ {
+ if (CCommand::Parse() == false)
+ {
+ return false;
+ }
+
+ pair<ULONG, PBYTE> buf;
+
+ if (PopBufferParameter(Parameters, &buf) == false)
+ {
+ printf("Buffer required\n");
+ return false;
+ }
+
+ WriteLength = buf.first;
+ WriteBuffer = buf.second;
+
+ if (PopNumberParameter(Parameters, 10, &ReadLength) == false)
+ {
+ printf("Length required\n");
+ return false;
+ }
+
+ if (ReadLength > 0)
+ {
+ Buffer = new BYTE[ReadLength];
+ ZeroMemory(Buffer, ReadLength);
+ }
+ else
+ {
+ Buffer = nullptr;
+ }
+
+ return true;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CFullDuplexCommand : public CCommand
+{
+private:
+ ULONG WriteLength;
+ ULONG ReadLength;
+
+ PBYTE WriteBuffer;
+
+public:
+
+ CFullDuplexCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("fullduplex", Parameters)
+ {
+ WriteBuffer = NULL;
+ return;
+ }
+
+ ~CFullDuplexCommand(
+ void
+ )
+ {
+ delete[] WriteBuffer;
+ }
+
+ bool
+ Parse(
+ void
+ )
+ {
+ if (CCommand::Parse() == false)
+ {
+ return false;
+ }
+
+ pair<ULONG, PBYTE> buf;
+
+ if (PopBufferParameter(Parameters, &buf) == false)
+ {
+ printf("Buffer required\n");
+ return false;
+ }
+
+ WriteLength = buf.first;
+ WriteBuffer = buf.second;
+
+ if (PopNumberParameter(Parameters, 10, &ReadLength) == false)
+ {
+ printf("Length required\n");
+ return false;
+ }
+
+ if (ReadLength > 0)
+ {
+ Buffer = new BYTE[ReadLength];
+ ZeroMemory(Buffer, ReadLength);
+ }
+ else
+ {
+ Buffer = nullptr;
+ }
+
+ return true;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CSignalInterruptCommand : public CCommand
+{
+private:
+
+public:
+ CSignalInterruptCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("signal", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CWaitOnInterruptCommand : public CCommand
+{
+private:
+
+public:
+ CWaitOnInterruptCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("waitoninterrupt", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
diff --git a/SPB/SpbTestTool/exe/internal.h b/SPB/SpbTestTool/exe/internal.h
new file mode 100644
index 00000000..268186c0
--- /dev/null
+++ b/SPB/SpbTestTool/exe/internal.h
@@ -0,0 +1,99 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ internal.h
+
+Abstract:
+
+ This module contains the internal type definitations and
+ helper function declarations for the SpbTestTool app.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include <string>
+#include <map>
+#include <list>
+#include <map>
+#include <functional>
+
+#include <iomanip>
+
+#include <cstdlib>
+
+#include <stdexcept>
+
+#include <math.h>
+
+#include <wchar.h>
+#include <windows.h>
+#include <winioctl.h>
+#include <specstrings.h>
+
+#include "spbtestioctl.h"
+
+using namespace std;
+
+//
+// Global Variables
+//
+
+extern HANDLE g_Peripheral;
+
+typedef pair<ULONG, PBYTE> BUFPAIR;
+typedef list<BUFPAIR> BUFLIST;
+
+VOID
+PrintBytes(
+ _In_ ULONG BufferCb,
+ _In_reads_bytes_(BufferCb) BYTE Buffer[]
+ );
+
+_Success_(return)
+bool
+PopStringParameter(
+ _Inout_ list<string> *Parameters,
+ _Out_ string *Value,
+ _Out_opt_ bool *Present = nullptr
+ );
+
+typedef pair<ULONG,ULONG> bounds;
+
+_Success_(return)
+bool
+PopNumberParameter(
+ _Inout_ list<string> *Parameters,
+ _In_ ULONG Radix,
+ _Out_ ULONG *Value,
+ _In_opt_ pair<ULONG,ULONG> Bounds = bounds(0,0),
+ _Out_opt_ bool *Present = nullptr
+ );
+
+_Success_(return)
+bool
+ParseNumber(
+ _In_ const string &String,
+ _In_ ULONG Radix,
+ _Out_ ULONG *Value,
+ _In_opt_ bounds Bounds = bounds(0,0)
+ );
+
+_Success_(return)
+bool
+PopBufferParameter(
+ _Inout_ list<string> *Parameters,
+ _Out_ pair<ULONG, PBYTE> *Value
+ );
+
+
+#define countof(x) (sizeof(x) / sizeof(x[0]))
+
+#include "command.h"
diff --git a/SPB/SpbTestTool/exe/main.cpp b/SPB/SpbTestTool/exe/main.cpp
new file mode 100644
index 00000000..ce9e994a
--- /dev/null
+++ b/SPB/SpbTestTool/exe/main.cpp
@@ -0,0 +1,633 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ main.cpp
+
+Abstract:
+
+ This module contains the main entry point for the SpbTestTool
+ app. It facilitates accepting input, building commands, and
+ printing output.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+
+DWORD WINAPI InterruptNotificationThread(
+ _In_ LPVOID pvData
+ );
+
+bool
+ReadCommandFromStream(
+ _In_ FILE* InputStream,
+ _Inout_ list<string> *Tokens
+ );
+
+VOID
+PrintUsage(
+ _In_ PCSTR exeName
+ );
+
+DWORD
+RunCommand(
+ _In_ PCCommand Command
+ );
+
+HANDLE g_Peripheral = nullptr;
+HANDLE g_Event = nullptr;
+HANDLE g_InterruptNotificationThread = nullptr;
+PCCommand g_CurrentCommand = nullptr;
+bool g_WaitOnInterrupt = true;
+
+BOOL
+WINAPI
+OnControlKey(
+ _In_ DWORD ControlType
+ );
+
+void
+__cdecl
+main(
+ _In_ ULONG ArgumentsCe,
+ _In_reads_(ArgumentsCe) PCSTR Arguments[]
+ )
+{
+ FILE* inputStream = stdin;
+ bool prompt = true;
+
+ PCSTR peripheralPath = nullptr;
+ PCSTR inputPath = nullptr;
+
+ //
+ // Parse the command line arguments.
+ //
+
+ ULONG arg = 1;
+
+ while (arg < ArgumentsCe)
+ {
+ if ((Arguments[arg][0] != '/') &&
+ (Arguments[arg][0] != '-'))
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ else
+ {
+ if (tolower(Arguments[arg][1]) == 'p')
+ {
+ arg++;
+ if (arg == ArgumentsCe)
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ peripheralPath = Arguments[arg];
+ }
+ else if (tolower(Arguments[arg][1]) == 'i')
+ {
+ arg++;
+ if (arg == ArgumentsCe)
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ inputPath = Arguments[arg];
+ }
+ else
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ }
+
+ arg++;
+ }
+
+ //
+ // Open the input file if specified
+ //
+
+ if (inputPath != nullptr)
+ {
+ errno_t error;
+
+ printf("Opening %s as command input file\n", inputPath);
+ error = fopen_s(&inputStream, inputPath, "r");
+
+ if (error != 0)
+ {
+ printf("Error opening input file %s - %d\n", inputPath, error);
+ goto exit;
+ }
+
+ if (inputStream == nullptr)
+ {
+ printf("Error opening input file %s - %d\n", inputPath, error);
+ goto exit;
+ }
+
+ prompt = false;
+ }
+
+ //
+ // Open peripheral driver
+ //
+
+ if (peripheralPath == nullptr)
+ {
+ g_Peripheral = CreateFileW(
+ SPBTESTTOOL_USERMODE_PATH,
+ (GENERIC_READ | GENERIC_WRITE),
+ 0,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ nullptr);
+ }
+ else
+ {
+ printf("Opening %s as peripheral driver path \n", peripheralPath);
+ g_Peripheral = CreateFileA(
+ peripheralPath,
+ (GENERIC_READ | GENERIC_WRITE),
+ 0,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ nullptr);
+ }
+
+ if (g_Peripheral == INVALID_HANDLE_VALUE)
+ {
+ printf("Error opening peripheral driver - %u\n", GetLastError());
+ goto exit;
+ }
+
+ setvbuf(inputStream, nullptr, _IONBF, 0);
+ setvbuf(stdout, nullptr, _IONBF, 0);
+ setvbuf(stderr, nullptr, _IONBF, 0);
+
+ //
+ // Setup a control-C handler.
+ //
+
+ if (SetConsoleCtrlHandler(OnControlKey, true) == FALSE)
+ {
+ printf("Error setting ctrl-C handler - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Setup an overlapped structure to use with each I/O.
+ //
+
+ g_Event = CreateEvent(nullptr, true, false, nullptr);
+
+ if (g_Event == nullptr)
+ {
+ printf("error creating I/O event - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Create interrupt notification thread.
+ //
+
+ g_InterruptNotificationThread = CreateThread(
+ nullptr,
+ 0,
+ &InterruptNotificationThread,
+ nullptr,
+ 0,
+ nullptr);
+
+ if (g_InterruptNotificationThread == nullptr)
+ {
+ printf("error creating interrupt notification thread - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Loop reading commands off the command line and parsing them.
+ // EOF causes an exit.
+ //
+
+ do
+ {
+ list<string> *tokens = new list<string>();
+ PCCommand command;
+
+ if (prompt)
+ {
+ printf("> ");
+ fflush(stdout);
+ }
+
+ if (ReadCommandFromStream(inputStream, tokens) == false)
+ {
+ delete tokens;
+ break;
+ }
+
+ if (tokens->empty())
+ {
+ delete tokens;
+ continue;
+ }
+
+ command = CCommand::_ParseCommand(tokens);
+
+ if (command == nullptr)
+ {
+ delete tokens;
+ continue;
+ }
+
+ g_CurrentCommand = command;
+
+ RunCommand(command);
+
+ g_CurrentCommand = nullptr;
+
+ //
+ // A reference to the tokens list is saved by
+ // the command and will be freed when the command
+ // is deleted.
+ //
+
+ delete command;
+
+ }
+ while (feof(inputStream) == 0);
+
+exit:
+
+ if (prompt == false)
+ {
+ fclose(inputStream);
+ }
+
+ if (g_InterruptNotificationThread != nullptr)
+ {
+ WaitForSingleObject(g_InterruptNotificationThread, INFINITE);
+ CloseHandle(g_InterruptNotificationThread);
+ }
+
+ CloseHandle(g_Peripheral);
+
+ if (g_Event != nullptr)
+ {
+ CloseHandle(g_Event);
+ }
+
+ return;
+}
+
+DWORD WINAPI InterruptNotificationThread(
+ _In_ LPVOID pvData
+ )
+{
+ DWORD status;
+ DWORD bytesReturned;
+ OVERLAPPED ov = {0};
+
+ UNREFERENCED_PARAMETER(pvData);
+
+ ov.hEvent = CreateEvent(nullptr, false, false, nullptr);
+
+ if (ov.hEvent == nullptr)
+ {
+ printf("error creating overlapped event for interrupt thread - %u\n", GetLastError());
+ goto exit;
+ }
+
+ while (g_WaitOnInterrupt == true)
+ {
+ if ((DeviceIoControl(
+ g_Peripheral,
+ IOCTL_SPBTESTTOOL_WAIT_ON_INTERRUPT,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ &ov) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ printf("failed to pend WaitOnInterrupt IOCTL- %u\n", GetLastError());
+ goto exit;
+ }
+
+ status = WaitForSingleObject(ov.hEvent, INFINITE);
+
+ switch (status)
+ {
+ // DeviceIoControl completed.
+ case WAIT_OBJECT_0:
+
+ if (!GetOverlappedResult(g_Peripheral, &ov, &bytesReturned, FALSE))
+ {
+ printf("GetOverlappedResult failed with status: %u\n\n", GetLastError());
+ }
+ else
+ {
+ printf("\n\n");
+ printf(" ** Interrupt detected. Please acknowledge or disable **\n");
+ printf(" *** and type 'signal' to inform the SpbTestTool driver ***\n");
+ printf(" ** that the interrupt has been handled. **\n");
+ printf("\n");
+ }
+ break;
+
+ default:
+ // Error in the WaitForSingleObject; abort.
+ // This indicates a problem with the OVERLAPPED
+ // structure's event handle.
+ printf("WaitOnInterrupt unexpected return %u\n\n", status);
+ break;
+ }
+ }
+
+exit:
+
+ return 0;
+}
+
+VOID
+PrintUsage(
+ _In_ PCSTR exeName
+ )
+{
+ printf("Usage: %s [/p <driver_path>] [/i <script_name>]\n",
+ exeName);
+}
+
+DWORD
+RunCommand(
+ _In_ PCCommand Command
+ )
+{
+
+ DWORD status;
+ DWORD bytesTransferred;
+
+ Command->Overlapped.hEvent = g_Event;
+
+ if (Command->Execute() == true)
+ {
+ if (GetOverlappedResult(Command->File,
+ &(Command->Overlapped),
+ &bytesTransferred,
+ true) == FALSE)
+ {
+ status = GetLastError();
+ }
+ else
+ {
+ status = NO_ERROR;
+ }
+
+ Command->Complete(status, bytesTransferred);
+ }
+ else
+ {
+ status = GetLastError();
+ }
+
+ return status;
+}
+
+
+bool
+ReadLine(
+ _In_ FILE* InputStream,
+ _In_ ULONG BufferCch,
+ _Out_writes_(BufferCch) CHAR Buffer[]
+ )
+{
+ ULONG i;
+
+ for(i = 0; i < BufferCch - 1; i += 1)
+ {
+ if (fread(&(Buffer[i]), sizeof(CHAR), 1, InputStream) == 0)
+ {
+ return false;
+ }
+
+ fputc(Buffer[i], stdout);
+
+ if (Buffer[i] == '\n')
+ {
+ Buffer[i] = '\0';
+ return true;
+ }
+ else if (Buffer[i] == '\b')
+ {
+ Buffer[i] = '\0';
+ i -= 2;
+ }
+ }
+
+ return false;
+}
+
+bool
+ReadCommandFromStream(
+ _In_ FILE* InputStream,
+ _Inout_ list<string> *Tokens
+ )
+{
+ CHAR buffer[255];
+
+ if (ReadLine(InputStream,
+ sizeof(buffer) - 1,
+ buffer) == false)
+ {
+ return false;
+ }
+
+ string currentLine(buffer);
+ string token;
+
+ list<string> tokens;
+
+ Tokens->clear();
+
+ string::size_type i;
+ long start;
+
+ for(i = 0, start = -1;
+ i < currentLine.length() + 1;
+ i += 1)
+ {
+ wchar_t c;
+
+ if (i < currentLine.length())
+ {
+ c = currentLine[i];
+ }
+ else
+ {
+ c = L'\0';
+ }
+
+ if (iswspace(c) || (c == L'\0'))
+ {
+ if (start == -1)
+ {
+ //
+ // not tracking a token - whitespace is skipped.
+ //
+ }
+ else
+ {
+ //
+ // tracking a token - whitespace or NUL terminates it.
+ // if the last character was a } then split the token.
+ //
+
+ if (currentLine[i-1] == '}')
+ {
+ token.assign(currentLine, start, i - start - 1);
+ Tokens->insert(Tokens->end(), token);
+ Tokens->insert(Tokens->end(), string("}"));
+ }
+ else
+ {
+ token.assign(currentLine, start, i - start);
+ Tokens->insert(Tokens->end(), token);
+ }
+
+ start = -1;
+ }
+ }
+ else if (start == -1)
+ {
+ //
+ // first character of a token. If it's { then split the token.
+ //
+
+ if (currentLine[i] == '{')
+ {
+ Tokens->insert(Tokens->end(), string("{"));
+ }
+ else if (currentLine[i] == '}')
+ {
+ Tokens->insert(Tokens->end(), string("}"));
+ }
+ else
+ {
+ start = (long) i;
+ }
+ }
+ else
+ {
+ //
+ // tracking a token - non whitespace is included.
+ //
+ }
+ }
+
+ return true;
+}
+
+VOID
+PrintBytes(
+ _In_ ULONG BufferCb,
+ _In_reads_bytes_(BufferCb) BYTE Buffer[]
+ )
+{
+ ULONG index = 0;
+
+ for(index = 0; index < BufferCb; index += 16)
+ {
+ printf(" ");
+ for(ULONG i = index; i < (index + 16); i += 1)
+ {
+ if (i < BufferCb)
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf("- ");
+ }
+
+ printf("%02x ", Buffer[i]);
+ }
+ else
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf(" ");
+ }
+ printf(" ");
+ }
+ }
+
+ printf(" : ");
+
+ for(ULONG i = index; i < (index + 16); i += 1)
+ {
+ if (i < BufferCb)
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf(" ");
+ }
+
+ if (isprint(Buffer[i]))
+ {
+ printf("%c", Buffer[i]);
+ }
+ else
+ {
+ printf(".");
+ }
+ }
+ else
+ {
+ printf(" ");
+ }
+ }
+
+ printf("\n");
+ }
+}
+
+BOOL
+WINAPI
+OnControlKey(
+ _In_ DWORD ControlType
+ )
+{
+ if (ControlType == CTRL_C_EVENT)
+ {
+ //
+ // If there's a current command then attempt to cancel it.
+ //
+
+ if (g_CurrentCommand != nullptr)
+ {
+ g_CurrentCommand->Cancel();
+ return TRUE;
+ }
+
+ //
+ // Stop the interrupt notification thread
+ //
+
+ g_WaitOnInterrupt = false;
+ CancelIo(g_Peripheral);
+ }
+
+ return FALSE;
+}
diff --git a/SPB/SpbTestTool/exe/util.cpp b/SPB/SpbTestTool/exe/util.cpp
new file mode 100644
index 00000000..fe6067c9
--- /dev/null
+++ b/SPB/SpbTestTool/exe/util.cpp
@@ -0,0 +1,250 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ util.cpp
+
+Abstract:
+
+ This module contains the internal helper function definitions
+ for the SpbTestTool app.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+
+_Success_(return)
+bool
+PopStringParameter(
+ _Inout_ list<string> *Parameters,
+ _Out_ string *Value,
+ _Out_opt_ bool *Present
+ )
+{
+ if (Parameters->empty())
+ {
+ if (Present != nullptr)
+ {
+ *Present = false;
+ *Value = string("");
+ return true;
+ }
+ else
+ {
+ printf("Missing required parameter\n");
+ return false;
+ }
+ }
+
+ if (Present != nullptr)
+ {
+ *Present = true;
+ }
+
+ *Value = Parameters->front();
+ Parameters->pop_front();
+ return true;
+}
+
+_Success_(return)
+bool
+ParseNumber(
+ _In_ const string &String,
+ _In_ ULONG Radix,
+ _Out_ ULONG *Value,
+ _In_opt_ bounds Bounds
+ )
+{
+ PSTR end;
+
+#pragma prefast(suppress:__WARNING_MISSING_ZERO_TERMINATION2 ,"zero-termination is checked below")
+ *Value = strtoul(String.c_str(), &end, Radix);
+
+ //
+ // Make sure the entire string parsed.
+ //
+
+ if (*end != '\0')
+ {
+ printf("Value %s is not a number\n", String.c_str());
+ return false;
+ }
+
+ //
+ // See if we should do a bounds check.
+ //
+
+ if ((Bounds.first != 0) || (Bounds.second != 0))
+ {
+ if ((*Value < Bounds.first) ||
+ (*Value > Bounds.second))
+ {
+ printf("Value %s is out of bounds\n", String.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+_Success_(return)
+bool
+PopNumberParameter(
+ _Inout_ list<string> *Parameters,
+ _In_ ULONG Radix,
+ _Out_ ULONG *Value,
+ _In_opt_ bounds Bounds,
+ _Out_opt_ bool *Present
+ )
+{
+ string s;
+
+ bool found = PopStringParameter(Parameters, &s, Present);
+
+ if ((Present != nullptr) && (*Present == false))
+ {
+ *Value = Bounds.first;
+ return true;
+ }
+ else if (found == false)
+ {
+ return false;
+ }
+
+ return ParseNumber(s, Radix, Value, Bounds);
+}
+
+_Success_(return)
+bool
+PopBufferParameter(
+ _Inout_ list<string> *Parameters,
+ _Out_ pair<ULONG, PBYTE> *Value
+ )
+{
+ ULONG length = 0;
+ PBYTE buffer = nullptr;
+
+ //
+ // Check for an explict length
+ //
+
+ if (Parameters->front() != "{")
+ {
+ if (PopNumberParameter(Parameters, 10, &length) == false)
+ {
+ printf("Length expected\n");
+ return false;
+ }
+
+ //
+ // Consume any leading {
+ //
+
+ if ((Parameters->empty() == false) &&
+ (Parameters->front() == "{"))
+ {
+ Parameters->pop_front();
+ }
+ }
+ else
+ {
+ string tmp;
+ if (PopStringParameter(Parameters, &tmp) == false)
+ {
+ return false;
+ }
+ else if (tmp != "{")
+ {
+ printf("output buffer must start with {\n");
+ return false;
+ }
+
+ //
+ // Count values until the trailing } - assume one byte per value
+ //
+
+ list<string>::iterator i;
+
+ for(i = Parameters->begin();
+ ((i != Parameters->end()) && (*i != "}"));
+ i++)
+ {
+ length += 1;
+ }
+
+ if (*i != "}")
+ {
+ printf("output buffer must end with }\n");
+ return false;
+ }
+ }
+
+ if (length != 0)
+ {
+ ULONG b = 0;
+ bool bufferEnd = false;
+
+ //
+ // Allocate the buffer.
+ //
+
+ buffer = new BYTE[length];
+
+ for(b = 0; b < length; b += 1)
+ {
+ ULONG value = 0;
+
+ if (bufferEnd == false)
+ {
+ string nextElement;
+
+ PopStringParameter(Parameters, &nextElement);
+
+ if (nextElement == "}")
+ {
+ value = 0;
+ bufferEnd = true;
+ }
+ else if (ParseNumber(nextElement, 16, &value, bounds(0, 0xff)) == false)
+ {
+ printf("invalid byte value %s\n", nextElement.c_str());
+ delete [] buffer;
+ return false;
+ }
+ }
+
+ buffer[b] = (BYTE) value;
+ }
+
+ if (bufferEnd == false)
+ {
+ string end;
+
+ if (PopStringParameter(Parameters, &end) == false)
+ {
+ printf("unclosed buffer\n");
+ delete [] buffer;
+ return false;
+ }
+ else if (end != "}")
+ {
+ printf("buffer has too many initializers\n");
+ delete [] buffer;
+ return false;
+ }
+ }
+ }
+
+ Value->first = length;
+ Value->second = buffer;
+
+ return true;
+}
diff --git a/SPB/SpbTestTool/sys/SpbTestTool.vcxproj b/SPB/SpbTestTool/sys/SpbTestTool.vcxproj
new file mode 100644
index 00000000..48b8d23c
--- /dev/null
+++ b/SPB/SpbTestTool/sys/SpbTestTool.vcxproj
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{028ED946-EE58-43F7-834B-297119CE4720}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{775D4A74-74A0-4CBD-B20E-06D7EAEEAB7C}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="driver.cpp; device.cpp; peripheral.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>trace.h</WppScanConfigurationData>
+ <WppTraceFunction>Trace(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ </ClCompile>
+ <Inf Include=".\SpbTestTool.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\SpbTestTool.inf</CopyOutput>
+ </Inf>
+ <OtherWpp Include="resource.rc">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>trace.h</WppScanConfigurationData>
+ <WppTraceFunction>Trace(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/SPB/SpbTestTool/sys/SpbTestTool.vcxproj.Filters b/SPB/SpbTestTool/sys/SpbTestTool.vcxproj.Filters
new file mode 100644
index 00000000..5c06c882
--- /dev/null
+++ b/SPB/SpbTestTool/sys/SpbTestTool.vcxproj.Filters
@@ -0,0 +1,42 @@
+<?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>{758F79B8-0F82-4FC8-8C5B-0875A36D16C8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{BB2FE3BE-C3C8-4C98-A8FA-CE4AE906C0BC}</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>{B0A30E36-D110-468D-A945-84112CBE25B8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{A2DE30AB-8982-454D-976D-E8F3EB8B4777}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="driver.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="peripheral.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Include=".\SpbTestTool.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/SPB/SpbTestTool/sys/device.cpp b/SPB/SpbTestTool/sys/device.cpp
new file mode 100644
index 00000000..28455aa6
--- /dev/null
+++ b/SPB/SpbTestTool/sys/device.cpp
@@ -0,0 +1,848 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ device.cpp
+
+Abstract:
+
+ This module contains WDF device initialization
+ functions for the peripheral driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "peripheral.h"
+#include "device.h"
+
+#include "device.tmh"
+
+
+/////////////////////////////////////////////////
+//
+// WDF callbacks.
+//
+/////////////////////////////////////////////////
+
+NTSTATUS
+OnPrepareHardware(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDFCMRESLIST FxResourcesRaw,
+ _In_ WDFCMRESLIST FxResourcesTranslated
+ )
+/*++
+
+ Routine Description:
+
+ This routine caches the SPB resource connection ID.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxResourcesRaw - list of translated hardware resources that
+ the PnP manager has assigned to the device
+ FxResourcesTranslated - list of raw hardware resources that
+ the PnP manager has assigned to the device
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PDEVICE_CONTEXT pDevice = GetDeviceContext(FxDevice);
+ BOOLEAN fSpbResourceFound = FALSE;
+ BOOLEAN fInterruptResourceFound = FALSE;
+ ULONG interruptIndex = 0;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(FxResourcesRaw);
+
+ //
+ // Parse the peripheral's resources.
+ //
+
+ ULONG resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated);
+
+ for(ULONG i = 0; i < resourceCount; i++)
+ {
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;
+ UCHAR Class;
+ UCHAR Type;
+
+ pDescriptor = WdfCmResourceListGetDescriptor(
+ FxResourcesTranslated, i);
+
+ switch (pDescriptor->Type)
+ {
+ case CmResourceTypeConnection:
+
+ //
+ // Look for I2C or SPI resource and save connection ID.
+ //
+
+ Class = pDescriptor->u.Connection.Class;
+ Type = pDescriptor->u.Connection.Type;
+
+ if ((Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL) &&
+ ((Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) ||
+ (Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_SPI)))
+ {
+ if (fSpbResourceFound == FALSE)
+ {
+ pDevice->PeripheralId.LowPart =
+ pDescriptor->u.Connection.IdLowPart;
+ pDevice->PeripheralId.HighPart =
+ pDescriptor->u.Connection.IdHighPart;
+
+ fSpbResourceFound = TRUE;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_WDFLOADING,
+ "SPB resource found with ID=0x%llx",
+ pDevice->PeripheralId.QuadPart);
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Duplicate SPB resource found with ID=0x%llx",
+ pDevice->PeripheralId.QuadPart);
+ }
+ }
+
+ break;
+
+ case CmResourceTypeInterrupt:
+
+ if (fInterruptResourceFound == FALSE)
+ {
+ fInterruptResourceFound = TRUE;
+ interruptIndex = i;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_WDFLOADING,
+ "Interrupt resource found");
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Duplicate interrupt resource found");
+ }
+
+ break;
+
+ default:
+
+ //
+ // Ignoring all other resource types.
+ //
+
+ break;
+ }
+ }
+
+ //
+ // An SPB resource is required.
+ //
+
+ if (fSpbResourceFound == FALSE)
+ {
+ status = STATUS_NOT_FOUND;
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "SPB resource not found - %!STATUS!",
+ status);
+ }
+
+ //
+ // Create the interrupt if an interrupt
+ // resource was found.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ if ((pDevice->ConnectInterrupt == TRUE) &&
+ (fInterruptResourceFound == TRUE))
+ {
+ WDF_INTERRUPT_CONFIG interruptConfig;
+ WDF_INTERRUPT_CONFIG_INIT(
+ &interruptConfig,
+ OnInterruptIsr,
+ NULL);
+
+ interruptConfig.PassiveHandling = TRUE;
+ interruptConfig.InterruptTranslated = WdfCmResourceListGetDescriptor(
+ FxResourcesTranslated,
+ interruptIndex);
+ interruptConfig.InterruptRaw = WdfCmResourceListGetDescriptor(
+ FxResourcesRaw,
+ interruptIndex);
+
+ status = WdfInterruptCreate(
+ pDevice->FxDevice,
+ &interruptConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevice->Interrupt);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "WdfInterruptCreate failed - %!STATUS!",
+ status);
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ KeInitializeEvent(
+ &pDevice->IsrWaitEvent,
+ SynchronizationEvent,
+ FALSE);
+ }
+ }
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnReleaseHardware(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDFCMRESLIST FxResourcesTranslated
+ )
+/*++
+
+ Routine Description:
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxResourcesTranslated - list of raw hardware resources that
+ the PnP manager has assigned to the device
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PDEVICE_CONTEXT pDevice = GetDeviceContext(FxDevice);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(FxResourcesTranslated);
+
+ if (pDevice->Interrupt != nullptr)
+ {
+ WdfObjectDelete(pDevice->Interrupt);
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnD0Entry(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDF_POWER_DEVICE_STATE FxPreviousState
+ )
+/*++
+
+ Routine Description:
+
+ This routine allocates objects needed by the driver.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxPreviousState - previous power state
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ UNREFERENCED_PARAMETER(FxPreviousState);
+
+ PDEVICE_CONTEXT pDevice = GetDeviceContext(FxDevice);
+ NTSTATUS status;
+
+ //
+ // Create the SPB target.
+ //
+
+ WDF_OBJECT_ATTRIBUTES targetAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&targetAttributes);
+
+ status = WdfIoTargetCreate(
+ pDevice->FxDevice,
+ &targetAttributes,
+ &pDevice->SpbController);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create IO target - %!STATUS!",
+ status);
+ }
+
+ //
+ // InputMemory will be created when an SPB request is about to be
+ // sent. Indicate that it is not yet initialized.
+ //
+
+ pDevice->InputMemory = WDF_NO_HANDLE;
+
+ //
+ // Create the SPB request.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ WDF_OBJECT_ATTRIBUTES requestAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttributes, REQUEST_CONTEXT);
+
+ status = WdfRequestCreate(
+ &requestAttributes,
+ nullptr,
+ &pDevice->SpbRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create IO request - %!STATUS!",
+ status);
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ PREQUEST_CONTEXT pRequest = GetRequestContext(
+ pDevice->SpbRequest);
+
+ pRequest->FxDevice = pDevice->FxDevice;
+ pRequest->IsSpbSequenceRequest = FALSE;
+ pRequest->SequenceWriteLength = 0;
+ }
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnD0Exit(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDF_POWER_DEVICE_STATE FxPreviousState
+ )
+/*++
+
+ Routine Description:
+
+ This routine destroys objects needed by the driver.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxPreviousState - previous power state
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ UNREFERENCED_PARAMETER(FxPreviousState);
+
+ PDEVICE_CONTEXT pDevice = GetDeviceContext(FxDevice);
+
+ if (pDevice->SpbController != WDF_NO_HANDLE)
+ {
+ WdfObjectDelete(pDevice->SpbController);
+ pDevice->SpbController = WDF_NO_HANDLE;
+ }
+
+ if (pDevice->SpbRequest != WDF_NO_HANDLE)
+ {
+ WdfObjectDelete(pDevice->SpbRequest);
+ pDevice->SpbRequest = WDF_NO_HANDLE;
+ }
+
+ if (pDevice->InputMemory != WDF_NO_HANDLE)
+ {
+ WdfObjectDelete(pDevice->InputMemory);
+ pDevice->InputMemory = WDF_NO_HANDLE;
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+OnFileCleanup(
+ _In_ WDFFILEOBJECT FileObject
+ )
+/*++
+
+ Routine Description:
+
+ This routine is called before a device is stopped.
+
+ Arguments:
+
+ FileObject - a handle to the framework file object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+
+ device = WdfFileObjectGetDevice(FileObject);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // The client app has closed the device handle,
+ // make sure the SPB target is closed.
+ //
+
+ SpbPeripheralClose(pDevice);
+
+ //
+ // Signal the ISR wait event just
+ // in case the callback is stalled.
+ //
+
+ if (pDevice->Interrupt != nullptr)
+ {
+ KeSetEvent(
+ &pDevice->IsrWaitEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+}
+
+VOID
+OnTopLevelIoDefault(
+ _In_ WDFQUEUE FxQueue,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ Accepts all incoming requests and pends or forwards appropriately.
+
+ Arguments:
+
+ FxQueue - Handle to the framework queue object that is associated with the
+ I/O request.
+ FxRequest - Handle to a framework request object.
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxQueue);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+ WDF_REQUEST_PARAMETERS params;
+ NTSTATUS status;
+
+ device = WdfIoQueueGetDevice(FxQueue);
+ pDevice = GetDeviceContext(device);
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(FxRequest, &params);
+
+ if ((params.Type == WdfRequestTypeDeviceControl) &&
+ (params.Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_SPBTESTTOOL_WAIT_ON_INTERRUPT))
+ {
+ SpbPeripheralWaitOnInterrupt(pDevice, FxRequest);
+ }
+ else
+ {
+ status = WdfRequestForwardToIoQueue(FxRequest, pDevice->SpbQueue);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to forward WDFREQUEST %p to SPB queue %p - %!STATUS!",
+ FxRequest,
+ pDevice->SpbQueue,
+ status);
+
+ WdfRequestComplete(FxRequest, status);
+ }
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+OnIoRead (
+ _In_ WDFQUEUE FxQueue,
+ _In_ WDFREQUEST FxRequest,
+ _In_ size_t Length
+ )
+/*++
+
+ Routine Description:
+
+ Performs read from the toaster device. This event is called when the
+ framework receives IRP_MJ_READ requests.
+
+ Arguments:
+
+ FxQueue - Handle to the framework queue object that is associated with the
+ I/O request.
+ FxRequest - Handle to a framework request object.
+ Length - Length of the data buffer associated with the request.
+ By default, the queue does not dispatch zero length read & write
+ requests to the driver and instead to complete such requests with
+ status success. So we will never get a zero length request.
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(Length);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Read request %p received",
+ FxRequest);
+
+ device = WdfIoQueueGetDevice(FxQueue);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // Send the read request.
+ //
+
+ SpbPeripheralRead(pDevice,FxRequest);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+OnIoWrite (
+ _In_ WDFQUEUE FxQueue,
+ _In_ WDFREQUEST FxRequest,
+ _In_ size_t Length
+ )
+/*++
+
+Routine Description:
+
+ Performs write to the toaster device. This event is called when the
+ framework receives IRP_MJ_WRITE requests.
+
+Arguments:
+
+ Queue - Handle to the framework queue object that is associated
+ with the I/O request.
+ Request - Handle to a framework request object.
+ Lenght - Length of the data buffer associated with the request.
+ The default property of the queue is to not dispatch
+ zero lenght read & write requests to the driver and
+ complete is with status success. So we will never get
+ a zero length request.
+
+Return Value:
+
+ None
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(Length);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Write request %p received",
+ FxRequest);
+
+ device = WdfIoQueueGetDevice(FxQueue);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // Send the write request.
+ //
+
+ SpbPeripheralWrite(pDevice,FxRequest);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+OnIoDeviceControl(
+ _In_ WDFQUEUE FxQueue,
+ _In_ WDFREQUEST FxRequest,
+ _In_ size_t OutputBufferLength,
+ _In_ size_t InputBufferLength,
+ _In_ ULONG IoControlCode
+ )
+/*++
+Routine Description:
+
+ This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
+ requests from the system.
+
+Arguments:
+
+ FxQueue - Handle to the framework queue object that is associated
+ with the I/O request.
+ FxRequest - Handle to a framework request object.
+ OutputBufferLength - length of the request's output buffer,
+ if an output buffer is available.
+ InputBufferLength - length of the request's input buffer,
+ if an input buffer is available.
+ IoControlCode - the driver-defined or system-defined I/O control code
+ (IOCTL) that is associated with the request.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+ BOOLEAN fSync = FALSE;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "DeviceIoControl request %p received with IOCTL=%lu",
+ FxRequest,
+ IoControlCode);
+
+ device = WdfIoQueueGetDevice(FxQueue);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // Translate the test IOCTL into the appropriate
+ // SPB API method. Open and close are completed
+ // synchronously.
+ //
+
+ switch (IoControlCode)
+ {
+ case IOCTL_SPBTESTTOOL_OPEN:
+ fSync = TRUE;
+ status = SpbPeripheralOpen(pDevice);
+ break;
+
+ case IOCTL_SPBTESTTOOL_CLOSE:
+ fSync = TRUE;
+ status = SpbPeripheralClose(pDevice);
+ break;
+
+ case IOCTL_SPBTESTTOOL_LOCK:
+ SpbPeripheralLock(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_UNLOCK:
+ SpbPeripheralUnlock(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_LOCK_CONNECTION:
+ SpbPeripheralLockConnection(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_UNLOCK_CONNECTION:
+ SpbPeripheralUnlockConnection(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_WRITEREAD:
+ SpbPeripheralWriteRead(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_FULL_DUPLEX:
+ SpbPeripheralFullDuplex(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_SIGNAL_INTERRUPT:
+ SpbPeripheralSignalInterrupt(pDevice, FxRequest);
+ break;
+
+ default:
+ fSync = TRUE;
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Request %p received with unexpected IOCTL=%lu",
+ FxRequest,
+ IoControlCode);
+ }
+
+ //
+ // Complete the request if necessary.
+ //
+
+ if (fSync)
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Completing request %p with %!STATUS!",
+ FxRequest,
+ status);
+
+ WdfRequestComplete(FxRequest, status);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+BOOLEAN
+OnInterruptIsr(
+ _In_ WDFINTERRUPT FxInterrupt,
+ _In_ ULONG MessageID
+ )
+/*++
+
+ Routine Description:
+
+ This routine responds to interrupts generated by the H/W.
+ It then waits indefinitely for the user to signal that
+ the interrupt has been acknowledged, allowing the ISR to
+ return. This ISR is called at PASSIVE_LEVEL.
+
+ Arguments:
+
+ Interrupt - a handle to a framework interrupt object
+ MessageID - message number identifying the device's
+ hardware interrupt message (if using MSI)
+
+ Return Value:
+
+ TRUE if interrupt recognized.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ BOOLEAN fInterruptRecognized = TRUE;
+ BOOLEAN fNotificationSent;
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+
+ UNREFERENCED_PARAMETER(MessageID);
+
+ device = WdfInterruptGetDevice(FxInterrupt);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // Notify the app that an interrupt has occurred.
+ //
+
+ fNotificationSent = SpbPeripheralInterruptNotify(pDevice);
+
+ if (fNotificationSent)
+ {
+ //
+ // Stall in ISR until acknowledged by user.
+ //
+ // Note: In a 'real' driver, the ISR should directly
+ // acknowledge the interrupt and then queue
+ // a workitem to carry out any additional
+ // processing. The ISR should never call
+ // KeWaitForSingleObject as done below.
+ //
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Stalling in ISR until continue command received");
+
+ KeClearEvent(&pDevice->IsrWaitEvent);
+
+ KeWaitForSingleObject(
+ &pDevice->IsrWaitEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected, but failed to send notification, ignoring");
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return fInterruptRecognized;
+}
diff --git a/SPB/SpbTestTool/sys/device.h b/SPB/SpbTestTool/sys/device.h
new file mode 100644
index 00000000..40ec9904
--- /dev/null
+++ b/SPB/SpbTestTool/sys/device.h
@@ -0,0 +1,43 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ device.h
+
+Abstract:
+
+ This module contains the function definitions for
+ the WDF device.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _DEVICE_H_
+#define _DEVICE_H_
+
+//
+// WDF event callbacks.
+//
+
+EVT_WDF_DEVICE_PREPARE_HARDWARE OnPrepareHardware;
+EVT_WDF_DEVICE_RELEASE_HARDWARE OnReleaseHardware;
+EVT_WDF_DEVICE_D0_ENTRY OnD0Entry;
+EVT_WDF_DEVICE_D0_EXIT OnD0Exit;
+
+EVT_WDF_FILE_CLEANUP OnFileCleanup;
+
+EVT_WDF_IO_QUEUE_IO_DEFAULT OnTopLevelIoDefault;
+EVT_WDF_IO_QUEUE_IO_READ OnIoRead;
+EVT_WDF_IO_QUEUE_IO_WRITE OnIoWrite;
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OnIoDeviceControl;
+
+EVT_WDF_INTERRUPT_ISR OnInterruptIsr;
+
+#endif \ No newline at end of file
diff --git a/SPB/SpbTestTool/sys/driver.cpp b/SPB/SpbTestTool/sys/driver.cpp
new file mode 100644
index 00000000..253a8858
--- /dev/null
+++ b/SPB/SpbTestTool/sys/driver.cpp
@@ -0,0 +1,414 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ driver.cpp
+
+Abstract:
+
+ This module contains the WDF driver initialization
+ functions for the peripheral driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "driver.h"
+#include "device.h"
+#include "ntstrsafe.h"
+
+#include "driver.tmh"
+
+NTSTATUS
+#pragma prefast(suppress:__WARNING_DRIVER_FUNCTION_TYPE, "thanks, i know this already")
+DriverEntry(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+ )
+{
+ WDF_DRIVER_CONFIG driverConfig;
+ WDF_OBJECT_ATTRIBUTES driverAttributes;
+
+ WDFDRIVER fxDriver;
+
+ NTSTATUS status;
+
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ WDF_DRIVER_CONFIG_INIT(&driverConfig, OnDeviceAdd);
+ driverConfig.DriverPoolTag = SPBT_POOL_TAG;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&driverAttributes);
+ driverAttributes.EvtCleanupCallback = OnDriverCleanup;
+
+ status = WdfDriverCreate(
+ DriverObject,
+ RegistryPath,
+ &driverAttributes,
+ &driverConfig,
+ &fxDriver);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating WDF driver object - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_WDFLOADING,
+ "Created WDF driver object");
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+VOID
+OnDriverCleanup(
+ _In_ WDFOBJECT Object
+ )
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ UNREFERENCED_PARAMETER(Object);
+
+ WPP_CLEANUP(nullptr);
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+}
+
+NTSTATUS
+OnDeviceAdd(
+ _In_ WDFDRIVER FxDriver,
+ _Inout_ PWDFDEVICE_INIT FxDeviceInit
+ )
+/*++
+
+ Routine Description:
+
+ This routine creates the device object for an SPB
+ controller and the device's child objects.
+
+ Arguments:
+
+ FxDriver - the WDF driver object handle
+ FxDeviceInit - information about the PDO that we are loading on
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PDEVICE_CONTEXT pDevice;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(FxDriver);
+
+ //
+ // Setup PNP/Power callbacks.
+ //
+
+ {
+ WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks;
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks);
+
+ pnpCallbacks.EvtDevicePrepareHardware = OnPrepareHardware;
+ pnpCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware;
+ pnpCallbacks.EvtDeviceD0Entry = OnD0Entry;
+ pnpCallbacks.EvtDeviceD0Exit = OnD0Exit;
+
+ WdfDeviceInitSetPnpPowerEventCallbacks(FxDeviceInit, &pnpCallbacks);
+ }
+
+ //
+ // Prepare for file object handling.
+ //
+
+ {
+ WDF_FILEOBJECT_CONFIG fileObjectConfig;
+
+ WDF_FILEOBJECT_CONFIG_INIT(
+ &fileObjectConfig,
+ nullptr,
+ nullptr,
+ OnFileCleanup);
+
+ WDF_OBJECT_ATTRIBUTES fileObjectAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&fileObjectAttributes);
+
+ WdfDeviceInitSetFileObjectConfig(
+ FxDeviceInit,
+ &fileObjectConfig,
+ &fileObjectAttributes);
+ }
+
+ //
+ // Set request attributes.
+ //
+
+ {
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
+ &attributes,
+ REQUEST_CONTEXT);
+
+ WdfDeviceInitSetRequestAttributes(FxDeviceInit, &attributes);
+ }
+
+ //
+ // Create the device.
+ //
+
+ {
+ WDFDEVICE fxDevice;
+ WDF_OBJECT_ATTRIBUTES deviceAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
+
+ status = WdfDeviceCreate(
+ &FxDeviceInit,
+ &deviceAttributes,
+ &fxDevice);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating WDFDEVICE - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ pDevice = GetDeviceContext(fxDevice);
+ NT_ASSERT(pDevice != nullptr);
+
+ pDevice->FxDevice = fxDevice;
+ }
+
+ //
+ // Ensure device is disable-able
+ //
+
+ {
+ WDF_DEVICE_STATE deviceState;
+ WDF_DEVICE_STATE_INIT(&deviceState);
+
+ deviceState.NotDisableable = WdfFalse;
+ WdfDeviceSetDeviceState(pDevice->FxDevice, &deviceState);
+ }
+
+ //
+ // Create queues to handle IO
+ //
+
+ {
+ WDF_IO_QUEUE_CONFIG queueConfig;
+ WDFQUEUE queue;
+
+ //
+ // Top-level queue
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
+ &queueConfig,
+ WdfIoQueueDispatchParallel);
+
+ queueConfig.EvtIoDefault = OnTopLevelIoDefault;
+ queueConfig.PowerManaged = WdfFalse;
+
+ status = WdfIoQueueCreate(
+ pDevice->FxDevice,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &queue
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating top-level IO queue - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ //
+ // Sequential SPB queue
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT(
+ &queueConfig,
+ WdfIoQueueDispatchSequential);
+
+ queueConfig.EvtIoRead = OnIoRead;
+ queueConfig.EvtIoWrite = OnIoWrite;
+ queueConfig.EvtIoDeviceControl = OnIoDeviceControl;
+ queueConfig.PowerManaged = WdfFalse;
+
+ status = WdfIoQueueCreate(
+ pDevice->FxDevice,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevice->SpbQueue
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating SPB IO queue - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+ }
+
+ //
+ // Create a symbolic link.
+ //
+
+ {
+ DECLARE_UNICODE_STRING_SIZE(symbolicLinkName, 128);
+
+ status = RtlUnicodeStringPrintf(
+ &symbolicLinkName, L"%ws",
+ SPBTESTTOOL_SYMBOLIC_NAME);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating symbolic link string for device "
+ "- %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ status = WdfDeviceCreateSymbolicLink(
+ pDevice->FxDevice,
+ &symbolicLinkName);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating symbolic link for device "
+ "- %!STATUS!",
+ status);
+
+ goto exit;
+ }
+ }
+
+ //
+ // Retrieve registry settings.
+ //
+
+ {
+ WDFKEY key = NULL;
+ WDFKEY subkey = NULL;
+ ULONG connectInterrupt = 0;
+ NTSTATUS settingStatus;
+
+ DECLARE_CONST_UNICODE_STRING(subkeyName, L"Settings");
+ DECLARE_CONST_UNICODE_STRING(connectInterruptName, L"ConnectInterrupt");
+
+ settingStatus = WdfDeviceOpenRegistryKey(
+ pDevice->FxDevice,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &key);
+
+ if (!NT_SUCCESS(settingStatus))
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Error opening device registry key - %!STATUS!",
+ settingStatus);
+ }
+
+ if (NT_SUCCESS(settingStatus))
+ {
+ settingStatus = WdfRegistryOpenKey(
+ key,
+ &subkeyName,
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &subkey);
+
+ if (!NT_SUCCESS(settingStatus))
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Error opening registry subkey for 'Settings' - %!STATUS!",
+ settingStatus);
+ }
+ }
+
+ if (NT_SUCCESS(settingStatus))
+ {
+
+ settingStatus = WdfRegistryQueryULong(
+ subkey,
+ &connectInterruptName,
+ &connectInterrupt);
+
+ if (!NT_SUCCESS(settingStatus))
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Error querying registry value for 'ConnectInterrupt' - %!STATUS!",
+ settingStatus);
+ }
+ }
+
+ if (key != NULL)
+ {
+ WdfRegistryClose(key);
+ }
+
+ if (subkey != NULL)
+ {
+ WdfRegistryClose(subkey);
+ }
+
+ pDevice->ConnectInterrupt = (connectInterrupt == 1);
+ }
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
diff --git a/SPB/SpbTestTool/sys/driver.h b/SPB/SpbTestTool/sys/driver.h
new file mode 100644
index 00000000..1029377c
--- /dev/null
+++ b/SPB/SpbTestTool/sys/driver.h
@@ -0,0 +1,36 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ driver.h
+
+Abstract:
+
+ This module contains the function definitions for
+ the WDF driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _DRIVER_H_
+#define _DRIVER_H_
+
+extern "C"
+
+NTSTATUS
+DriverEntry(
+ _In_ PDRIVER_OBJECT pDriverObject,
+ _In_ PUNICODE_STRING pRegistryPath
+ );
+
+EVT_WDF_DRIVER_DEVICE_ADD OnDeviceAdd;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP OnDriverCleanup;
+
+#endif
diff --git a/SPB/SpbTestTool/sys/internal.h b/SPB/SpbTestTool/sys/internal.h
new file mode 100644
index 00000000..7e40560b
--- /dev/null
+++ b/SPB/SpbTestTool/sys/internal.h
@@ -0,0 +1,144 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ internal.h
+
+Abstract:
+
+ This module contains the common internal type and function
+ definitions for the SPB peripheral driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _INTERNAL_H_
+#define _INTERNAL_H_
+
+#pragma warning(push)
+#pragma warning(disable:4512)
+#pragma warning(disable:4480)
+
+#define SPBT_POOL_TAG ((ULONG) 'TBPS')
+
+/////////////////////////////////////////////////
+//
+// Common includes.
+//
+/////////////////////////////////////////////////
+
+#include <ntddk.h>
+#include <wdm.h>
+#include <wdf.h>
+#include <ntstrsafe.h>
+
+#include "spb.h"
+#include "spbtestioctl.h"
+
+#define RESHUB_USE_HELPER_ROUTINES
+#include "reshub.h"
+
+#include "trace.h"
+
+//
+// Forward Declarations
+//
+
+typedef struct _DEVICE_CONTEXT DEVICE_CONTEXT, *PDEVICE_CONTEXT;
+typedef struct _REQUEST_CONTEXT REQUEST_CONTEXT, *PREQUEST_CONTEXT;
+
+struct _DEVICE_CONTEXT
+{
+ //
+ // Handle back to the WDFDEVICE
+ //
+
+ WDFDEVICE FxDevice;
+
+ //
+ // Handle to the sequential SPB queue
+ //
+
+ WDFQUEUE SpbQueue;
+
+ //
+ // Connection ID for SPB peripheral
+ //
+
+ LARGE_INTEGER PeripheralId;
+
+ //
+ // Interrupt object and wait event
+ //
+
+ WDFINTERRUPT Interrupt;
+ KEVENT IsrWaitEvent;
+
+ //
+ // Setting indicating whether the interrupt should be connected
+ //
+
+ BOOLEAN ConnectInterrupt;
+
+ //
+ // SPB controller target
+ //
+
+ WDFIOTARGET SpbController;
+
+ //
+ // SPB request object
+ //
+
+ WDFREQUEST SpbRequest;
+
+ //
+ // Input memory for request. Valid while request in progress.
+ //
+
+ WDFMEMORY InputMemory;
+
+ //
+ // Client request object
+ //
+
+ WDFREQUEST ClientRequest;
+
+ //
+ // WaitOnInterrupt request object
+ //
+
+ WDFREQUEST WaitOnInterruptRequest;
+};
+
+struct _REQUEST_CONTEXT
+{
+ //
+ // Associated framework device object
+ //
+
+ WDFDEVICE FxDevice;
+
+ //
+ // Variables to track write length for a sequence request.
+ // There are needed to complete the client request with
+ // correct bytesReturned value.
+ //
+
+ BOOLEAN IsSpbSequenceRequest;
+ ULONG_PTR SequenceWriteLength;
+};
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext);
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT, GetRequestContext);
+
+#pragma warning(pop)
+
+#endif // _INTERNAL_H_
diff --git a/SPB/SpbTestTool/sys/peripheral.cpp b/SPB/SpbTestTool/sys/peripheral.cpp
new file mode 100644
index 00000000..c6966ada
--- /dev/null
+++ b/SPB/SpbTestTool/sys/peripheral.cpp
@@ -0,0 +1,1683 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ peripheral.cpp
+
+Abstract:
+
+ This module contains the function for interaction
+ with the SPB API.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "peripheral.h"
+
+#include "peripheral.tmh"
+
+NTSTATUS
+SpbPeripheralOpen(
+ _In_ PDEVICE_CONTEXT pDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine opens a handle to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ WDF_IO_TARGET_OPEN_PARAMS openParams;
+ NTSTATUS status;
+
+ //
+ // Create the device path using the connection ID.
+ //
+
+ DECLARE_UNICODE_STRING_SIZE(DevicePath, RESOURCE_HUB_PATH_SIZE);
+
+ RESOURCE_HUB_CREATE_PATH_FROM_ID(
+ &DevicePath,
+ pDevice->PeripheralId.LowPart,
+ pDevice->PeripheralId.HighPart);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Opening handle to SPB target via %wZ",
+ &DevicePath);
+
+ //
+ // Open a handle to the SPB controller.
+ //
+
+ WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
+ &openParams,
+ &DevicePath,
+ (GENERIC_READ | GENERIC_WRITE));
+
+ openParams.ShareAccess = 0;
+ openParams.CreateDisposition = FILE_OPEN;
+ openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+
+ status = WdfIoTargetOpen(
+ pDevice->SpbController,
+ &openParams);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to open SPB target - %!STATUS!",
+ status);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return status;
+}
+
+NTSTATUS
+SpbPeripheralClose(
+ _In_ PDEVICE_CONTEXT pDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine closes a handle to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Closing handle to SPB target");
+
+ WdfIoTargetClose(pDevice->SpbController);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+SpbPeripheralLock(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a lock command to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_LOCK_CONTROLLER",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for lock and send.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_LOCK_CONTROLLER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_LOCK_CONTROLLER - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralUnlock(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends an unlock command to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_UNLOCK_CONTROLLER",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for unlock and send.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_UNLOCK_CONTROLLER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_UNLOCK_CONTROLLER - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralLockConnection(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a lock connection command to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_LOCK_CONNECTION",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for lock and send.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_LOCK_CONNECTION,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_LOCK_CONNECTION - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralUnlockConnection(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends an unlock connection command to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_UNLOCK_CONNECTION",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for unlock and send.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_UNLOCK_CONNECTION,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_UNLOCK_CONNECTION - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralRead(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine reads from the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ WDFMEMORY memory = nullptr;
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for read",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for read and send.
+ //
+
+ status = WdfRequestRetrieveOutputMemory(
+ FxRequest,
+ &memory);
+
+ if (NT_SUCCESS(status))
+ {
+ status = WdfIoTargetFormatRequestForRead(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ memory,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "read - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralWrite(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine writes to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ WDFMEMORY memory = nullptr;
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for write",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for write and send.
+ //
+
+ status = WdfRequestRetrieveInputMemory(
+ FxRequest,
+ &memory);
+
+ if (NT_SUCCESS(status))
+ {
+ status = WdfIoTargetFormatRequestForWrite(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ memory,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "write - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralWriteRead(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a write-read sequence to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ PVOID pInputBuffer = nullptr;
+ PVOID pOutputBuffer = nullptr;
+ size_t inputBufferLength = 0;
+ size_t outputBufferLength = 0;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ PREQUEST_CONTEXT pRequest;
+ NTSTATUS status;
+
+ pRequest = GetRequestContext(pDevice->SpbRequest);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_EXECUTE_SEQUENCE",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Get input and output buffers.
+ //
+
+ status = WdfRequestRetrieveInputBuffer(
+ FxRequest,
+ 0,
+ &pInputBuffer,
+ &inputBufferLength);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to retrieve input buffer - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ status = WdfRequestRetrieveOutputBuffer(
+ FxRequest,
+ 0,
+ &pOutputBuffer,
+ &outputBufferLength);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to retrieve output buffer - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ //
+ // Build SPB sequence.
+ //
+
+ const ULONG transfers = 2;
+
+ SPB_TRANSFER_LIST_AND_ENTRIES(transfers) seq;
+ SPB_TRANSFER_LIST_INIT(&(seq.List), transfers);
+
+ {
+ //
+ // PreFAST cannot figure out the SPB_TRANSFER_LIST_ENTRY
+ // "struct hack" size but using an index variable quiets
+ // the warning. This is a false positive from OACR.
+ //
+
+ ULONG index = 0;
+ seq.List.Transfers[index] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionToDevice,
+ 0,
+ pInputBuffer,
+ (ULONG)inputBufferLength);
+
+ seq.List.Transfers[index + 1] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionFromDevice,
+ 0,
+ pOutputBuffer,
+ (ULONG)outputBufferLength);
+ }
+
+ //
+ // Create preallocated WDFMEMORY. The IOCTL is METHOD_BUFFERED,
+ // so the memory doesn't have to persist until the request is
+ // completed.
+ //
+
+ NT_ASSERT(pDevice->InputMemory == WDF_NO_HANDLE);
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+
+ status = WdfMemoryCreatePreallocated(
+ &attributes,
+ (PVOID)&seq,
+ sizeof(seq),
+ &pDevice->InputMemory);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to create WDFMEMORY - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Built write-read sequence %p with byte length=%lu",
+ &seq,
+ (ULONG)(inputBufferLength + outputBufferLength));
+
+ //
+ // Send sequence IOCTL.
+ //
+
+ //
+ // Mark SPB request as sequence and save length.
+ // These will be used in the completion callback
+ // to complete the client request with the correct
+ // number of bytes
+ //
+
+ pRequest->IsSpbSequenceRequest = TRUE;
+ pRequest->SequenceWriteLength = (ULONG_PTR)inputBufferLength;
+
+ //
+ // Format and send the SPB sequence request.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_EXECUTE_SEQUENCE,
+ pDevice->InputMemory,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to format request - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_EXECUTE_SEQUENCE - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ goto Done;
+ }
+
+Done:
+
+ if (!NT_SUCCESS(status))
+ {
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralFullDuplex(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a full duplex transfer to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ PVOID pInputBuffer = nullptr;
+ PVOID pOutputBuffer = nullptr;
+ size_t inputBufferLength = 0;
+ size_t outputBufferLength = 0;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ PREQUEST_CONTEXT pRequest;
+ NTSTATUS status;
+
+ pRequest = GetRequestContext(pDevice->SpbRequest);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_FULL_DUPLEX",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Get input and output buffers.
+ //
+
+ status = WdfRequestRetrieveInputBuffer(
+ FxRequest,
+ 0,
+ &pInputBuffer,
+ &inputBufferLength);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to retrieve input buffer - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ status = WdfRequestRetrieveOutputBuffer(
+ FxRequest,
+ 0,
+ &pOutputBuffer,
+ &outputBufferLength);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to retrieve output buffer - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ //
+ // Build full duplex transfer using SPB transfer list.
+ //
+
+ const ULONG transfers = 2;
+
+ SPB_TRANSFER_LIST_AND_ENTRIES(transfers) seq;
+ SPB_TRANSFER_LIST_INIT(&(seq.List), transfers);
+
+ {
+ //
+ // PreFAST cannot figure out the SPB_TRANSFER_LIST_ENTRY
+ // "struct hack" size but using an index variable quiets
+ // the warning. This is a false positive from OACR.
+ //
+
+ ULONG index = 0;
+ seq.List.Transfers[index] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionToDevice,
+ 0,
+ pInputBuffer,
+ (ULONG)inputBufferLength);
+
+ seq.List.Transfers[index + 1] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionFromDevice,
+ 0,
+ pOutputBuffer,
+ (ULONG)outputBufferLength);
+ }
+
+ //
+ // Create preallocated WDFMEMORY. The IOCTL is METHOD_BUFFERED,
+ // so the memory doesn't have to persist until the request is
+ // completed.
+ //
+
+ NT_ASSERT(pDevice->InputMemory == WDF_NO_HANDLE);
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+
+ status = WdfMemoryCreatePreallocated(
+ &attributes,
+ (PVOID)&seq,
+ sizeof(seq),
+ &pDevice->InputMemory);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to create WDFMEMORY - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Built full duplex transfer %p with byte length=%lu",
+ &seq,
+ (ULONG)(inputBufferLength + outputBufferLength));
+
+ //
+ // Send full duplex IOCTL.
+ //
+
+ //
+ // Mark SPB request as full duplex (sequence format)
+ // and save length. These will be used in the completion
+ // callback to complete the client request with the correct
+ // number of bytes
+ //
+
+ pRequest->IsSpbSequenceRequest = TRUE;
+ pRequest->SequenceWriteLength = (ULONG_PTR)inputBufferLength;
+
+ //
+ // Format and send the full duplex request.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_FULL_DUPLEX,
+ pDevice->InputMemory,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to format request - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_FULL_DUPLEX - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ goto Done;
+ }
+
+Done:
+
+ if (!NT_SUCCESS(status))
+ {
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralSignalInterrupt(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+Routine Description:
+
+ This routine signals the interrupt service routine
+ to continue.
+
+Arguments:
+
+ pDevice - the device context
+ FxRequest - the framework request object
+
+Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if (pDevice->Interrupt != nullptr)
+ {
+ //
+ // Signal ISR to continue.
+ //
+
+ KeSetEvent(
+ &pDevice->IsrWaitEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Setting ISR wait event");
+ }
+ else
+ {
+ status = STATUS_NOT_FOUND;
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "No interrupt object found, ignoring - %!STATUS!",
+ status);
+ }
+
+ WdfRequestComplete(FxRequest, status);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralWaitOnInterrupt(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+Routine Description:
+
+ This routine pends the WaitOnInterrupt request.
+
+Arguments:
+
+ pDevice - the device context
+ FxRequest - the framework request object
+
+Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest = GetRequestContext(FxRequest);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if (pDevice->WaitOnInterruptRequest == nullptr)
+ {
+ //
+ // Mark request cancellable.
+ //
+
+ pRequest->FxDevice = pDevice->FxDevice;
+
+ status = WdfRequestMarkCancelableEx(
+ FxRequest,
+ SpbPeripheralOnWaitOnInterruptCancel);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to mark WaitOnInterrupt request %p cancellable - %!STATUS!",
+ FxRequest,
+ status);
+ }
+
+ //
+ // Pend the WaitOnInterrupt request.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "WaitOnInterrupt request %p pended",
+ FxRequest);
+
+ pDevice->WaitOnInterruptRequest = FxRequest;
+ }
+ }
+ else
+ {
+ status = STATUS_INVALID_DEVICE_STATE;
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Cannont pend multiple WaitOnInterrupt requests, ignoring - %!STATUS!",
+ status);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ WdfRequestComplete(FxRequest, status);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralOnWaitOnInterruptCancel(
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+Routine Description:
+
+ This event is called when the WaitOnInterrupt request is cancelled.
+
+Arguments:
+
+ FxRequest - the framework request object
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest;
+ PDEVICE_CONTEXT pDevice;
+
+ pRequest = GetRequestContext(FxRequest);
+ pDevice = GetDeviceContext(pRequest->FxDevice);
+
+ //
+ // Complete the request as cancelled
+ //
+
+ if (FxRequest == pDevice->WaitOnInterruptRequest)
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "WaitOnInterrupt request %p cancelled",
+ FxRequest);
+
+ pDevice->WaitOnInterruptRequest = nullptr;
+ WdfRequestComplete(FxRequest, STATUS_CANCELLED);
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Cancel for WDFREQUEST %p without WaitOnInterrupt request pended,"
+ "will complete as cancelled anyway",
+ FxRequest);
+
+ WdfRequestComplete(FxRequest, STATUS_CANCELLED);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+BOOLEAN
+SpbPeripheralInterruptNotify(
+ _In_ PDEVICE_CONTEXT pDevice
+ )
+/*++
+Routine Description:
+
+ This routine completes the pending WaitOnInterrupt request.
+
+Arguments:
+
+ pDevice - the device context
+
+Return Value:
+
+ TRUE if notification sent, false otherwise.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ WDFREQUEST request;
+ BOOLEAN fNotificationSent = FALSE;
+ NTSTATUS status;
+
+ if (pDevice->WaitOnInterruptRequest != nullptr)
+ {
+ //
+ // Complete the WaitOnInterrupt request.
+ //
+
+ request = pDevice->WaitOnInterruptRequest;
+ pDevice->WaitOnInterruptRequest = nullptr;
+
+ status = WdfRequestUnmarkCancelable(request);
+
+ if (NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected, WaitOnInterrupt request %p completed",
+ pDevice->WaitOnInterruptRequest);
+
+ WdfRequestComplete(request, STATUS_SUCCESS);
+ fNotificationSent = TRUE;
+ }
+ else if (status == STATUS_CANCELLED)
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected with WaitOnInterrupt request %p "
+ "already completed - %!STATUS!",
+ pDevice->WaitOnInterruptRequest,
+ status);
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected but failed to unmark WaitOnInterrupt "
+ "request %p as cancellable - %!STATUS!",
+ pDevice->WaitOnInterruptRequest,
+ status);
+ }
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected without a pended WaitOnInterrupt request");
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return fNotificationSent;
+}
+
+NTSTATUS
+SpbPeripheralSendRequest(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST SpbRequest,
+ _In_ WDFREQUEST ClientRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a write-read sequence to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ SpbRequest - the SPB request object
+ ClientRequest - the client request object
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest = GetRequestContext(ClientRequest);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Saving client request %p, and "
+ "sending SPB request %p",
+ ClientRequest,
+ SpbRequest);
+
+ //
+ // Init client request context.
+ //
+
+ pRequest->FxDevice = pDevice->FxDevice;
+ pRequest->IsSpbSequenceRequest = FALSE;
+ pRequest->SequenceWriteLength = 0;
+
+ //
+ // Mark the client request as cancellable.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ status = WdfRequestMarkCancelableEx(
+ ClientRequest,
+ SpbPeripheralOnCancel);
+ }
+
+ //
+ // Send the SPB request.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ WdfRequestSetCompletionRoutine(
+ SpbRequest,
+ SpbPeripheralOnCompletion,
+ GetRequestContext(SpbRequest));
+
+ BOOLEAN fSent = WdfRequestSend(
+ SpbRequest,
+ pDevice->SpbController,
+ WDF_NO_SEND_OPTIONS);
+
+ if (!fSent)
+ {
+ status = WdfRequestGetStatus(SpbRequest);
+
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p - %!STATUS!",
+ SpbRequest,
+ status);
+
+ NTSTATUS cancelStatus;
+ cancelStatus = WdfRequestUnmarkCancelable(ClientRequest);
+
+ if (!NT_SUCCESS(cancelStatus))
+ {
+ NT_ASSERTMSG("WdfRequestUnmarkCancelable should only fail if request has already been cancelled",
+ cancelStatus == STATUS_CANCELLED);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Client request %p has already been cancelled - "
+ "%!STATUS!",
+ ClientRequest,
+ cancelStatus);
+ }
+ }
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return status;
+}
+
+VOID
+SpbPeripheralOnCompletion(
+ _In_ WDFREQUEST FxRequest,
+ _In_ WDFIOTARGET FxTarget,
+ _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
+ _In_ WDFCONTEXT Context
+ )
+/*++
+
+ Routine Description:
+
+ This routine is called when a request completes.
+
+ Arguments:
+
+ FxRequest - the framework request object
+ FxTarget - the framework IO target object
+ Params - a pointer to the request completion parameters
+ Context - the request context
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxTarget);
+ UNREFERENCED_PARAMETER(Context);
+
+ PREQUEST_CONTEXT pRequest;
+ PDEVICE_CONTEXT pDevice;
+ NTSTATUS status;
+ NTSTATUS cancelStatus;
+ ULONG_PTR bytesCompleted;
+
+ pRequest = GetRequestContext(FxRequest);
+ pDevice = GetDeviceContext(pRequest->FxDevice);
+
+ status = Params->IoStatus.Status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Completion callback received for SPB request %p with %!STATUS!",
+ FxRequest,
+ status);
+
+ //
+ // Unmark the client request as cancellable
+ //
+
+ cancelStatus = WdfRequestUnmarkCancelable(pDevice->ClientRequest);
+
+ if (!NT_SUCCESS(cancelStatus))
+ {
+ NT_ASSERTMSG("WdfRequestUnmarkCancelable should only fail if request has already been cancelled",
+ cancelStatus == STATUS_CANCELLED);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Client request %p has already been cancelled - %!STATUS!",
+ pDevice->ClientRequest,
+ cancelStatus);
+ }
+
+ //
+ // Complete the request pair
+ //
+
+ if (pRequest->IsSpbSequenceRequest == TRUE)
+ {
+ //
+ // The client DeviceIoControl should only be
+ // completed with bytesReturned and not total
+ // bytes transferred. Here we infer the number
+ // of bytes read by substracting the write
+ // length from the total.
+ //
+
+ bytesCompleted =
+ Params->IoStatus.Information < pRequest->SequenceWriteLength ?
+ 0 : Params->IoStatus.Information - pRequest->SequenceWriteLength;
+ }
+ else
+ {
+ bytesCompleted = Params->IoStatus.Information;
+ }
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ bytesCompleted);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralOnCancel(
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+Routine Description:
+
+ This event is called when the client request is cancelled.
+
+Arguments:
+
+ FxRequest - the framework request object
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest;
+ PDEVICE_CONTEXT pDevice;
+
+ pRequest = GetRequestContext(FxRequest);
+ pDevice = GetDeviceContext(pRequest->FxDevice);
+
+ //
+ // Attempt to cancel the SPB request
+ //
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Cancel received for client request %p, "
+ "attempting to cancel SPB request %p",
+ FxRequest,
+ pDevice->SpbRequest);
+
+ WdfRequestCancelSentRequest(pDevice->SpbRequest);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralCompleteRequestPair(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ NTSTATUS status,
+ _In_ ULONG_PTR bytesCompleted
+ )
+/*++
+Routine Description:
+
+ This routine marks the SpbRequest as reuse
+ and completes the client request.
+
+Arguments:
+
+ pDevice - the device context
+ status - the client completion status
+ bytesCompleted - the number of bytes completed
+ for the client request
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest;
+ pRequest = GetRequestContext(pDevice->SpbRequest);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Marking SPB request %p for reuse, and completing "
+ "client request %p with %!STATUS! and bytes=%lu",
+ pDevice->SpbRequest,
+ pDevice->ClientRequest,
+ status,
+ (ULONG)bytesCompleted);
+
+ //
+ // Mark the SPB request as reuse
+ //
+
+ pRequest->IsSpbSequenceRequest = FALSE;
+ pRequest->SequenceWriteLength = 0;
+
+ WDF_REQUEST_REUSE_PARAMS params;
+ WDF_REQUEST_REUSE_PARAMS_INIT(
+ &params,
+ WDF_REQUEST_REUSE_NO_FLAGS,
+ STATUS_SUCCESS);
+
+ WdfRequestReuse(pDevice->SpbRequest, &params);
+
+ if (pDevice->InputMemory != WDF_NO_HANDLE)
+ {
+ WdfObjectDelete(pDevice->InputMemory);
+ pDevice->InputMemory = WDF_NO_HANDLE;
+ }
+
+ //
+ // Complete the client request
+ //
+
+ if (pDevice->ClientRequest != nullptr)
+ {
+ WDFREQUEST clientRequest = pDevice->ClientRequest;
+ pDevice->ClientRequest = nullptr;
+
+ //
+ // In order to satisfy SDV, assume clientRequest
+ // is equal to pDevice->ClientRequest. This suppresses
+ // a warning in the driver's cancellation path.
+ //
+ // Typically when WdfRequestUnmarkCancelable returns
+ // STATUS_CANCELLED a driver does not go on to complete
+ // the request in that context. This sample, however,
+ // driver has handled this condition appropriately by
+ // not completing the cancelled request in its
+ // EvtRequestCancel callback. Developers should be
+ // cautious when copying code from this sample, paying
+ // close attention to the cancellation logic.
+ //
+ _Analysis_assume_(clientRequest == pDevice->ClientRequest);
+
+ WdfRequestCompleteWithInformation(
+ clientRequest,
+ status,
+ bytesCompleted);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
diff --git a/SPB/SpbTestTool/sys/peripheral.h b/SPB/SpbTestTool/sys/peripheral.h
new file mode 100644
index 00000000..fc1fc806
--- /dev/null
+++ b/SPB/SpbTestTool/sys/peripheral.h
@@ -0,0 +1,104 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ peripheral.h
+
+Abstract:
+
+ This module contains the function definitions for
+ interaction with the SPB API.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _PERIPHERAL_H_
+#define _PERIPHERAL_H_
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE SpbPeripheralOnCompletion;
+EVT_WDF_REQUEST_CANCEL SpbPeripheralOnCancel;
+
+EVT_WDF_REQUEST_CANCEL SpbPeripheralOnWaitOnInterruptCancel;
+
+NTSTATUS
+SpbPeripheralOpen(
+ _In_ PDEVICE_CONTEXT pDevice);
+
+NTSTATUS
+SpbPeripheralClose(
+ _In_ PDEVICE_CONTEXT pDevice);
+
+VOID
+SpbPeripheralLock(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralUnlock(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralLockConnection(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralUnlockConnection(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralRead(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralWrite(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralWriteRead(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralFullDuplex(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralSignalInterrupt(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralWaitOnInterrupt(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+BOOLEAN
+SpbPeripheralInterruptNotify(
+ _In_ PDEVICE_CONTEXT pDevice);
+
+NTSTATUS
+SpbPeripheralSendRequest(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST SpbRequest,
+ _In_ WDFREQUEST ClientRequest);
+
+VOID
+SpbPeripheralCompleteRequestPair(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ NTSTATUS status,
+ _In_ ULONG_PTR bytesCompleted);
+
+#endif // _PERIPHERAL_H_
diff --git a/SPB/SpbTestTool/sys/resource.rc b/SPB/SpbTestTool/sys/resource.rc
new file mode 100644
index 00000000..24dd5368
--- /dev/null
+++ b/SPB/SpbTestTool/sys/resource.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "SPB Test Tool Peripheral Driver"
+#define VER_INTERNALNAME_STR "SpbTestTool.sys"
+#define VER_ORIGINALFILENAME_STR "SpbTestTool.sys"
+
+#include "common.ver"
diff --git a/SPB/SpbTestTool/sys/spbtestioctl.h b/SPB/SpbTestTool/sys/spbtestioctl.h
new file mode 100644
index 00000000..5a0f7b7e
--- /dev/null
+++ b/SPB/SpbTestTool/sys/spbtestioctl.h
@@ -0,0 +1,50 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ spbtestioctl.h
+
+Abstract:
+
+ This module contains the public device path names and
+ IOCTL definitions for the SpbTestTool.
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _SPBTESTIOCTL_H_
+#define _SPBTESTIOCTL_H_
+
+//
+// Device path names
+//
+
+#define SPBTESTTOOL_NAME L"SPBTESTTOOL"
+
+#define SPBTESTTOOL_SYMBOLIC_NAME L"\\DosDevices\\" SPBTESTTOOL_NAME
+#define SPBTESTTOOL_USERMODE_PATH L"\\\\.\\" SPBTESTTOOL_NAME
+#define SPBTESTTOOL_USERMODE_PATH_SIZE sizeof(SPBTESTTOOL_USERMODE_PATH)
+
+//
+// Priavte SpbTestTool IOCTLs
+//
+
+#define FILE_DEVICE_SPB_PERIPHERAL 0x400
+
+#define IOCTL_SPBTESTTOOL_OPEN CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x700, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_CLOSE CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x701, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_LOCK CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x702, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_UNLOCK CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x703, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_WRITEREAD CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x704, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_LOCK_CONNECTION CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x705, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_UNLOCK_CONNECTION CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x706, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_SIGNAL_INTERRUPT CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x707, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_WAIT_ON_INTERRUPT CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x708, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_FULL_DUPLEX CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x709, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#endif _SPBTESTIOCTL_H_ \ No newline at end of file
diff --git a/SPB/SpbTestTool/sys/spbtesttool.asl b/SPB/SpbTestTool/sys/spbtesttool.asl
new file mode 100644
index 00000000..fcf3c9ac
--- /dev/null
+++ b/SPB/SpbTestTool/sys/spbtesttool.asl
@@ -0,0 +1,26 @@
+//
+// Test peripheral device node
+//
+Device(SPBT)
+{
+ Name(_HID, "SpbTestTool")
+ Name(_UID, 1)
+ Method(_CRS, 0x0, NotSerialized)
+ {
+ Name (RBUF, ResourceTemplate ()
+ {
+ //
+ // Sample I2C and GPIO resources. Modify to match your
+ // platform's underlying controllers and connections.
+ // \_SB.I2C and \_SB.GPIO are paths to predefined I2C
+ // and GPIO controller instances.
+ //
+ // Note: SpbTestTool does not require a GPIO resource.
+ // Remove as necessary.
+ //
+ I2CSerialBus(0x1D, ControllerInitiated, 400000, AddressingMode7Bit, "\\_SB.I2C", , )
+ GpioInt(Level, ActiveHigh, Exclusive, PullDown, 0, "\\_SB.GPIO") {1}
+ })
+ Return(RBUF)
+ }
+} \ No newline at end of file
diff --git a/SPB/SpbTestTool/sys/spbtesttool.inx b/SPB/SpbTestTool/sys/spbtesttool.inx
new file mode 100644
index 00000000..ae1a4fe4
--- /dev/null
+++ b/SPB/SpbTestTool/sys/spbtesttool.inx
Binary files differ
diff --git a/SPB/SpbTestTool/sys/trace.h b/SPB/SpbTestTool/sys/trace.h
new file mode 100644
index 00000000..00601dc8
--- /dev/null
+++ b/SPB/SpbTestTool/sys/trace.h
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ trace.h
+
+Abstract:
+
+ This module contains the trace definitions for the SPB
+ peripheral driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+extern "C"
+{
+//
+// Tracing Definitions:
+//
+// Control GUID:
+// {73e3b785-f5fb-423e-94a9-56627fea9053}
+//
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID( \
+ SpbTestToolTraceGuid, \
+ (73e3b785,f5fb,423e,94a9,56627fea9053), \
+ WPP_DEFINE_BIT(TRACE_FLAG_WDFLOADING) \
+ WPP_DEFINE_BIT(TRACE_FLAG_SPBAPI) \
+ WPP_DEFINE_BIT(TRACE_FLAG_OTHER) \
+ )
+}
+
+#define WPP_LEVEL_FLAGS_LOGGER(level,flags) WPP_LEVEL_LOGGER(flags)
+#define WPP_LEVEL_FLAGS_ENABLED(level, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level)
+
+// begin_wpp config
+// FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS);
+// FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS);
+// USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry");
+// USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--");
+// end_wpp
+
+#endif _TRACE_H_