├── .gitattributes ├── .gitignore ├── LICENSE ├── README-IASL.md ├── README.md ├── SimplePeripheralBusProbe.sln ├── device.cpp ├── device.h ├── driver.cpp ├── driver.h ├── i2ctrace.h ├── internal.h ├── peripheral.cpp ├── peripheral.h ├── resource.rc ├── spbProbe.asl ├── spbProbe.inx ├── spbProbe.vcxproj └── spbProbe.vcxproj.Filters /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Microsoft Public License (MS-PL) 2 | Copyright (c) 2015 Microsoft 3 | 4 | This license governs use of the accompanying software. If you use the software, you 5 | accept this license. If you do not accept the license, do not use the software. 6 | 7 | 1. Definitions 8 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the 9 | same meaning here as under U.S. copyright law. 10 | A "contribution" is the original software, or any additions or changes to the software. 11 | A "contributor" is any person that distributes its contribution under this license. 12 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 13 | 14 | 2. Grant of Rights 15 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 16 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 17 | 18 | 3. Conditions and Limitations 19 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 20 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 21 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 22 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 23 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 24 | -------------------------------------------------------------------------------- /README-IASL.md: -------------------------------------------------------------------------------- 1 | Requirements 2 | ------------ 3 | 4 | - a Windows machine with a serial (I2C or SPI) device ACPI-enumerated 5 | - time 6 | - skills 7 | - ```traceview.exe``` (provided by the WDK in tools\tracing\) 8 | - ```asl.exe``` (provided by the WDK in Tools\x64\ACPIVerify) 9 | - ```iasl.exe``` (https://www.acpica.org/downloads/binary-tools) 10 | - ```RWEverything``` (http://rweverything.phpnet.us) 11 | - a way to boot a repair environment if things goes wrong (recovery disk, Windows disk, repair partition) 12 | 13 | Instructions 14 | ------------ 15 | 16 | The guide for doing everything with Microsoft `asl.exe` is quite detailed. Use the original [README](./README.md) where details are required. 17 | 18 | This is as an extension because often the microsoft compiler does not know how to compile the `asl` it extracted. 19 | Intel created a compiler that is much clearer in it's error messages and knows howto compile the `asl`-files. 20 | Unfortunately you have to provide the binary files in order to decompile them. 21 | 22 | Getting the Binary DSDT 23 | ======================= 24 | 25 | Install `RWEverything`. 26 | Once you have installed `RWEverything`, run it and from the `Access` menu, choose `ACPI Tables`. From the `ACPI Table` window that opens, first select the `DSDT` tab to make sure the `DSDT` table is active, then choose the `Save as Binary` button on toolbar (or `Shift+F2`). 27 | 28 | You should then have a file called `DSDT.aml` 29 | 30 | Decompiling 31 | =========== 32 | 33 | To decompile the file with `iasl.exe` you need to call it with the full path. 34 | 35 | In theory this could work: 36 | 37 | ``` 38 | > iasl.exe -d DSDT.aml 39 | ``` 40 | 41 | However in many cases `iasl` complains about missing dependencies. To get those you can extract the `SSDT`-tables using `RWEverything`. - Make sure to store them with a different extension (example `.bin`) than the `DSDT` to later be able to use easy wildcards. 42 | 43 | Also this might still not be enough and you need additional info. You can add this info using a `refs.txt` file with the following content: 44 | 45 | ### refs.txt 46 | ``` 47 | External(MDBG, MethodObj, 1) 48 | External(_GPE.MMTB, MethodObj, 0) 49 | External(_SB.PCI0.LPCB.H_EC.ECWT, MethodObj, 2) 50 | External(_SB.PCI0.LPCB.H_EC.ECRD, MethodObj, 1) 51 | External(_SB.PCI0.LPCB.H_EC.ECMD, MethodObj, 1) 52 | External(_SB.PCI0.PEG0.PEGP.SGPO, MethodObj, 2) 53 | External(_SB.PCI0.GFX0.DD02._BCM, MethodObj, 1) 54 | External(_SB.PCI0.SAT0.SDSM, MethodObj, 4) 55 | External(_GPE.VHOV, MethodObj, 3) 56 | External(_SB.PCI0.XHC.RHUB.TPLD, MethodObj, 2) 57 | ``` 58 | 59 | If the decompiler complains about duplicate definitions just remove them from this file. 60 | 61 | With those files set up you should now be able to run a command similar to this: 62 | 63 | ``` 64 | > iasl.exe -e *.bin -fe refs.txt -d DSDT.aml 65 | ``` 66 | 67 | This should sucessfully decompile the code to a file called `DSDT.dsl`. Rename this file to a different name to avoid confusions with the original version for example `DSDT-mod.dsl`. Adjust this file just as explained in the original tutorial. 68 | 69 | Compiling the `DSDT-mod.asl` 70 | ======================== 71 | 72 | Compiling is pretty straight forward: 73 | 74 | ``` 75 | > iasl.exe DSDT-mod.dsl 76 | ``` 77 | 78 | This produces a file `DSDT-mod.aml` - If you did not rename the original `DSDT` this might overwrite your `DSDT.aml` extracted earlier. 79 | 80 | Loading the compiled `DSDT-mod.aml` 81 | =================================== 82 | 83 | To load the file you just use the microsoft `asl.exe` 84 | 85 | ``` 86 | > asl.exe /loadtable DSDT-mod.aml 87 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | I2C/SPI probe for Windows 10 3 | ============================ 4 | 5 | This driver is **extremely** experimental and should only be used by people knowing what they are doing and not afraid of destroying their Windows install. 6 | 7 | The purpose of this project is to insert a pass-through driver between the target I2C or SPI device and its driver. 8 | This pass-through device logs each serial transaction made by the driver. It's useful to watch the raw sequences to ensure the driver works properly. 9 | 10 | This code is a mix of 2 drivers found in the Windows drivers sample repository, so the license is the Microsoft one (see LICENSE). 11 | 12 | Often `asl.exe` fails at compiling its extracted file. The `iasl.exe` provided by Intel is much better at this job. For a guide on how to use this compiler instead use: [README-IASL.md](./README-IASL.md) (this text is just an extension and not a replacement for the explanations here) 13 | 14 | Requirements 15 | ------------ 16 | 17 | - a Windows machine with a serial (I2C or SPI) device ACPI-enumerated 18 | - time 19 | - skills 20 | - ```traceview.exe``` (provided by the WDK in tools\tracing\) 21 | - ```asl.exe``` (provided by the WDK in Tools\x64\ACPIVerify) 22 | - a way to boot a repair environment if things goes wrong (recovery disk, Windows disk, repair partition) 23 | 24 | Overloading the DSDT 25 | -------------------- 26 | 27 | Windows 8+ allows to overload the DSDT through the ```/loadtable``` option of asl.exe. 28 | See https://msdn.microsoft.com/en-us/windows/hardware/drivers/bringup/microsoft-asl-compiler 29 | 30 | The first step is to retrieve the current DSDT (or SSDT if the device is declared in the SSDT). Run in a cmd.exe process as an Administrator: 31 | 32 | You will find WDK Tools under ```C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify``` if you didn't changed the default installation path. 33 | 34 | ``` 35 | asl.exe /tab=DSDT 36 | ``` 37 | 38 | This will produce a DSDT.ASL file in the current directory. 39 | Next step is to increment the version of the table (the last number in ```DefinitionBlock()```). 40 | 41 | Then recompile the DSDT and fix any errors 42 | 43 | ``` 44 | asl.exe DSDT.ASL 45 | ``` 46 | 47 | When the errors are fixed, you can load the table for the next boot: 48 | 49 | ``` 50 | asl.exe /loadtable -v DSDT.AML 51 | ``` 52 | 53 | Make sure the version is the one you just incremented. 54 | The table will be stored in the registry under ```HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\ACPI\Parameters\DSDT\XXX\YYY\ZZZ``` where ```XXX```,```YYY```,```ZZZ``` will depend from your edited DSDT. 55 | 56 | Before rebooting, make sure Windows will allow non signed drivers (*Be cautious when enabling this, any driver can now be installed, even bad ones*. You have been warned). 57 | 58 | ``` 59 | bcdedit /set testsigning on 60 | ``` 61 | 62 | Reboot. If the device reboots and a new dump of the DSDT shows your incremented version number, you are good to continue. If not, follow the next section to fix the broken boot. 63 | 64 | What if the DSDT is wrong and I get the blue screen ACPI_BIOS_ERROR at boot? 65 | ---------------------------------------------------------------------------- 66 | 67 | As mentioned previously, the overloaded DSDT is just stored in the registry, so the trick is to edit the registry and remove the keys. 68 | 69 | So if Windows doesn't start, you need first to boot into the recovery environment. 70 | For that, the simplest way is to boot the repair partition or use a recovery disk (search Microsoft's site for how to build one). 71 | For OEM installs, you just need to boot again on Windows and it will start the recovery environment. 72 | 73 | Once you are in the recovery environment, there is a trick (thanks this [blog](http://www.techrepublic.com/blog/windows-and-office/use-the-recovery-drive-command-prompt-to-edit-the-registry-or-recover-data/) and others). 74 | 75 | The key is that when you are in the recovery, if you start ```regedit```, the registry that will be edited is the one from the recovery environment, not the one from the installation. 76 | 77 | So once ```regedit``` is started, you need to add the keys from the current installation by using ```Load Hive...``` from the ```File``` menu. If the menu entry ```Load Hive...``` is disabled, please select a node (e.g. `HKEY_LOCAL_MACHINE`) and then the menu entry should be available. 78 | You will need to load the file ```%windir%\system32\config\SYSTEM```. You will be prompted to set a key name for this loaded hive. Name it as you wish (e.g. ```Origin```). 79 | In this newly loaded key, go down to ```HKEY_LOCAL_MACHINE\Origin\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\ACPI\Parameters\DSDT\XXX\YYY\ZZZ``` (as mentioned previously) and simply remove the ```ZZZ``` folder. 80 | 81 | Reboot and enjoy a working Windows. 82 | 83 | Insert the Probe in the DSDT 84 | ---------------------------- 85 | 86 | Now that you have a working environment for overloading the DSDT, we can start inserting the probe in order to be able to dump traces. 87 | 88 | Let's say you have a HID over I2C device not working properly and you want to check the I2C traces. 89 | 90 | The DSDT will look like (example taken from the [HID over I2C specification](https://msdn.microsoft.com/en-us/library/windows/hardware/dn642101(v=vs.85).aspx)): 91 | 92 | ``` 93 | Scope (\_SB) { 94 | 95 | //-------------------- 96 | // General Purpose I/O, ports 0...127 97 | //-------------------- 98 | 99 | 100 | Device(HIDI2C_DEVICE1) { 101 | Name(_ADR,0) 102 | Name (_HID, "MSFT1234”) 103 | Name (_CID, "PNP0C50") 104 | Name (_UID, 3) 105 | 106 | Method(_CRS, 0x0, NotSerialized) 107 | { 108 | Name (RBUF, ResourceTemplate () 109 | { 110 | // Address 0x07 on I2C-X (OEM selects this address) 111 | //IHV SPECIFIC I2C3 = I2C Controller; TGD0 = GPIO Controller; 112 | I2CSerialBus (0x07, ControllerInitiated, 100000,AddressingMode7Bit, "\\_SB.I2C3",,,,) 113 | GpioInt(Level, ActiveLow, Exclusive, PullUp, 0, "\\_SB. TGD0", 0 , ResourceConsumer, , ) {40} 114 | }) 115 | Return(RBUF) 116 | } 117 | 118 | 119 | 120 | Method(_DSM, 0x4, NotSerialized) 121 | { 122 | // BreakPoint 123 | Store ("Method _DSM begin", Debug) 124 | 125 | // DSM UUID 126 | switch(ToBuffer(Arg0)) 127 | { 128 | // ACPI DSM UUID for HIDI2C 129 | case(ToUUID("3CDFF6F7-4267-4555-AD05-B30A3D8938DE")) 130 | { 131 | // DSM Function 132 | switch(ToInteger(Arg2)) 133 | { 134 | // Function 0: Query function, return based on revision 135 | case(0) 136 | { 137 | // DSM Revision 138 | switch(ToInteger(Arg1)) 139 | { 140 | // Revision 1: Function 1 supported 141 | case(1) 142 | { 143 | Store ("Method _DSM Function Query", Debug) 144 | Return(Buffer(One) { 0x03 }) 145 | } 146 | 147 | default 148 | { 149 | // Revision 2+: no functions supported 150 | Return(Buffer(One) { 0x00 }) 151 | } 152 | } 153 | } 154 | 155 | // Function 1 : HID Function 156 | case(1) 157 | { 158 | Store ("Method _DSM Function HID", Debug) 159 | 160 | // HID Descriptor Address 161 | Return(0x0001) 162 | } 163 | 164 | default 165 | { 166 | // Functions 2+: not supported 167 | } 168 | } 169 | } 170 | 171 | default 172 | { 173 | // No other GUIDs supported 174 | Return(Buffer(One) { 0x00 }) 175 | } 176 | } 177 | } 178 | } 179 | ``` 180 | 181 | We are interested here in the ```_CRS``` method which describes the ressources used by the device. 182 | 183 | In the same scope (```\_SB```) we are going to add one new I2C device that uses the same I2C target (found in ```spbProbe.asl```): 184 | 185 | ``` 186 | Device(SPB1) 187 | { 188 | Name(_HID, "PROBE01") 189 | Name(_UID, 1) 190 | Method(_CRS, 0x0, NotSerialized) 191 | { 192 | Name (RBUF, ResourceTemplate () 193 | { 194 | I2CSerialBus (0x07, ControllerInitiated, 100000,AddressingMode7Bit, "\\_SB.I2C3",,,,) 195 | }) 196 | Return(RBUF) 197 | } 198 | 199 | } 200 | ``` 201 | 202 | And we edit the current device to point to this one instead: 203 | 204 | ``` 205 | Device(HIDI2C_DEVICE1) { 206 | Name(_ADR,0) 207 | Name (_HID, "MSFT1234”) 208 | Name (_CID, "PNP0C50") 209 | Name (_UID, 3) 210 | 211 | Method(_CRS, 0x0, NotSerialized) 212 | { 213 | Name (RBUF, ResourceTemplate () 214 | { 215 | // Address 0x07 on I2C-X (OEM selects this address) 216 | //IHV SPECIFIC I2C3 = I2C Controller; TGD0 = GPIO Controller; 217 | //I2CSerialBus (0x07, ControllerInitiated, 100000,AddressingMode7Bit, "\\_SB.I2C3",,,,) 218 | I2CSerialBus (0x07, ControllerInitiated, 100000,AddressingMode7Bit, "\\_SB.SPB1",,,,) 219 | GpioInt(Level, ActiveLow, Exclusive, PullUp, 0, "\\_SB. TGD0", 0 , ResourceConsumer, , ) {40} 220 | }) 221 | Return(RBUF) 222 | } 223 | 224 | ``` 225 | 226 | Do not forget to increment the DSDT version. Then recompile the DSDT and load it (```asl /loadtable DSDT.AML```). 227 | 228 | Reboot. 229 | 230 | In the ```Device Manager```, there should be a new device with an exclamation mark (```Unknown Device```) with a path ```ACPI\SPBPROBE\1``` in the ```Details``` panel of the device. 231 | 232 | Load the driver from this repository (after compiling it or by downloading it from the [releases page](https://github.com/bentiss/SimplePeripheralBusProbe/releases)). You will get a big red screen telling you Windows can't trust the publisher of the driver, which is true. But if you are here, well you know what you are doing so just hit ```Install it anyway```. 233 | 234 | Now the device should show up in the ```system devices``` list and will have the name ```I1C/SPI Probe Controller Driver```. 235 | If you did not messed up, the target device (the HID over I2C device we are targeting) should still be working, and the probe must also say that it is working properly. 236 | 237 | Get traces from the probe 238 | ------------------------- 239 | 240 | Run ```traceview.exe``` as an Administrator. Then ```File``` -> ```Create New Log Session...```. 241 | 242 | Chose the .pdb from the driver in the ```Add provider``` window. Then ```Next```. 243 | By default, you will only have the real time display selected. If you want to store and process the logs, you want to also log the data to ta file. 244 | 245 | For better traces, before hitting ```Finish```, disable the targeted device in Device Manager so that the logs start as if the device have just been plugged in. 246 | 247 | > **Note:** By default, the traces are dumped as ```Error``` level. So you don't need to set any flags. 248 | > If you run into problems and need to debug the probe, you can lower the debug level here to trace what is happening in the driver. 249 | 250 | Convert the traces into a text file 251 | ----------------------------------- 252 | 253 | When you record the logs, the traces are not in a text file, which doesn't help much to look at them. 254 | 255 | ```traceview``` allows to convert them into text. 256 | 257 | First, you need to export the dictionary from the pdb file: 258 | 259 | ``` 260 | traceview.exe -parsepdb spbProbe.pdb 261 | ``` 262 | 263 | Then you can dump the actual log file: 264 | 265 | ``` 266 | traceview.exe -process LogSession_mmddyy_hhmmss.etl -o myFirstLogs.txt 267 | ``` 268 | 269 | (amend ```LogSession_mmddyy_hhmmss.etl``` and ```myFirstLogs.txt``` to match your needs). 270 | 271 | That's it. Now ```myFirstLogs.txt``` contains the logs and you can do an analysis of where the problem is. 272 | -------------------------------------------------------------------------------- /SimplePeripheralBusProbe.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 14 3 | VisualStudioVersion = 14.0.25123.0 4 | MinimumVisualStudioVersion = 12.0 5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spbProbe", "spbProbe.vcxproj", "{8C1BB5BA-283E-460F-A682-4548A1DAFA59}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Win32 = Debug|Win32 10 | Debug|x64 = Debug|x64 11 | Release|Win32 = Release|Win32 12 | Release|x64 = Release|x64 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Debug|Win32.ActiveCfg = Debug|Win32 16 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Debug|Win32.Build.0 = Debug|Win32 17 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Debug|x64.ActiveCfg = Debug|x64 18 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Debug|x64.Build.0 = Debug|x64 19 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Release|Win32.ActiveCfg = Release|Win32 20 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Release|Win32.Build.0 = Release|Win32 21 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Release|x64.ActiveCfg = Release|x64 22 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Release|x64.Build.0 = Release|x64 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | EndGlobal 28 | -------------------------------------------------------------------------------- /device.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | device.cpp 8 | 9 | Abstract: 10 | 11 | This module contains WDF device initialization 12 | and SPB callback functions for the controller driver. 13 | 14 | Environment: 15 | 16 | kernel-mode only 17 | 18 | Revision History: 19 | 20 | --*/ 21 | 22 | #include "internal.h" 23 | #include "device.h" 24 | #include "peripheral.h" 25 | 26 | #include "device.tmh" 27 | 28 | #pragma warning(disable:4100) 29 | 30 | 31 | ///////////////////////////////////////////////// 32 | // 33 | // WDF and SPB DDI callbacks. 34 | // 35 | ///////////////////////////////////////////////// 36 | 37 | NTSTATUS 38 | OnPrepareHardware( 39 | _In_ WDFDEVICE FxDevice, 40 | _In_ WDFCMRESLIST FxResourcesRaw, 41 | _In_ WDFCMRESLIST FxResourcesTranslated 42 | ) 43 | /*++ 44 | 45 | Routine Description: 46 | 47 | This routine caches the SPB resource connection ID. 48 | 49 | Arguments: 50 | 51 | FxDevice - a handle to the framework device object 52 | FxResourcesRaw - list of translated hardware resources that 53 | the PnP manager has assigned to the device 54 | FxResourcesTranslated - list of raw hardware resources that 55 | the PnP manager has assigned to the device 56 | 57 | Return Value: 58 | 59 | Status 60 | 61 | --*/ 62 | { 63 | FuncEntry(TRACE_FLAG_WDFLOADING); 64 | 65 | PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); 66 | BOOLEAN fSpbResourceFound = FALSE; 67 | NTSTATUS status = STATUS_SUCCESS; 68 | 69 | UNREFERENCED_PARAMETER(FxResourcesRaw); 70 | 71 | // 72 | // Parse the peripheral's resources. 73 | // 74 | 75 | ULONG resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated); 76 | 77 | for (ULONG i = 0; i < resourceCount; i++) 78 | { 79 | PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; 80 | UCHAR Class; 81 | UCHAR Type; 82 | 83 | pDescriptor = WdfCmResourceListGetDescriptor( 84 | FxResourcesTranslated, i); 85 | 86 | switch (pDescriptor->Type) 87 | { 88 | case CmResourceTypeConnection: 89 | 90 | // 91 | // Look for I2C or SPI resource and save connection ID. 92 | // 93 | 94 | Class = pDescriptor->u.Connection.Class; 95 | Type = pDescriptor->u.Connection.Type; 96 | 97 | if ((Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL) && 98 | ((Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) || 99 | (Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_SPI))) 100 | { 101 | if (fSpbResourceFound == FALSE) 102 | { 103 | pDevice->PeripheralId.LowPart = 104 | pDescriptor->u.Connection.IdLowPart; 105 | pDevice->PeripheralId.HighPart = 106 | pDescriptor->u.Connection.IdHighPart; 107 | 108 | fSpbResourceFound = TRUE; 109 | 110 | Trace( 111 | TRACE_LEVEL_INFORMATION, 112 | TRACE_FLAG_WDFLOADING, 113 | "SPB resource found with ID=0x%llx", 114 | pDevice->PeripheralId.QuadPart); 115 | } 116 | else 117 | { 118 | Trace( 119 | TRACE_LEVEL_WARNING, 120 | TRACE_FLAG_WDFLOADING, 121 | "Duplicate SPB resource found with ID=0x%llx", 122 | pDevice->PeripheralId.QuadPart); 123 | } 124 | } 125 | 126 | break; 127 | 128 | default: 129 | 130 | // 131 | // Ignoring all other resource types. 132 | // 133 | 134 | break; 135 | } 136 | } 137 | 138 | // 139 | // An SPB resource is required. 140 | // 141 | 142 | if (fSpbResourceFound == FALSE) 143 | { 144 | status = STATUS_NOT_FOUND; 145 | Trace( 146 | TRACE_LEVEL_ERROR, 147 | TRACE_FLAG_WDFLOADING, 148 | "SPB resource not found - %!STATUS!", 149 | status); 150 | } 151 | 152 | FuncExit(TRACE_FLAG_WDFLOADING); 153 | 154 | return status; 155 | } 156 | 157 | NTSTATUS 158 | OnReleaseHardware( 159 | _In_ WDFDEVICE FxDevice, 160 | _In_ WDFCMRESLIST FxResourcesTranslated 161 | ) 162 | /*++ 163 | 164 | Routine Description: 165 | 166 | Arguments: 167 | 168 | FxDevice - a handle to the framework device object 169 | FxResourcesTranslated - list of raw hardware resources that 170 | the PnP manager has assigned to the device 171 | 172 | Return Value: 173 | 174 | Status 175 | 176 | --*/ 177 | { 178 | FuncEntry(TRACE_FLAG_WDFLOADING); 179 | 180 | NTSTATUS status = STATUS_SUCCESS; 181 | 182 | UNREFERENCED_PARAMETER(FxResourcesTranslated); 183 | 184 | FuncExit(TRACE_FLAG_WDFLOADING); 185 | 186 | return status; 187 | } 188 | 189 | NTSTATUS 190 | OnD0Entry( 191 | _In_ WDFDEVICE FxDevice, 192 | _In_ WDF_POWER_DEVICE_STATE FxPreviousState 193 | ) 194 | /*++ 195 | 196 | Routine Description: 197 | 198 | This routine allocates objects needed by the driver. 199 | 200 | Arguments: 201 | 202 | FxDevice - a handle to the framework device object 203 | FxPreviousState - previous power state 204 | 205 | Return Value: 206 | 207 | Status 208 | 209 | --*/ 210 | { 211 | FuncEntry(TRACE_FLAG_WDFLOADING); 212 | 213 | UNREFERENCED_PARAMETER(FxPreviousState); 214 | 215 | PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); 216 | NTSTATUS status; 217 | 218 | // 219 | // Create the SPB target. 220 | // 221 | 222 | WDF_OBJECT_ATTRIBUTES targetAttributes; 223 | WDF_OBJECT_ATTRIBUTES_INIT(&targetAttributes); 224 | 225 | status = WdfIoTargetCreate( 226 | pDevice->FxDevice, 227 | &targetAttributes, 228 | &pDevice->TrueSpbController); 229 | 230 | if (!NT_SUCCESS(status)) 231 | { 232 | Trace( 233 | TRACE_LEVEL_ERROR, 234 | TRACE_FLAG_WDFLOADING, 235 | "Failed to create IO target - %!STATUS!", 236 | status); 237 | } 238 | 239 | // 240 | // InputMemory will be created when an SPB request is about to be 241 | // sent. Indicate that it is not yet initialized. 242 | // 243 | 244 | pDevice->InputMemory = WDF_NO_HANDLE; 245 | 246 | // 247 | // Create the SPB request. 248 | // 249 | 250 | if (NT_SUCCESS(status)) 251 | { 252 | WDF_OBJECT_ATTRIBUTES requestAttributes; 253 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttributes, PBC_REQUEST); 254 | 255 | status = WdfRequestCreate( 256 | &requestAttributes, 257 | nullptr, 258 | &pDevice->SpbRequest); 259 | 260 | if (!NT_SUCCESS(status)) 261 | { 262 | Trace( 263 | TRACE_LEVEL_ERROR, 264 | TRACE_FLAG_WDFLOADING, 265 | "Failed to create IO request - %!STATUS!", 266 | status); 267 | } 268 | 269 | if (NT_SUCCESS(status)) 270 | { 271 | PPBC_REQUEST pRequest = GetRequestContext( 272 | pDevice->SpbRequest); 273 | 274 | pRequest->FxDevice = pDevice->FxDevice; 275 | } 276 | } 277 | 278 | FuncExit(TRACE_FLAG_WDFLOADING); 279 | 280 | return status; 281 | } 282 | 283 | NTSTATUS 284 | OnD0Exit( 285 | _In_ WDFDEVICE FxDevice, 286 | _In_ WDF_POWER_DEVICE_STATE FxPreviousState 287 | ) 288 | /*++ 289 | 290 | Routine Description: 291 | 292 | This routine destroys objects needed by the driver. 293 | 294 | Arguments: 295 | 296 | FxDevice - a handle to the framework device object 297 | FxPreviousState - previous power state 298 | 299 | Return Value: 300 | 301 | Status 302 | 303 | --*/ 304 | { 305 | FuncEntry(TRACE_FLAG_WDFLOADING); 306 | 307 | UNREFERENCED_PARAMETER(FxPreviousState); 308 | 309 | PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); 310 | 311 | if (pDevice->TrueSpbController != WDF_NO_HANDLE) 312 | { 313 | WdfObjectDelete(pDevice->TrueSpbController); 314 | pDevice->TrueSpbController = WDF_NO_HANDLE; 315 | } 316 | 317 | if (pDevice->SpbRequest != WDF_NO_HANDLE) 318 | { 319 | WdfObjectDelete(pDevice->SpbRequest); 320 | pDevice->SpbRequest = WDF_NO_HANDLE; 321 | } 322 | 323 | if (pDevice->InputMemory != WDF_NO_HANDLE) 324 | { 325 | WdfObjectDelete(pDevice->InputMemory); 326 | pDevice->InputMemory = WDF_NO_HANDLE; 327 | } 328 | 329 | FuncExit(TRACE_FLAG_WDFLOADING); 330 | 331 | return STATUS_SUCCESS; 332 | } 333 | 334 | NTSTATUS 335 | OnTargetConnect( 336 | _In_ WDFDEVICE SpbController, 337 | _In_ SPBTARGET SpbTarget 338 | ) 339 | /*++ 340 | 341 | Routine Description: 342 | 343 | This routine is invoked whenever a peripheral driver opens 344 | a target. It retrieves target-specific settings from the 345 | Resource Hub and saves them in the target's context. 346 | 347 | Arguments: 348 | 349 | SpbController - a handle to the framework device object 350 | representing an SPB controller 351 | SpbTarget - a handle to the SPBTARGET object 352 | 353 | Return Value: 354 | 355 | Status 356 | 357 | --*/ 358 | { 359 | FuncEntry(TRACE_FLAG_SPBDDI); 360 | 361 | PPBC_DEVICE pDevice = GetDeviceContext(SpbController); 362 | PPBC_TARGET pTarget = GetTargetContext(SpbTarget); 363 | 364 | NT_ASSERT(pDevice != NULL); 365 | NT_ASSERT(pTarget != NULL); 366 | 367 | NTSTATUS status = STATUS_SUCCESS; 368 | 369 | // 370 | // Get target connection parameters. 371 | // 372 | 373 | SPB_CONNECTION_PARAMETERS params; 374 | SPB_CONNECTION_PARAMETERS_INIT(¶ms); 375 | 376 | SpbTargetGetConnectionParameters(SpbTarget, ¶ms); 377 | 378 | // 379 | // Retrieve target settings. 380 | // 381 | 382 | status = PbcTargetGetSettings(pDevice, 383 | params.ConnectionParameters, 384 | &pTarget->Settings 385 | ); 386 | 387 | // 388 | // Initialize target context. 389 | // 390 | 391 | if (NT_SUCCESS(status)) 392 | { 393 | pTarget->SpbTarget = SpbTarget; 394 | pTarget->pCurrentRequest = NULL; 395 | 396 | Trace( 397 | TRACE_LEVEL_INFORMATION, 398 | TRACE_FLAG_SPBDDI, 399 | "Connected to SPBTARGET %p at address 0x%lx from WDFDEVICE %p", 400 | pTarget->SpbTarget, 401 | pTarget->Settings.Address, 402 | pDevice->FxDevice); 403 | } 404 | 405 | status = SpbPeripheralOpen(pDevice); 406 | if (!NT_SUCCESS(status)) 407 | { 408 | Trace( 409 | TRACE_LEVEL_ERROR, 410 | TRACE_FLAG_SPBDDI, 411 | "Can't open the underlying device - %!STATUS!", 412 | status); 413 | } 414 | 415 | FuncExit(TRACE_FLAG_SPBDDI); 416 | 417 | return status; 418 | } 419 | 420 | VOID 421 | OnTargetDisconnect( 422 | _In_ WDFDEVICE SpbController, 423 | _In_ SPBTARGET SpbTarget 424 | ) 425 | /*++ 426 | 427 | Routine Description: 428 | 429 | This routine is invoked whenever a peripheral driver closes 430 | a target. 431 | 432 | Arguments: 433 | 434 | SpbController - a handle to the framework device object 435 | representing an SPB controller 436 | SpbTarget - a handle to the SPBTARGET object 437 | 438 | Return Value: 439 | 440 | None 441 | 442 | --*/ 443 | { 444 | FuncEntry(TRACE_FLAG_SPBDDI); 445 | 446 | PPBC_DEVICE pDevice = GetDeviceContext(SpbController); 447 | PPBC_TARGET pTarget = GetTargetContext(SpbTarget); 448 | 449 | NT_ASSERT(pDevice != NULL); 450 | NT_ASSERT(pTarget != NULL); 451 | 452 | SpbPeripheralClose(pDevice); 453 | 454 | FuncExit(TRACE_FLAG_SPBDDI); 455 | } 456 | 457 | VOID 458 | OnControllerLock( 459 | _In_ WDFDEVICE SpbController, 460 | _In_ SPBTARGET SpbTarget, 461 | _In_ SPBREQUEST SpbRequest 462 | ) 463 | /*++ 464 | 465 | Routine Description: 466 | 467 | This routine is invoked whenever the controller is to 468 | be locked for a single target. The request is only completed 469 | if there is an error configuring the transfer. 470 | 471 | Arguments: 472 | 473 | SpbController - a handle to the framework device object 474 | representing an SPB controller 475 | SpbTarget - a handle to the SPBTARGET object 476 | SpbRequest - a handle to the SPBREQUEST object 477 | 478 | Return Value: 479 | 480 | None. The request is completed synchronously. 481 | 482 | --*/ 483 | { 484 | FuncEntry(TRACE_FLAG_SPBDDI); 485 | 486 | PPBC_DEVICE pDevice = GetDeviceContext(SpbController); 487 | PPBC_TARGET pTarget = GetTargetContext(SpbTarget); 488 | 489 | NT_ASSERT(pDevice != NULL); 490 | NT_ASSERT(pTarget != NULL); 491 | 492 | SpbPeripheralLock(pDevice, SpbRequest); 493 | 494 | Trace( 495 | TRACE_LEVEL_INFORMATION, 496 | TRACE_FLAG_SPBDDI, 497 | "Controller locked for SPBTARGET %p at address 0x%lx (WDFDEVICE %p)", 498 | pTarget->SpbTarget, 499 | pTarget->Settings.Address, 500 | pDevice->FxDevice); 501 | 502 | FuncExit(TRACE_FLAG_SPBDDI); 503 | } 504 | 505 | VOID 506 | OnControllerUnlock( 507 | _In_ WDFDEVICE SpbController, 508 | _In_ SPBTARGET SpbTarget, 509 | _In_ SPBREQUEST SpbRequest 510 | ) 511 | /*++ 512 | 513 | Routine Description: 514 | 515 | This routine is invoked whenever the controller is to 516 | be unlocked for a single target. The request is only completed 517 | if there is an error configuring the transfer. 518 | 519 | Arguments: 520 | 521 | SpbController - a handle to the framework device object 522 | representing an SPB controller 523 | SpbTarget - a handle to the SPBTARGET object 524 | SpbRequest - a handle to the SPBREQUEST object 525 | 526 | Return Value: 527 | 528 | None. The request is completed asynchronously. 529 | 530 | --*/ 531 | { 532 | FuncEntry(TRACE_FLAG_SPBDDI); 533 | 534 | PPBC_DEVICE pDevice = GetDeviceContext(SpbController); 535 | PPBC_TARGET pTarget = GetTargetContext(SpbTarget); 536 | 537 | NT_ASSERT(pDevice != NULL); 538 | NT_ASSERT(pTarget != NULL); 539 | 540 | 541 | SpbPeripheralUnlock(pDevice, SpbRequest); 542 | 543 | Trace( 544 | TRACE_LEVEL_INFORMATION, 545 | TRACE_FLAG_SPBDDI, 546 | "Controller unlocked for SPBTARGET %p at address 0x%lx (WDFDEVICE %p)", 547 | pTarget->SpbTarget, 548 | pTarget->Settings.Address, 549 | pDevice->FxDevice); 550 | 551 | FuncExit(TRACE_FLAG_SPBDDI); 552 | } 553 | 554 | VOID 555 | OnRead( 556 | _In_ WDFDEVICE SpbController, 557 | _In_ SPBTARGET SpbTarget, 558 | _In_ SPBREQUEST SpbRequest, 559 | _In_ size_t Length 560 | ) 561 | /*++ 562 | 563 | Routine Description: 564 | 565 | This routine sets up a read from the target device using 566 | the supplied buffers. The request is only completed 567 | if there is an error configuring the transfer. 568 | 569 | Arguments: 570 | 571 | SpbController - a handle to the framework device object 572 | representing an SPB controller 573 | SpbTarget - a handle to the SPBTARGET object 574 | SpbRequest - a handle to the SPBREQUEST object 575 | Length - the number of bytes to read from the target 576 | 577 | Return Value: 578 | 579 | None. The request is completed asynchronously. 580 | 581 | --*/ 582 | { 583 | PPBC_DEVICE pDevice = GetDeviceContext(SpbController); 584 | 585 | FuncEntry(TRACE_FLAG_SPBDDI); 586 | 587 | Trace( 588 | TRACE_LEVEL_INFORMATION, 589 | TRACE_FLAG_SPBDDI, 590 | "Received read request %p of length %Iu for SPBTARGET %p " 591 | "(WDFDEVICE %p)", 592 | SpbRequest, 593 | Length, 594 | SpbTarget, 595 | SpbController); 596 | 597 | SpbPeripheralRead(pDevice, SpbRequest, WdfFalse); 598 | 599 | FuncExit(TRACE_FLAG_SPBDDI); 600 | } 601 | 602 | VOID 603 | OnWrite( 604 | _In_ WDFDEVICE SpbController, 605 | _In_ SPBTARGET SpbTarget, 606 | _In_ SPBREQUEST SpbRequest, 607 | _In_ size_t Length 608 | ) 609 | /*++ 610 | 611 | Routine Description: 612 | 613 | This routine sets up a write to the target device using 614 | the supplied buffers. The request is only completed 615 | if there is an error configuring the transfer. 616 | 617 | Arguments: 618 | 619 | SpbController - a handle to the framework device object 620 | representing an SPB controller 621 | SpbTarget - a handle to the SPBTARGET object 622 | SpbRequest - a handle to the SPBREQUEST object 623 | Length - the number of bytes to write to the target 624 | 625 | Return Value: 626 | 627 | None. The request is completed asynchronously. 628 | 629 | --*/ 630 | { 631 | PPBC_DEVICE pDevice = GetDeviceContext(SpbController); 632 | 633 | FuncEntry(TRACE_FLAG_SPBDDI); 634 | 635 | Trace( 636 | TRACE_LEVEL_INFORMATION, 637 | TRACE_FLAG_SPBDDI, 638 | "Received write request %p of length %Iu for SPBTARGET %p " 639 | "(WDFDEVICE %p)", 640 | SpbRequest, 641 | Length, 642 | SpbTarget, 643 | SpbController); 644 | 645 | SpbPeripheralWrite(pDevice, SpbRequest, WdfFalse); 646 | 647 | FuncExit(TRACE_FLAG_SPBDDI); 648 | } 649 | 650 | VOID 651 | OnSequence( 652 | _In_ WDFDEVICE SpbController, 653 | _In_ SPBTARGET SpbTarget, 654 | _In_ SPBREQUEST SpbRequest, 655 | _In_ ULONG TransferCount 656 | ) 657 | /*++ 658 | 659 | Routine Description: 660 | 661 | This routine sets up a sequence of reads and writes. It 662 | validates parameters as necessary. The request is only 663 | completed if there is an error configuring the transfer. 664 | 665 | Arguments: 666 | 667 | SpbController - a handle to the framework device object 668 | representing an SPB controller 669 | SpbTarget - a handle to the SPBTARGET object 670 | SpbRequest - a handle to the SPBREQUEST object 671 | TransferCount - number of individual transfers in the sequence 672 | 673 | Return Value: 674 | 675 | None. The request is completed asynchronously. 676 | 677 | --*/ 678 | { 679 | FuncEntry(TRACE_FLAG_SPBDDI); 680 | 681 | PPBC_DEVICE pDevice = GetDeviceContext(SpbController); 682 | PPBC_TARGET pTarget = GetTargetContext(SpbTarget); 683 | PPBC_REQUEST pRequest = GetRequestContext(SpbRequest); 684 | 685 | NT_ASSERT(pDevice != NULL); 686 | NT_ASSERT(pTarget != NULL); 687 | NT_ASSERT(pRequest != NULL); 688 | 689 | // 690 | // Get request parameters. 691 | // 692 | 693 | SPB_REQUEST_PARAMETERS params; 694 | SPB_REQUEST_PARAMETERS_INIT(¶ms); 695 | SpbRequestGetParameters(SpbRequest, ¶ms); 696 | 697 | NT_ASSERT(params.Position == SpbRequestSequencePositionSingle); 698 | NT_ASSERT(params.Type == SpbRequestTypeSequence); 699 | 700 | Trace( 701 | TRACE_LEVEL_INFORMATION, 702 | TRACE_FLAG_SPBDDI, 703 | "Received sequence request %p with transfer count %d for SPBTARGET %p " 704 | "(WDFDEVICE %p)", 705 | SpbRequest, 706 | TransferCount, 707 | SpbTarget, 708 | SpbController); 709 | 710 | SpbPeripheralSequence(pDevice, SpbRequest, TransferCount); 711 | 712 | FuncExit(TRACE_FLAG_SPBDDI); 713 | } 714 | 715 | VOID 716 | OnOtherInCallerContext( 717 | _In_ WDFDEVICE SpbController, 718 | _In_ WDFREQUEST FxRequest 719 | ) 720 | /*++ 721 | 722 | Routine Description: 723 | 724 | This routine preprocesses custom IO requests before the framework 725 | places them in an IO queue. For requests using the SPB transfer list 726 | format, it calls SpbRequestCaptureIoOtherTransferList to capture the 727 | client's buffers. 728 | 729 | Arguments: 730 | 731 | SpbController - a handle to the framework device object 732 | representing an SPB controller 733 | SpbRequest - a handle to the SPBREQUEST object 734 | 735 | Return Value: 736 | 737 | None. The request is either completed or enqueued asynchronously. 738 | 739 | --*/ 740 | { 741 | FuncEntry(TRACE_FLAG_SPBDDI); 742 | 743 | NTSTATUS status; 744 | 745 | // 746 | // Check for custom IOCTLs that this driver handles. If 747 | // unrecognized mark as STATUS_NOT_SUPPORTED and complete. 748 | // 749 | 750 | WDF_REQUEST_PARAMETERS fxParams; 751 | WDF_REQUEST_PARAMETERS_INIT(&fxParams); 752 | 753 | WdfRequestGetParameters(FxRequest, &fxParams); 754 | 755 | if ((fxParams.Type != WdfRequestTypeDeviceControl) && 756 | (fxParams.Type != WdfRequestTypeDeviceControlInternal)) 757 | { 758 | status = STATUS_NOT_SUPPORTED; 759 | Trace( 760 | TRACE_LEVEL_ERROR, 761 | TRACE_FLAG_SPBDDI, 762 | "FxRequest %p is of unsupported request type - %!STATUS!", 763 | FxRequest, 764 | status 765 | ); 766 | goto exit; 767 | } 768 | 769 | // 770 | // TODO: verify the driver supports this DeviceIoContol code, 771 | // otherwise mark as STATUS_NOT_SUPPORTED and complete. 772 | // 773 | 774 | // 775 | // For custom IOCTLs that use the SPB transfer list format 776 | // (i.e. sequence formatting), call SpbRequestCaptureIoOtherTransferList 777 | // so that the driver can leverage other SPB DDIs for this request. 778 | // 779 | 780 | status = SpbRequestCaptureIoOtherTransferList((SPBREQUEST)FxRequest); 781 | 782 | if (!NT_SUCCESS(status)) 783 | { 784 | Trace( 785 | TRACE_LEVEL_ERROR, 786 | TRACE_FLAG_SPBDDI, 787 | "Failed to capture transfer list for custom SpbRequest %p" 788 | " - %!STATUS!", 789 | FxRequest, 790 | status 791 | ); 792 | goto exit; 793 | } 794 | 795 | // 796 | // Preprocessing has succeeded, enqueue the request. 797 | // 798 | 799 | status = WdfDeviceEnqueueRequest(SpbController, FxRequest); 800 | 801 | if (!NT_SUCCESS(status)) 802 | { 803 | goto exit; 804 | } 805 | 806 | exit: 807 | 808 | if (!NT_SUCCESS(status)) 809 | { 810 | WdfRequestComplete(FxRequest, status); 811 | } 812 | 813 | FuncExit(TRACE_FLAG_SPBDDI); 814 | } 815 | 816 | NTSTATUS 817 | OnFullDuplex( 818 | _In_ WDFDEVICE SpbController, 819 | _In_ SPBTARGET SpbTarget, 820 | _In_ SPBREQUEST SpbRequest 821 | ) 822 | /*++ 823 | 824 | Routine Description: 825 | 826 | This routine processes Full Duplex requests. 827 | 828 | Arguments: 829 | 830 | SpbController - a handle to the framework device object 831 | representing an SPB controller 832 | SpbTarget - a handle to the SPBTARGET object 833 | SpbRequest - a handle to the SPBREQUEST object 834 | OutputBufferLength - the request's output buffer length 835 | InputBufferLength - the requests input buffer length 836 | 837 | Return Value: 838 | 839 | None. The request is completed asynchronously. 840 | 841 | --*/ 842 | { 843 | FuncEntry(TRACE_FLAG_SPBDDI); 844 | 845 | NTSTATUS status = STATUS_SUCCESS; 846 | PPBC_DEVICE pDevice = GetDeviceContext(SpbController); 847 | 848 | UNREFERENCED_PARAMETER(SpbController); 849 | UNREFERENCED_PARAMETER(SpbTarget); 850 | UNREFERENCED_PARAMETER(SpbRequest); 851 | 852 | // 853 | // Validate the transfer count. 854 | // 855 | 856 | SPB_REQUEST_PARAMETERS params; 857 | SPB_REQUEST_PARAMETERS_INIT(¶ms); 858 | SpbRequestGetParameters(SpbRequest, ¶ms); 859 | 860 | if (params.SequenceTransferCount != 2) 861 | { 862 | // 863 | // The full-suplex request must have 864 | // exactly two transfer descriptor 865 | // 866 | 867 | status = STATUS_INVALID_PARAMETER; 868 | goto exit; 869 | } 870 | 871 | // 872 | // Retrieve the write and read transfer descriptors. 873 | // 874 | 875 | const ULONG fullDuplexWriteIndex = 0; 876 | const ULONG fullDuplexReadIndex = 1; 877 | 878 | SPB_TRANSFER_DESCRIPTOR writeDescriptor; 879 | SPB_TRANSFER_DESCRIPTOR readDescriptor; 880 | PMDL pWriteMdl; 881 | PMDL pReadMDL; 882 | 883 | SPB_TRANSFER_DESCRIPTOR_INIT(&writeDescriptor); 884 | SPB_TRANSFER_DESCRIPTOR_INIT(&readDescriptor); 885 | 886 | SpbRequestGetTransferParameters( 887 | SpbRequest, 888 | fullDuplexWriteIndex, 889 | &writeDescriptor, 890 | &pWriteMdl); 891 | 892 | SpbRequestGetTransferParameters( 893 | SpbRequest, 894 | fullDuplexReadIndex, 895 | &readDescriptor, 896 | &pReadMDL); 897 | 898 | // 899 | // Validate the transfer direction of each descriptor. 900 | // 901 | 902 | if ((writeDescriptor.Direction != SpbTransferDirectionToDevice) || 903 | (readDescriptor.Direction != SpbTransferDirectionFromDevice)) 904 | { 905 | // 906 | // For Full-duplex I/O, the direction of the first transfer 907 | // must be SpbTransferDirectionToDevice, and the direction 908 | // of the second must be SpbTransferDirectionFromDevice. 909 | // 910 | 911 | status = STATUS_INVALID_PARAMETER; 912 | goto exit; 913 | } 914 | 915 | // 916 | // Validate the delay for each transfer descriptor. 917 | // 918 | 919 | if ((writeDescriptor.DelayInUs != 0) || (readDescriptor.DelayInUs != 0)) 920 | { 921 | // 922 | // The write and read buffers for full-duplex I?O are transferred 923 | // simultaneously over the bus, The delay parameter in each transfer 924 | // descriptor must be set to 0. 925 | // 926 | 927 | status = STATUS_INVALID_PARAMETER; 928 | goto exit; 929 | } 930 | 931 | SpbPeripheralFullDuplex(pDevice, SpbRequest); 932 | //if (InputBufferLength && OutputBufferLength) 933 | //{ 934 | // SpbPeripheralFullDuplex(pDevice, SpbRequest, OutputBufferLength, InputBufferLength); 935 | //} 936 | //else if (OutputBufferLength) 937 | //{ 938 | // SpbPeripheralWrite(pDevice, SpbRequest, WdfTrue); 939 | // //SpbPeripheralRead(pDevice, SpbRequest, WdfFalse); 940 | //} 941 | //else 942 | //{ 943 | // SpbPeripheralRead(pDevice, SpbRequest, WdfTrue); 944 | // //SpbPeripheralWrite(pDevice, SpbRequest, WdfFalse); 945 | //} 946 | 947 | exit: 948 | 949 | FuncExit(TRACE_FLAG_SPBDDI); 950 | 951 | return status; 952 | } 953 | 954 | VOID 955 | OnOther( 956 | _In_ WDFDEVICE SpbController, 957 | _In_ SPBTARGET SpbTarget, 958 | _In_ SPBREQUEST SpbRequest, 959 | _In_ size_t OutputBufferLength, 960 | _In_ size_t InputBufferLength, 961 | _In_ ULONG IoControlCode 962 | ) 963 | /*++ 964 | 965 | Routine Description: 966 | 967 | This routine processes custom IO requests that are not natively 968 | supported by the SPB framework extension. For requests using the 969 | SPB transfer list format, SpbRequestCaptureIoOtherTransferList 970 | must have been called in the driver's OnOtherInCallerContext routine. 971 | 972 | Arguments: 973 | 974 | SpbController - a handle to the framework device object 975 | representing an SPB controller 976 | SpbTarget - a handle to the SPBTARGET object 977 | SpbRequest - a handle to the SPBREQUEST object 978 | OutputBufferLength - the request's output buffer length 979 | InputBufferLength - the requests input buffer length 980 | IoControlCode - the device IO control code 981 | 982 | Return Value: 983 | 984 | None. The request is completed asynchronously. 985 | 986 | --*/ 987 | { 988 | FuncEntry(TRACE_FLAG_SPBDDI); 989 | 990 | NTSTATUS status = STATUS_NOT_SUPPORTED; 991 | 992 | UNREFERENCED_PARAMETER(SpbController); 993 | UNREFERENCED_PARAMETER(SpbTarget); 994 | UNREFERENCED_PARAMETER(SpbRequest); 995 | UNREFERENCED_PARAMETER(OutputBufferLength); 996 | UNREFERENCED_PARAMETER(InputBufferLength); 997 | UNREFERENCED_PARAMETER(IoControlCode); 998 | 999 | 1000 | if (IoControlCode == IOCTL_SPB_FULL_DUPLEX) 1001 | { 1002 | Trace( 1003 | TRACE_LEVEL_ERROR, 1004 | TRACE_FLAG_SPBDDI, 1005 | "Received Full Duplex SpbRequest %p in: %lu out: %lu", 1006 | SpbRequest, 1007 | (unsigned long)InputBufferLength, 1008 | (unsigned long)OutputBufferLength 1009 | ); 1010 | 1011 | status = OnFullDuplex(SpbController, SpbTarget, SpbRequest); 1012 | } 1013 | else 1014 | { 1015 | Trace( 1016 | TRACE_LEVEL_ERROR, 1017 | TRACE_FLAG_SPBDDI, 1018 | "Received Other SpbRequest %p ControlCode: %d", 1019 | SpbRequest, 1020 | IoControlCode 1021 | ); 1022 | } 1023 | 1024 | if (!NT_SUCCESS(status)) 1025 | { 1026 | SpbRequestComplete(SpbRequest, status); 1027 | } 1028 | 1029 | FuncExit(TRACE_FLAG_SPBDDI); 1030 | } 1031 | ///////////////////////////////////////////////// 1032 | // 1033 | // PBC functions. 1034 | // 1035 | ///////////////////////////////////////////////// 1036 | 1037 | NTSTATUS 1038 | PbcTargetGetSettings( 1039 | _In_ PPBC_DEVICE pDevice, 1040 | _In_ PVOID ConnectionParameters, 1041 | _Out_ PPBC_TARGET_SETTINGS pSettings 1042 | ) 1043 | /*++ 1044 | 1045 | Routine Description: 1046 | 1047 | This routine populates the target's settings. 1048 | 1049 | Arguments: 1050 | 1051 | pDevice - a pointer to the PBC device context 1052 | ConnectionParameters - a pointer to a blob containing the 1053 | connection parameters 1054 | Settings - a pointer the the target's settings 1055 | 1056 | Return Value: 1057 | 1058 | Status 1059 | 1060 | --*/ 1061 | { 1062 | FuncEntry(TRACE_FLAG_PBCLOADING); 1063 | 1064 | UNREFERENCED_PARAMETER(pDevice); 1065 | NTSTATUS status = STATUS_INVALID_PARAMETER; 1066 | 1067 | NT_ASSERT(ConnectionParameters != nullptr); 1068 | NT_ASSERT(pSettings != nullptr); 1069 | 1070 | PRH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER connection; 1071 | PPNP_SERIAL_BUS_DESCRIPTOR descriptor; 1072 | PPNP_I2C_SERIAL_BUS_DESCRIPTOR i2cDescriptor; 1073 | PPNP_SPI_SERIAL_BUS_DESCRIPTOR spiDescriptor; 1074 | 1075 | connection = (PRH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER) 1076 | ConnectionParameters; 1077 | 1078 | if (connection->PropertiesLength < sizeof(PNP_SERIAL_BUS_DESCRIPTOR)) 1079 | { 1080 | Trace( 1081 | TRACE_LEVEL_ERROR, 1082 | TRACE_FLAG_PBCLOADING, 1083 | "Invalid connection properties (length = %lu, " 1084 | "expected = %Iu)", 1085 | connection->PropertiesLength, 1086 | sizeof(PNP_SERIAL_BUS_DESCRIPTOR)); 1087 | 1088 | return STATUS_INVALID_PARAMETER; 1089 | } 1090 | 1091 | descriptor = (PPNP_SERIAL_BUS_DESCRIPTOR) 1092 | connection->ConnectionProperties; 1093 | 1094 | if (descriptor->SerialBusType == I2C_SERIAL_BUS_TYPE) 1095 | { 1096 | i2cDescriptor = (PPNP_I2C_SERIAL_BUS_DESCRIPTOR) 1097 | connection->ConnectionProperties; 1098 | 1099 | Trace( 1100 | TRACE_LEVEL_INFORMATION, 1101 | TRACE_FLAG_PBCLOADING, 1102 | "I2C Connection Descriptor %p " 1103 | "ConnectionSpeed:%lu " 1104 | "Address:0x%hx", 1105 | i2cDescriptor, 1106 | i2cDescriptor->ConnectionSpeed, 1107 | i2cDescriptor->SlaveAddress); 1108 | 1109 | // Target address 1110 | pSettings->Address = (ULONG)i2cDescriptor->SlaveAddress; 1111 | 1112 | // Address mode 1113 | USHORT i2cFlags = i2cDescriptor->SerialBusDescriptor.TypeSpecificFlags; 1114 | pSettings->AddressMode = 1115 | ((i2cFlags & I2C_SERIAL_BUS_SPECIFIC_FLAG_10BIT_ADDRESS) == 0) ? 1116 | AddressMode7Bit : AddressMode10Bit; 1117 | 1118 | // Clock speed 1119 | pSettings->ConnectionSpeed = i2cDescriptor->ConnectionSpeed; 1120 | status = STATUS_SUCCESS; 1121 | } 1122 | 1123 | if (descriptor->SerialBusType == SPI_SERIAL_BUS_TYPE) 1124 | { 1125 | spiDescriptor = (PPNP_SPI_SERIAL_BUS_DESCRIPTOR) 1126 | connection->ConnectionProperties; 1127 | 1128 | Trace( 1129 | TRACE_LEVEL_INFORMATION, 1130 | TRACE_FLAG_PBCLOADING, 1131 | "SPI Connection Descriptor %p " 1132 | "ConnectionSpeed:%lu ", 1133 | spiDescriptor, 1134 | spiDescriptor->ConnectionSpeed 1135 | ); 1136 | 1137 | // Clock speed 1138 | pSettings->ConnectionSpeed = spiDescriptor->ConnectionSpeed; 1139 | status = STATUS_SUCCESS; 1140 | } 1141 | 1142 | if (status == STATUS_INVALID_PARAMETER) 1143 | { 1144 | Trace( 1145 | TRACE_LEVEL_ERROR, 1146 | TRACE_FLAG_PBCLOADING, 1147 | "Bus type %c not supported, only I2C or SPI", 1148 | descriptor->SerialBusType); 1149 | } 1150 | 1151 | FuncExit(TRACE_FLAG_PBCLOADING); 1152 | 1153 | return STATUS_SUCCESS; 1154 | } 1155 | 1156 | -------------------------------------------------------------------------------- /device.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | device.h 8 | 9 | Abstract: 10 | 11 | This module contains the function definitions for the 12 | WDF device. 13 | 14 | Environment: 15 | 16 | kernel-mode only 17 | 18 | Revision History: 19 | 20 | --*/ 21 | 22 | #ifndef _DEVICE_H_ 23 | #define _DEVICE_H_ 24 | 25 | // 26 | // WDF event callbacks. 27 | // 28 | 29 | EVT_WDF_DEVICE_PREPARE_HARDWARE OnPrepareHardware; 30 | EVT_WDF_DEVICE_RELEASE_HARDWARE OnReleaseHardware; 31 | EVT_WDF_DEVICE_D0_ENTRY OnD0Entry; 32 | EVT_WDF_DEVICE_D0_EXIT OnD0Exit; 33 | 34 | EVT_WDF_INTERRUPT_ISR OnInterruptIsr; 35 | EVT_WDF_INTERRUPT_DPC OnInterruptDpc; 36 | 37 | EVT_WDF_REQUEST_CANCEL OnCancel; 38 | 39 | // 40 | // SPBCx event callbacks. 41 | // 42 | 43 | EVT_SPB_TARGET_CONNECT OnTargetConnect; 44 | EVT_SPB_TARGET_DISCONNECT OnTargetDisconnect; 45 | EVT_SPB_CONTROLLER_LOCK OnControllerLock; 46 | EVT_SPB_CONTROLLER_UNLOCK OnControllerUnlock; 47 | EVT_SPB_CONTROLLER_READ OnRead; 48 | EVT_SPB_CONTROLLER_WRITE OnWrite; 49 | EVT_SPB_CONTROLLER_SEQUENCE OnSequence; 50 | 51 | EVT_WDF_IO_IN_CALLER_CONTEXT OnOtherInCallerContext; 52 | EVT_SPB_CONTROLLER_OTHER OnOther; 53 | 54 | // 55 | // PBC function prototypes. 56 | // 57 | 58 | NTSTATUS 59 | PbcTargetGetSettings( 60 | _In_ PPBC_DEVICE pDevice, 61 | _In_ PVOID ConnectionParameters, 62 | _Out_ PPBC_TARGET_SETTINGS pSettings); 63 | 64 | #if 0 65 | NTSTATUS 66 | FORCEINLINE 67 | PbcRequestSetByte( 68 | _In_ PPBC_REQUEST pRequest, 69 | _In_ size_t Index, 70 | _In_ UCHAR Byte 71 | ) 72 | /*++ 73 | 74 | Routine Description: 75 | 76 | This is a helper routine used to set the 77 | specified byte of the current transfer descriptor buffer. 78 | 79 | Arguments: 80 | 81 | pRequest - a pointer to the PBC request context 82 | 83 | Index - index of desired byte in current transfer descriptor buffer 84 | 85 | Byte - the byte 86 | 87 | Return Value: 88 | 89 | STATUS_INFO_LENGTH_MISMATCH if invalid index, 90 | otherwise STATUS_SUCCESS 91 | 92 | --*/ 93 | { 94 | PMDL mdl = pRequest->pMdlChain; 95 | size_t mdlByteCount; 96 | size_t currentOffset = Index; 97 | PUCHAR pBuffer; 98 | NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH; 99 | 100 | // 101 | // Check for out-of-bounds index 102 | // 103 | 104 | if (Index < pRequest->Length) 105 | { 106 | while (mdl != NULL) 107 | { 108 | mdlByteCount = MmGetMdlByteCount(mdl); 109 | 110 | if (currentOffset < mdlByteCount) 111 | { 112 | pBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe( 113 | mdl, 114 | NormalPagePriority | MdlMappingNoExecute); 115 | 116 | if (pBuffer != NULL) 117 | { 118 | // 119 | // Byte found, mark successful 120 | // 121 | 122 | pBuffer[currentOffset] = Byte; 123 | status = STATUS_SUCCESS; 124 | } 125 | 126 | break; 127 | } 128 | 129 | currentOffset -= mdlByteCount; 130 | mdl = mdl->Next; 131 | } 132 | 133 | // 134 | // If after walking the MDL the byte hasn't been found, 135 | // status will still be STATUS_INFO_LENGTH_MISMATCH 136 | // 137 | } 138 | 139 | return status; 140 | } 141 | #endif 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /driver.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | driver.cpp 8 | 9 | Abstract: 10 | 11 | This module contains the WDF driver initialization 12 | functions for the controller driver. 13 | 14 | Environment: 15 | 16 | kernel-mode only 17 | 18 | Revision History: 19 | 20 | --*/ 21 | 22 | #include "internal.h" 23 | #include "driver.h" 24 | #include "device.h" 25 | #include "ntstrsafe.h" 26 | 27 | #include "driver.tmh" 28 | 29 | NTSTATUS 30 | #pragma prefast(suppress:__WARNING_DRIVER_FUNCTION_TYPE, "thanks, i know this already") 31 | DriverEntry( 32 | _In_ PDRIVER_OBJECT DriverObject, 33 | _In_ PUNICODE_STRING RegistryPath 34 | ) 35 | { 36 | WDF_DRIVER_CONFIG driverConfig; 37 | WDF_OBJECT_ATTRIBUTES driverAttributes; 38 | 39 | WDFDRIVER fxDriver; 40 | 41 | NTSTATUS status; 42 | 43 | WPP_INIT_TRACING(DriverObject, RegistryPath); 44 | 45 | FuncEntry(TRACE_FLAG_WDFLOADING); 46 | 47 | WDF_DRIVER_CONFIG_INIT(&driverConfig, OnDeviceAdd); 48 | driverConfig.DriverPoolTag = SI2C_POOL_TAG; 49 | 50 | WDF_OBJECT_ATTRIBUTES_INIT(&driverAttributes); 51 | driverAttributes.EvtCleanupCallback = OnDriverCleanup; 52 | 53 | status = WdfDriverCreate( 54 | DriverObject, 55 | RegistryPath, 56 | &driverAttributes, 57 | &driverConfig, 58 | &fxDriver); 59 | 60 | if (!NT_SUCCESS(status)) 61 | { 62 | Trace( 63 | TRACE_LEVEL_ERROR, 64 | TRACE_FLAG_WDFLOADING, 65 | "Error creating WDF driver object - %!STATUS!", 66 | status); 67 | 68 | goto exit; 69 | } 70 | 71 | Trace( 72 | TRACE_LEVEL_VERBOSE, 73 | TRACE_FLAG_WDFLOADING, 74 | "Created WDFDRIVER %p", 75 | fxDriver); 76 | 77 | exit: 78 | 79 | FuncExit(TRACE_FLAG_WDFLOADING); 80 | 81 | return status; 82 | } 83 | 84 | VOID 85 | OnDriverCleanup( 86 | _In_ WDFOBJECT Object 87 | ) 88 | { 89 | FuncEntry(TRACE_FLAG_WDFLOADING); 90 | UNREFERENCED_PARAMETER(Object); 91 | 92 | FuncExit(TRACE_FLAG_WDFLOADING); 93 | WPP_CLEANUP(NULL); 94 | } 95 | 96 | NTSTATUS 97 | OnDeviceAdd( 98 | _In_ WDFDRIVER FxDriver, 99 | _Inout_ PWDFDEVICE_INIT FxDeviceInit 100 | ) 101 | /*++ 102 | 103 | Routine Description: 104 | 105 | This routine creates the device object for an SPB 106 | controller and the device's child objects. 107 | 108 | Arguments: 109 | 110 | FxDriver - the WDF driver object handle 111 | FxDeviceInit - information about the PDO that we are loading on 112 | 113 | Return Value: 114 | 115 | Status 116 | 117 | --*/ 118 | { 119 | FuncEntry(TRACE_FLAG_WDFLOADING); 120 | 121 | PPBC_DEVICE pDevice; 122 | NTSTATUS status; 123 | 124 | UNREFERENCED_PARAMETER(FxDriver); 125 | 126 | // 127 | // Setup PNP/Power callbacks. 128 | // 129 | 130 | { 131 | WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks; 132 | WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks); 133 | 134 | pnpCallbacks.EvtDevicePrepareHardware = OnPrepareHardware; 135 | pnpCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware; 136 | pnpCallbacks.EvtDeviceD0Entry = OnD0Entry; 137 | pnpCallbacks.EvtDeviceD0Exit = OnD0Exit; 138 | 139 | WdfDeviceInitSetPnpPowerEventCallbacks(FxDeviceInit, &pnpCallbacks); 140 | } 141 | 142 | // 143 | // Configure DeviceInit structure 144 | // 145 | 146 | status = SpbDeviceInitConfig(FxDeviceInit); 147 | 148 | if (!NT_SUCCESS(status)) 149 | { 150 | Trace( 151 | TRACE_LEVEL_ERROR, 152 | TRACE_FLAG_WDFLOADING, 153 | "Failed SpbDeviceInitConfig() for WDFDEVICE_INIT %p - %!STATUS!", 154 | FxDeviceInit, 155 | status); 156 | 157 | goto exit; 158 | } 159 | 160 | // 161 | // Note: The SPB class extension sets a default 162 | // security descriptor to allow access to 163 | // user-mode drivers. This can be overridden 164 | // by calling WdfDeviceInitAssignSDDLString() 165 | // with the desired setting. This must be done 166 | // after calling SpbDeviceInitConfig() but 167 | // before WdfDeviceCreate(). 168 | // 169 | 170 | 171 | // 172 | // Create the device. 173 | // 174 | 175 | { 176 | WDF_OBJECT_ATTRIBUTES deviceAttributes; 177 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, PBC_DEVICE); 178 | WDFDEVICE fxDevice; 179 | 180 | status = WdfDeviceCreate( 181 | &FxDeviceInit, 182 | &deviceAttributes, 183 | &fxDevice); 184 | 185 | if (!NT_SUCCESS(status)) 186 | { 187 | Trace( 188 | TRACE_LEVEL_ERROR, 189 | TRACE_FLAG_WDFLOADING, 190 | "Failed to create WDFDEVICE from WDFDEVICE_INIT %p - %!STATUS!", 191 | FxDeviceInit, 192 | status); 193 | 194 | goto exit; 195 | } 196 | 197 | pDevice = GetDeviceContext(fxDevice); 198 | NT_ASSERT(pDevice != NULL); 199 | 200 | pDevice->FxDevice = fxDevice; 201 | } 202 | 203 | // 204 | // Ensure device is disable-able 205 | // 206 | 207 | { 208 | WDF_DEVICE_STATE deviceState; 209 | WDF_DEVICE_STATE_INIT(&deviceState); 210 | 211 | deviceState.NotDisableable = WdfFalse; 212 | WdfDeviceSetDeviceState(pDevice->FxDevice, &deviceState); 213 | } 214 | 215 | // 216 | // Bind a SPB controller object to the device. 217 | // 218 | 219 | { 220 | SPB_CONTROLLER_CONFIG spbConfig; 221 | SPB_CONTROLLER_CONFIG_INIT(&spbConfig); 222 | 223 | // 224 | // Register for target (dis)connect callbacks. 225 | // 226 | 227 | spbConfig.EvtSpbTargetConnect = OnTargetConnect; 228 | spbConfig.EvtSpbTargetDisconnect = OnTargetDisconnect; 229 | 230 | // 231 | // Register for IO callbacks. 232 | // 233 | 234 | spbConfig.ControllerDispatchType = WdfIoQueueDispatchSequential; 235 | spbConfig.PowerManaged = WdfTrue; 236 | spbConfig.EvtSpbIoRead = OnRead; 237 | spbConfig.EvtSpbIoWrite = OnWrite; 238 | spbConfig.EvtSpbIoSequence = OnSequence; 239 | spbConfig.EvtSpbControllerLock = OnControllerLock; 240 | spbConfig.EvtSpbControllerUnlock = OnControllerUnlock; 241 | 242 | status = SpbDeviceInitialize(pDevice->FxDevice, &spbConfig); 243 | 244 | if (!NT_SUCCESS(status)) 245 | { 246 | Trace( 247 | TRACE_LEVEL_ERROR, 248 | TRACE_FLAG_WDFLOADING, 249 | "Failed SpbDeviceInitialize() for WDFDEVICE %p - %!STATUS!", 250 | pDevice->FxDevice, 251 | status); 252 | 253 | goto exit; 254 | } 255 | 256 | // 257 | // Register for IO other callbacks. 258 | // 259 | 260 | SpbControllerSetIoOtherCallback( 261 | pDevice->FxDevice, 262 | OnOther, 263 | OnOtherInCallerContext); 264 | } 265 | 266 | // 267 | // Set target object attributes. 268 | // 269 | 270 | { 271 | WDF_OBJECT_ATTRIBUTES targetAttributes; 272 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&targetAttributes, PBC_TARGET); 273 | 274 | SpbControllerSetTargetAttributes(pDevice->FxDevice, &targetAttributes); 275 | } 276 | 277 | // 278 | // Set request object attributes. 279 | // 280 | 281 | { 282 | WDF_OBJECT_ATTRIBUTES requestAttributes; 283 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttributes, PBC_REQUEST); 284 | 285 | // 286 | // NOTE: Be mindful when registering for EvtCleanupCallback or 287 | // EvtDestroyCallback. IO requests arriving in the class 288 | // extension, but not presented to the driver (due to 289 | // cancellation), will still have their cleanup and destroy 290 | // callbacks invoked. 291 | // 292 | 293 | SpbControllerSetRequestAttributes(pDevice->FxDevice, &requestAttributes); 294 | } 295 | 296 | // 297 | // Configure idle settings to use system 298 | // managed idle timeout. 299 | // 300 | { 301 | WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; 302 | WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT( 303 | &idleSettings, 304 | IdleCannotWakeFromS0); 305 | 306 | // 307 | // Explicitly set initial idle timeout delay. 308 | // 309 | 310 | idleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint; 311 | idleSettings.IdleTimeout = IDLE_TIMEOUT_MONITOR_ON; 312 | 313 | status = WdfDeviceAssignS0IdleSettings( 314 | pDevice->FxDevice, 315 | &idleSettings); 316 | 317 | if (!NT_SUCCESS(status)) 318 | { 319 | Trace( 320 | TRACE_LEVEL_ERROR, 321 | TRACE_FLAG_WDFLOADING, 322 | "Failed to initalize S0 idle settings for WDFDEVICE %p- %!STATUS!", 323 | pDevice->FxDevice, 324 | status); 325 | 326 | goto exit; 327 | } 328 | } 329 | 330 | exit: 331 | 332 | FuncExit(TRACE_FLAG_WDFLOADING); 333 | 334 | return status; 335 | } 336 | -------------------------------------------------------------------------------- /driver.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | driver.h 8 | 9 | Abstract: 10 | 11 | This module contains the function definitions for 12 | the WDF driver. 13 | 14 | Environment: 15 | 16 | kernel-mode only 17 | 18 | Revision History: 19 | 20 | --*/ 21 | 22 | #ifndef _DRIVER_H_ 23 | #define _DRIVER_H_ 24 | 25 | extern "C" 26 | 27 | NTSTATUS 28 | DriverEntry( 29 | _In_ PDRIVER_OBJECT pDriverObject, 30 | _In_ PUNICODE_STRING pRegistryPath 31 | ); 32 | 33 | EVT_WDF_DRIVER_DEVICE_ADD OnDeviceAdd; 34 | EVT_WDF_OBJECT_CONTEXT_CLEANUP OnDriverCleanup; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /i2ctrace.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | i2ctrace.h 8 | 9 | Abstract: 10 | 11 | This module contains the trace definitions for the PBC 12 | controller driver. 13 | 14 | Environment: 15 | 16 | kernel-mode only 17 | 18 | Revision History: 19 | 20 | --*/ 21 | 22 | #ifndef _I2CTRACE_H_ 23 | #define _I2CTRACE_H_ 24 | 25 | extern "C" 26 | { 27 | // 28 | // Tracing Definitions: 29 | // 30 | 31 | #define WPP_CONTROL_GUIDS \ 32 | WPP_DEFINE_CONTROL_GUID( \ 33 | PbcTraceGuid, \ 34 | (421EE9DB,1461,4D9B,9C7E,E7553729EBE8), \ 35 | WPP_DEFINE_BIT(TRACE_FLAG_WDFLOADING) \ 36 | WPP_DEFINE_BIT(TRACE_FLAG_SPBDDI) \ 37 | WPP_DEFINE_BIT(TRACE_FLAG_SPBAPI) \ 38 | WPP_DEFINE_BIT(TRACE_FLAG_PBCLOADING) \ 39 | WPP_DEFINE_BIT(TRACE_FLAG_TRANSFER) \ 40 | WPP_DEFINE_BIT(TRACE_FLAG_OTHER) \ 41 | ) 42 | } 43 | 44 | #define WPP_LEVEL_FLAGS_LOGGER(level,flags) WPP_LEVEL_LOGGER(flags) 45 | #define WPP_LEVEL_FLAGS_ENABLED(level, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level) 46 | 47 | // begin_wpp config 48 | // FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); 49 | // FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); 50 | // USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry"); 51 | // USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--"); 52 | // end_wpp 53 | 54 | #endif // _I2CTRACE_H_ 55 | -------------------------------------------------------------------------------- /internal.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | internal.h 8 | 9 | Abstract: 10 | 11 | This module contains the common internal type and function 12 | definitions for the SPB controller driver. 13 | 14 | Environment: 15 | 16 | kernel-mode only 17 | 18 | Revision History: 19 | 20 | --*/ 21 | 22 | #ifndef _INTERNAL_H_ 23 | #define _INTERNAL_H_ 24 | 25 | #pragma warning(push) 26 | #pragma warning(disable:4512) 27 | #pragma warning(disable:4480) 28 | 29 | #define SI2C_POOL_TAG ((ULONG) 'C2IS') 30 | 31 | ///////////////////////////////////////////////// 32 | // 33 | // Common includes. 34 | // 35 | ///////////////////////////////////////////////// 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "SPBCx.h" 44 | #include "i2ctrace.h" 45 | 46 | #define RESHUB_USE_HELPER_ROUTINES 47 | #include "reshub.h" 48 | 49 | 50 | ///////////////////////////////////////////////// 51 | // 52 | // Hardware definitions. 53 | // 54 | ///////////////////////////////////////////////// 55 | 56 | ///////////////////////////////////////////////// 57 | // 58 | // Resource and descriptor definitions. 59 | // 60 | ///////////////////////////////////////////////// 61 | 62 | #include "reshub.h" 63 | 64 | // 65 | // I2C Serial peripheral bus descriptor 66 | // 67 | 68 | #include "pshpack1.h" 69 | 70 | typedef struct _PNP_I2C_SERIAL_BUS_DESCRIPTOR { 71 | PNP_SERIAL_BUS_DESCRIPTOR SerialBusDescriptor; 72 | ULONG ConnectionSpeed; 73 | USHORT SlaveAddress; 74 | // follwed by optional Vendor Data 75 | // followed by PNP_IO_DESCRIPTOR_RESOURCE_NAME 76 | } PNP_I2C_SERIAL_BUS_DESCRIPTOR, *PPNP_I2C_SERIAL_BUS_DESCRIPTOR; 77 | 78 | typedef struct _PNP_SPI_SERIAL_BUS_DESCRIPTOR { 79 | PNP_SERIAL_BUS_DESCRIPTOR SerialBusDescriptor; 80 | // see ACPI spec 81 | ULONG ConnectionSpeed; 82 | UCHAR DataBitLength; 83 | UCHAR Phase; 84 | UCHAR Polarity; 85 | USHORT DeviceSelection; 86 | // follwed by optional Vendor Data 87 | // followed by PNP_IO_DESCRIPTOR_RESOURCE_NAME 88 | } PNP_SPI_SERIAL_BUS_DESCRIPTOR, *PPNP_SPI_SERIAL_BUS_DESCRIPTOR; 89 | 90 | #include "poppack.h" 91 | 92 | #define I2C_SERIAL_BUS_TYPE 0x01 93 | #define I2C_SERIAL_BUS_SPECIFIC_FLAG_10BIT_ADDRESS 0x0001 94 | 95 | #define SPI_SERIAL_BUS_TYPE 0x02 96 | 97 | ///////////////////////////////////////////////// 98 | // 99 | // Settings. 100 | // 101 | ///////////////////////////////////////////////// 102 | 103 | // 104 | // Power settings. 105 | // 106 | 107 | #define IDLE_TIMEOUT_MONITOR_ON 2000 108 | #define IDLE_TIMEOUT_MONITOR_OFF 50 109 | 110 | // 111 | // Target settings. 112 | // 113 | 114 | typedef enum ADDRESS_MODE 115 | { 116 | AddressMode7Bit, 117 | AddressMode10Bit 118 | } 119 | ADDRESS_MODE, *PADDRESS_MODE; 120 | 121 | typedef struct PBC_TARGET_SETTINGS 122 | { 123 | ADDRESS_MODE AddressMode; 124 | USHORT Address; 125 | ULONG ConnectionSpeed; 126 | } 127 | PBC_TARGET_SETTINGS, *PPBC_TARGET_SETTINGS; 128 | 129 | ///////////////////////////////////////////////// 130 | // 131 | // Context definitions. 132 | // 133 | ///////////////////////////////////////////////// 134 | 135 | typedef struct PBC_DEVICE PBC_DEVICE, *PPBC_DEVICE; 136 | typedef struct PBC_TARGET PBC_TARGET, *PPBC_TARGET; 137 | typedef struct PBC_REQUEST PBC_REQUEST, *PPBC_REQUEST; 138 | 139 | // 140 | // Device context. 141 | // 142 | 143 | struct PBC_DEVICE 144 | { 145 | // Handle to the WDF device. 146 | WDFDEVICE FxDevice; 147 | 148 | // 149 | // Connection ID for SPB peripheral 150 | // 151 | 152 | LARGE_INTEGER PeripheralId; 153 | 154 | // 155 | // SPB controller target 156 | // 157 | 158 | WDFIOTARGET TrueSpbController; 159 | 160 | // 161 | // SPB request object 162 | // 163 | 164 | WDFREQUEST SpbRequest; 165 | 166 | // 167 | // Input memory for request. Valid while request in progress. 168 | // 169 | 170 | WDFMEMORY InputMemory; 171 | 172 | // 173 | // Client request object 174 | // 175 | 176 | SPBREQUEST ClientRequest; 177 | 178 | // Target that the controller is currently 179 | // configured for. In most cases this value is only 180 | // set when there is a request being handled, however, 181 | // it will persist between lock and unlock requests. 182 | // There cannot be more than one current target. 183 | PPBC_TARGET pCurrentTarget; 184 | 185 | // The power setting callback handle 186 | PVOID pMonitorPowerSettingHandle; 187 | }; 188 | 189 | // 190 | // Target context. 191 | // 192 | 193 | struct PBC_TARGET 194 | { 195 | // Handle to the SPB target. 196 | SPBTARGET SpbTarget; 197 | 198 | // Target specific settings. 199 | PBC_TARGET_SETTINGS Settings; 200 | 201 | // Current request associated with the 202 | // target. This value should only be non-null 203 | // when this target is the controller's current 204 | // target. 205 | PPBC_REQUEST pCurrentRequest; 206 | }; 207 | 208 | // 209 | // Request context. 210 | // 211 | 212 | struct PBC_REQUEST 213 | { 214 | // 215 | // Associated framework device object 216 | // 217 | 218 | WDFDEVICE FxDevice; 219 | 220 | // 221 | // Variables that persist for the lifetime of 222 | // the request. Specifically these apply to an 223 | // entire sequence request (not just a single transfer). 224 | // 225 | 226 | // Handle to the SPB request. 227 | SPBREQUEST SpbRequest; 228 | 229 | }; 230 | 231 | // 232 | // Declate contexts for device, target, and request. 233 | // 234 | 235 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PBC_DEVICE, GetDeviceContext); 236 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PBC_TARGET, GetTargetContext); 237 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PBC_REQUEST, GetRequestContext); 238 | 239 | #pragma warning(pop) 240 | 241 | #endif // _INTERNAL_H_ 242 | -------------------------------------------------------------------------------- /peripheral.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | peripheral.cpp 8 | 9 | Abstract: 10 | 11 | This module contains the function for interaction 12 | with the SPB API. 13 | 14 | Environment: 15 | 16 | kernel-mode only 17 | 18 | Revision History: 19 | 20 | --*/ 21 | 22 | #include "internal.h" 23 | #include "peripheral.h" 24 | 25 | #include "peripheral.tmh" 26 | 27 | NTSTATUS 28 | SpbPeripheralOpen( 29 | _In_ PPBC_DEVICE pDevice 30 | ) 31 | /*++ 32 | 33 | Routine Description: 34 | 35 | This routine opens a handle to the SPB controller. 36 | 37 | Arguments: 38 | 39 | pDevice - a pointer to the device context 40 | 41 | Return Value: 42 | 43 | Status 44 | 45 | --*/ 46 | { 47 | FuncEntry(TRACE_FLAG_SPBAPI); 48 | 49 | WDF_IO_TARGET_OPEN_PARAMS openParams; 50 | NTSTATUS status; 51 | 52 | // 53 | // Create the device path using the connection ID. 54 | // 55 | 56 | DECLARE_UNICODE_STRING_SIZE(DevicePath, RESOURCE_HUB_PATH_SIZE); 57 | 58 | WdfDeviceStopIdle(pDevice->FxDevice, WdfTrue); 59 | 60 | if (pDevice->TrueSpbController == WDF_NO_HANDLE) 61 | { 62 | status = STATUS_NOT_SUPPORTED; 63 | goto exit; 64 | } 65 | 66 | RESOURCE_HUB_CREATE_PATH_FROM_ID( 67 | &DevicePath, 68 | pDevice->PeripheralId.LowPart, 69 | pDevice->PeripheralId.HighPart); 70 | 71 | Trace( 72 | TRACE_LEVEL_INFORMATION, 73 | TRACE_FLAG_SPBDDI, 74 | "Opening handle to SPB target via %wZ", 75 | &DevicePath); 76 | 77 | // 78 | // Open a handle to the SPB controller. 79 | // 80 | 81 | WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( 82 | &openParams, 83 | &DevicePath, 84 | (GENERIC_READ | GENERIC_WRITE)); 85 | 86 | openParams.ShareAccess = 0; 87 | openParams.CreateDisposition = FILE_OPEN; 88 | openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL; 89 | 90 | status = WdfIoTargetOpen( 91 | pDevice->TrueSpbController, 92 | &openParams); 93 | 94 | if (!NT_SUCCESS(status)) 95 | { 96 | Trace( 97 | TRACE_LEVEL_ERROR, 98 | TRACE_FLAG_SPBAPI, 99 | "Failed to open SPB target - %!STATUS!", 100 | status); 101 | } 102 | 103 | exit: 104 | FuncExit(TRACE_FLAG_SPBDDI); 105 | 106 | return status; 107 | } 108 | 109 | NTSTATUS 110 | SpbPeripheralClose( 111 | _In_ PPBC_DEVICE pDevice 112 | ) 113 | /*++ 114 | 115 | Routine Description: 116 | 117 | This routine closes a handle to the SPB controller. 118 | 119 | Arguments: 120 | 121 | pDevice - a pointer to the device context 122 | 123 | Return Value: 124 | 125 | Status 126 | 127 | --*/ 128 | { 129 | FuncEntry(TRACE_FLAG_SPBAPI); 130 | 131 | UNREFERENCED_PARAMETER(pDevice); 132 | 133 | Trace( 134 | TRACE_LEVEL_INFORMATION, 135 | TRACE_FLAG_SPBAPI, 136 | "Closing handle to SPB target"); 137 | 138 | if (pDevice->TrueSpbController != WDF_NO_HANDLE) 139 | WdfIoTargetClose(pDevice->TrueSpbController); 140 | 141 | WdfDeviceResumeIdle(pDevice->FxDevice); 142 | 143 | FuncExit(TRACE_FLAG_SPBAPI); 144 | 145 | return STATUS_SUCCESS; 146 | } 147 | 148 | VOID 149 | SpbPeripheralLock( 150 | _In_ PPBC_DEVICE pDevice, 151 | _In_ SPBREQUEST spbRequest 152 | ) 153 | /*++ 154 | 155 | Routine Description: 156 | 157 | This routine sends a lock command to the SPB controller. 158 | 159 | Arguments: 160 | 161 | pDevice - a pointer to the device context 162 | spbRequest - the framework request object 163 | 164 | Return Value: 165 | 166 | None 167 | 168 | --*/ 169 | { 170 | FuncEntry(TRACE_FLAG_SPBAPI); 171 | 172 | UNREFERENCED_PARAMETER(spbRequest); 173 | 174 | NTSTATUS status; 175 | 176 | Trace( 177 | TRACE_LEVEL_INFORMATION, 178 | TRACE_FLAG_SPBAPI, 179 | "Formatting SPB request %p for IOCTL_SPB_LOCK_CONTROLLER", 180 | pDevice->SpbRequest); 181 | 182 | // 183 | // Save the client request. 184 | // 185 | 186 | pDevice->ClientRequest = spbRequest; 187 | 188 | // 189 | // Initialize the SPB request for lock and send. 190 | // 191 | 192 | status = WdfIoTargetFormatRequestForIoctl( 193 | pDevice->TrueSpbController, 194 | pDevice->SpbRequest, 195 | IOCTL_SPB_LOCK_CONTROLLER, 196 | nullptr, 197 | nullptr, 198 | nullptr, 199 | nullptr); 200 | 201 | if (NT_SUCCESS(status)) 202 | { 203 | status = SpbPeripheralSendRequest( 204 | pDevice, 205 | pDevice->SpbRequest, 206 | spbRequest); 207 | } 208 | 209 | if (!NT_SUCCESS(status)) 210 | { 211 | Trace( 212 | TRACE_LEVEL_ERROR, 213 | TRACE_FLAG_SPBAPI, 214 | "Failed to send SPB request %p for " 215 | "IOCTL_SPB_LOCK_CONTROLLER - %!STATUS!", 216 | pDevice->SpbRequest, 217 | status); 218 | 219 | SpbPeripheralCompleteRequestPair( 220 | pDevice, 221 | status, 222 | 0); 223 | } 224 | 225 | FuncExit(TRACE_FLAG_SPBAPI); 226 | } 227 | 228 | VOID 229 | SpbPeripheralUnlock( 230 | _In_ PPBC_DEVICE pDevice, 231 | _In_ SPBREQUEST spbRequest 232 | ) 233 | /*++ 234 | 235 | Routine Description: 236 | 237 | This routine sends an unlock command to the SPB controller. 238 | 239 | Arguments: 240 | 241 | pDevice - a pointer to the device context 242 | spbRequest - the framework request object 243 | 244 | Return Value: 245 | 246 | None 247 | 248 | --*/ 249 | { 250 | FuncEntry(TRACE_FLAG_SPBAPI); 251 | 252 | UNREFERENCED_PARAMETER(spbRequest); 253 | 254 | NTSTATUS status; 255 | 256 | Trace( 257 | TRACE_LEVEL_INFORMATION, 258 | TRACE_FLAG_SPBAPI, 259 | "Formatting SPB request %p for IOCTL_SPB_UNLOCK_CONTROLLER", 260 | pDevice->SpbRequest); 261 | 262 | // 263 | // Save the client request. 264 | // 265 | 266 | pDevice->ClientRequest = spbRequest; 267 | 268 | // 269 | // Initialize the SPB request for unlock and send. 270 | // 271 | 272 | status = WdfIoTargetFormatRequestForIoctl( 273 | pDevice->TrueSpbController, 274 | pDevice->SpbRequest, 275 | IOCTL_SPB_UNLOCK_CONTROLLER, 276 | nullptr, 277 | nullptr, 278 | nullptr, 279 | nullptr); 280 | 281 | if (NT_SUCCESS(status)) 282 | { 283 | status = SpbPeripheralSendRequest( 284 | pDevice, 285 | pDevice->SpbRequest, 286 | spbRequest); 287 | } 288 | 289 | if (!NT_SUCCESS(status)) 290 | { 291 | Trace( 292 | TRACE_LEVEL_ERROR, 293 | TRACE_FLAG_SPBAPI, 294 | "Failed to send SPB request %p for " 295 | "IOCTL_SPB_UNLOCK_CONTROLLER - %!STATUS!", 296 | pDevice->SpbRequest, 297 | status); 298 | 299 | SpbPeripheralCompleteRequestPair( 300 | pDevice, 301 | status, 302 | 0); 303 | } 304 | 305 | FuncExit(TRACE_FLAG_SPBAPI); 306 | } 307 | 308 | VOID 309 | SpbPeripheralLockConnection( 310 | _In_ PPBC_DEVICE pDevice, 311 | _In_ SPBREQUEST spbRequest 312 | ) 313 | /*++ 314 | 315 | Routine Description: 316 | 317 | This routine sends a lock connection command to the SPB controller. 318 | 319 | Arguments: 320 | 321 | pDevice - a pointer to the device context 322 | spbRequest - the framework request object 323 | 324 | Return Value: 325 | 326 | None 327 | 328 | --*/ 329 | { 330 | FuncEntry(TRACE_FLAG_SPBAPI); 331 | 332 | UNREFERENCED_PARAMETER(spbRequest); 333 | 334 | NTSTATUS status; 335 | 336 | Trace( 337 | TRACE_LEVEL_INFORMATION, 338 | TRACE_FLAG_SPBAPI, 339 | "Formatting SPB request %p for IOCTL_SPB_LOCK_CONNECTION", 340 | pDevice->SpbRequest); 341 | 342 | // 343 | // Save the client request. 344 | // 345 | 346 | pDevice->ClientRequest = spbRequest; 347 | 348 | // 349 | // Initialize the SPB request for lock and send. 350 | // 351 | 352 | status = WdfIoTargetFormatRequestForIoctl( 353 | pDevice->TrueSpbController, 354 | pDevice->SpbRequest, 355 | IOCTL_SPB_LOCK_CONNECTION, 356 | nullptr, 357 | nullptr, 358 | nullptr, 359 | nullptr); 360 | 361 | if (NT_SUCCESS(status)) 362 | { 363 | status = SpbPeripheralSendRequest( 364 | pDevice, 365 | pDevice->SpbRequest, 366 | spbRequest); 367 | } 368 | 369 | if (!NT_SUCCESS(status)) 370 | { 371 | Trace( 372 | TRACE_LEVEL_ERROR, 373 | TRACE_FLAG_SPBAPI, 374 | "Failed to send SPB request %p for " 375 | "IOCTL_SPB_LOCK_CONNECTION - %!STATUS!", 376 | pDevice->SpbRequest, 377 | status); 378 | 379 | SpbPeripheralCompleteRequestPair( 380 | pDevice, 381 | status, 382 | 0); 383 | } 384 | 385 | FuncExit(TRACE_FLAG_SPBAPI); 386 | } 387 | 388 | VOID 389 | SpbPeripheralUnlockConnection( 390 | _In_ PPBC_DEVICE pDevice, 391 | _In_ SPBREQUEST spbRequest 392 | ) 393 | /*++ 394 | 395 | Routine Description: 396 | 397 | This routine sends an unlock connection command to the SPB controller. 398 | 399 | Arguments: 400 | 401 | pDevice - a pointer to the device context 402 | spbRequest - the framework request object 403 | 404 | Return Value: 405 | 406 | None 407 | 408 | --*/ 409 | { 410 | FuncEntry(TRACE_FLAG_SPBAPI); 411 | 412 | UNREFERENCED_PARAMETER(spbRequest); 413 | 414 | NTSTATUS status; 415 | 416 | Trace( 417 | TRACE_LEVEL_INFORMATION, 418 | TRACE_FLAG_SPBAPI, 419 | "Formatting SPB request %p for IOCTL_SPB_UNLOCK_CONNECTION", 420 | pDevice->SpbRequest); 421 | 422 | // 423 | // Save the client request. 424 | // 425 | 426 | pDevice->ClientRequest = spbRequest; 427 | 428 | // 429 | // Initialize the SPB request for unlock and send. 430 | // 431 | 432 | status = WdfIoTargetFormatRequestForIoctl( 433 | pDevice->TrueSpbController, 434 | pDevice->SpbRequest, 435 | IOCTL_SPB_UNLOCK_CONNECTION, 436 | nullptr, 437 | nullptr, 438 | nullptr, 439 | nullptr); 440 | 441 | if (NT_SUCCESS(status)) 442 | { 443 | status = SpbPeripheralSendRequest( 444 | pDevice, 445 | pDevice->SpbRequest, 446 | spbRequest); 447 | } 448 | 449 | if (!NT_SUCCESS(status)) 450 | { 451 | Trace( 452 | TRACE_LEVEL_ERROR, 453 | TRACE_FLAG_SPBAPI, 454 | "Failed to send SPB request %p for " 455 | "IOCTL_SPB_UNLOCK_CONNECTION - %!STATUS!", 456 | pDevice->SpbRequest, 457 | status); 458 | 459 | SpbPeripheralCompleteRequestPair( 460 | pDevice, 461 | status, 462 | 0); 463 | } 464 | 465 | FuncExit(TRACE_FLAG_SPBAPI); 466 | } 467 | 468 | VOID 469 | SpbTraceBufferIndex( 470 | _In_ PPBC_DEVICE pDevice, 471 | _In_ SPBREQUEST clientRequest, 472 | _In_ ULONG index 473 | ) 474 | { 475 | SPB_TRANSFER_DESCRIPTOR transferDescriptor; 476 | PMDL pMdl; 477 | const ULONG max_len = 1024; 478 | UCHAR pBuffer[max_len] = { 0 }; 479 | CHAR pPrefix[32]; /* format "device NNN: ##nn write llll -" */ 480 | CHAR pDataString[5 + 3 * 16 + 1]; /* format "0000: XX XX XX XX" */ 481 | int dataIndex; 482 | SPB_TRANSFER_DESCRIPTOR_INIT(&transferDescriptor); 483 | 484 | SpbRequestGetTransferParameters( 485 | clientRequest, 486 | index, 487 | &transferDescriptor, 488 | &pMdl); 489 | 490 | for (ULONG offset = 0; offset < (ULONG)transferDescriptor.TransferLength; offset += max_len) 491 | { 492 | ULONG length = min((ULONG)transferDescriptor.TransferLength - offset, max_len); 493 | 494 | for (ULONG i = 0; i < length; i++) 495 | { 496 | RequestGetByte(pMdl, transferDescriptor.TransferLength, i + offset, &pBuffer[i]); 497 | } 498 | 499 | sprintf_s(pPrefix, sizeof(pPrefix), 500 | "device %3I64d: %c#%02d %5s %4lu - ", 501 | pDevice->PeripheralId.QuadPart, 502 | index == 0 ? '#' : ' ', 503 | index, 504 | transferDescriptor.Direction == SpbTransferDirectionToDevice ? "write" : "read", 505 | (unsigned long)transferDescriptor.TransferLength 506 | ); 507 | 508 | dataIndex = 0; 509 | dataIndex += sprintf(&pDataString[dataIndex], "%04x: %02x", offset, pBuffer[0]); 510 | for (ULONG i = 1; i <= length; i++) 511 | { 512 | if ((i % 16 == 0) || i == length) 513 | { 514 | Trace( 515 | TRACE_LEVEL_ERROR, 516 | TRACE_FLAG_SPBAPI, 517 | "%s %s", 518 | pPrefix, 519 | pDataString 520 | ); 521 | if (i == length) 522 | { 523 | break; 524 | } 525 | dataIndex = 0; 526 | dataIndex += sprintf(&pDataString[dataIndex], "%04x:", i + offset); 527 | } 528 | dataIndex += sprintf(&pDataString[dataIndex], " %02x", pBuffer[i]); 529 | } 530 | } 531 | } 532 | 533 | VOID 534 | SpbTraceBuffers( 535 | _In_ PPBC_DEVICE pDevice, 536 | _In_ SPBREQUEST clientRequest 537 | ) 538 | { 539 | SPB_REQUEST_PARAMETERS parameters; 540 | 541 | SPB_REQUEST_PARAMETERS_INIT(¶meters); 542 | 543 | SpbRequestGetParameters(clientRequest, ¶meters); 544 | 545 | for (ULONG i = 0; i < parameters.SequenceTransferCount; i += 1) 546 | { 547 | SpbTraceBufferIndex(pDevice, clientRequest, i); 548 | } 549 | 550 | } 551 | 552 | VOID 553 | SpbPeripheralRead( 554 | _In_ PPBC_DEVICE pDevice, 555 | _In_ SPBREQUEST spbRequest, 556 | _In_ BOOLEAN FullDuplex 557 | ) 558 | /*++ 559 | 560 | Routine Description: 561 | 562 | This routine reads from the SPB controller. 563 | 564 | Arguments: 565 | 566 | pDevice - a pointer to the device context 567 | spbRequest - the framework request object 568 | 569 | Return Value: 570 | 571 | None 572 | 573 | --*/ 574 | { 575 | FuncEntry(TRACE_FLAG_SPBAPI); 576 | 577 | UNREFERENCED_PARAMETER(spbRequest); 578 | 579 | WDFMEMORY memory = nullptr; 580 | NTSTATUS status; 581 | 582 | Trace( 583 | TRACE_LEVEL_INFORMATION, 584 | TRACE_FLAG_SPBAPI, 585 | "Formatting SPB request %p for read", 586 | pDevice->SpbRequest); 587 | 588 | // 589 | // Save the client request. 590 | // 591 | 592 | pDevice->ClientRequest = spbRequest; 593 | 594 | // 595 | // Initialize the SPB request for read and send. 596 | // 597 | 598 | if (!FullDuplex) 599 | { 600 | status = WdfRequestRetrieveOutputMemory( 601 | spbRequest, 602 | &memory); 603 | } 604 | else 605 | { 606 | status = WdfRequestRetrieveInputMemory( 607 | spbRequest, 608 | &memory); 609 | } 610 | 611 | if (NT_SUCCESS(status)) 612 | { 613 | status = WdfIoTargetFormatRequestForRead( 614 | pDevice->TrueSpbController, 615 | pDevice->SpbRequest, 616 | memory, 617 | nullptr, 618 | nullptr); 619 | 620 | if (NT_SUCCESS(status)) 621 | { 622 | status = SpbPeripheralSendRequest( 623 | pDevice, 624 | pDevice->SpbRequest, 625 | spbRequest); 626 | } 627 | } 628 | 629 | if (!NT_SUCCESS(status)) 630 | { 631 | Trace( 632 | TRACE_LEVEL_ERROR, 633 | TRACE_FLAG_SPBAPI, 634 | "Failed to send SPB request %p for " 635 | "read - %!STATUS!", 636 | pDevice->SpbRequest, 637 | status); 638 | 639 | SpbPeripheralCompleteRequestPair( 640 | pDevice, 641 | status, 642 | 0); 643 | } 644 | 645 | FuncExit(TRACE_FLAG_SPBAPI); 646 | } 647 | 648 | VOID 649 | SpbPeripheralWrite( 650 | _In_ PPBC_DEVICE pDevice, 651 | _In_ SPBREQUEST spbRequest, 652 | _In_ BOOLEAN FullDuplex 653 | ) 654 | /*++ 655 | 656 | Routine Description: 657 | 658 | This routine writes to the SPB controller. 659 | 660 | Arguments: 661 | 662 | pDevice - a pointer to the device context 663 | spbRequest - the framework request object 664 | 665 | Return Value: 666 | 667 | None 668 | 669 | --*/ 670 | { 671 | FuncEntry(TRACE_FLAG_SPBAPI); 672 | 673 | UNREFERENCED_PARAMETER(spbRequest); 674 | 675 | WDFMEMORY memory = nullptr; 676 | NTSTATUS status; 677 | 678 | Trace( 679 | TRACE_LEVEL_INFORMATION, 680 | TRACE_FLAG_SPBAPI, 681 | "Formatting SPB request %p for write", 682 | pDevice->SpbRequest); 683 | 684 | // 685 | // Save the client request. 686 | // 687 | 688 | pDevice->ClientRequest = spbRequest; 689 | 690 | // 691 | // Initialize the SPB request for write and send. 692 | // 693 | 694 | if (!FullDuplex) 695 | { 696 | status = WdfRequestRetrieveInputMemory( 697 | spbRequest, 698 | &memory); 699 | } 700 | else 701 | { 702 | status = WdfRequestRetrieveOutputMemory( 703 | spbRequest, 704 | &memory); 705 | } 706 | 707 | if (NT_SUCCESS(status)) 708 | { 709 | status = WdfIoTargetFormatRequestForWrite( 710 | pDevice->TrueSpbController, 711 | pDevice->SpbRequest, 712 | memory, 713 | nullptr, 714 | nullptr); 715 | 716 | if (NT_SUCCESS(status)) 717 | { 718 | status = SpbPeripheralSendRequest( 719 | pDevice, 720 | pDevice->SpbRequest, 721 | spbRequest); 722 | } 723 | } 724 | 725 | if (!NT_SUCCESS(status)) 726 | { 727 | Trace( 728 | TRACE_LEVEL_ERROR, 729 | TRACE_FLAG_SPBAPI, 730 | "Failed to send SPB request %p for " 731 | "write - %!STATUS!", 732 | pDevice->SpbRequest, 733 | status); 734 | 735 | SpbPeripheralCompleteRequestPair( 736 | pDevice, 737 | status, 738 | 0); 739 | } 740 | 741 | FuncExit(TRACE_FLAG_SPBAPI); 742 | } 743 | 744 | VOID 745 | SpbPeripheralFullDuplex( 746 | _In_ PPBC_DEVICE pDevice, 747 | _In_ SPBREQUEST spbRequest 748 | ) 749 | /*++ 750 | 751 | Routine Description: 752 | 753 | This routine sends a full duplex transfer to the SPB controller. 754 | 755 | Arguments: 756 | 757 | pDevice - a pointer to the device context 758 | spbRequest - the framework request object 759 | 760 | Return Value: 761 | 762 | None 763 | 764 | --*/ 765 | { 766 | FuncEntry(TRACE_FLAG_SPBAPI); 767 | 768 | UNREFERENCED_PARAMETER(spbRequest); 769 | 770 | WDF_OBJECT_ATTRIBUTES attributes; 771 | PPBC_REQUEST pRequest; 772 | NTSTATUS status; 773 | 774 | pRequest = GetRequestContext(pDevice->SpbRequest); 775 | 776 | Trace( 777 | TRACE_LEVEL_INFORMATION, 778 | TRACE_FLAG_SPBAPI, 779 | "Formatting SPB request %p for IOCTL_SPB_FULL_DUPLEX", 780 | pDevice->SpbRequest); 781 | 782 | // 783 | // Save the client request. 784 | // 785 | 786 | pDevice->ClientRequest = spbRequest; 787 | 788 | // 789 | // Get input and output buffers. 790 | // 791 | 792 | const ULONG fullDuplexWriteIndex = 0; 793 | const ULONG fullDuplexReadIndex = 1; 794 | 795 | SPB_TRANSFER_DESCRIPTOR writeDescriptor; 796 | SPB_TRANSFER_DESCRIPTOR readDescriptor; 797 | PMDL pWriteMdl; 798 | PMDL pReadMdl; 799 | 800 | SPB_TRANSFER_DESCRIPTOR_INIT(&writeDescriptor); 801 | SPB_TRANSFER_DESCRIPTOR_INIT(&readDescriptor); 802 | 803 | SpbRequestGetTransferParameters( 804 | spbRequest, 805 | fullDuplexWriteIndex, 806 | &writeDescriptor, 807 | &pWriteMdl); 808 | 809 | SpbRequestGetTransferParameters( 810 | spbRequest, 811 | fullDuplexReadIndex, 812 | &readDescriptor, 813 | &pReadMdl); 814 | 815 | // 816 | // Build full duplex transfer using SPB transfer list. 817 | // 818 | 819 | const ULONG transfers = 2; 820 | 821 | SPB_TRANSFER_LIST_AND_ENTRIES(transfers) seq; 822 | SPB_TRANSFER_LIST_INIT(&(seq.List), transfers); 823 | 824 | { 825 | // 826 | // PreFAST cannot figure out the SPB_TRANSFER_LIST_ENTRY 827 | // "struct hack" size but using an index variable quiets 828 | // the warning. This is a false positive from OACR. 829 | // 830 | 831 | const ULONG index = 0; 832 | 833 | seq.List.Transfers[index] = SPB_TRANSFER_LIST_ENTRY_INIT_MDL( 834 | SpbTransferDirectionToDevice, 835 | 0, 836 | pWriteMdl); 837 | 838 | seq.List.Transfers[index + 1] = SPB_TRANSFER_LIST_ENTRY_INIT_MDL( 839 | SpbTransferDirectionFromDevice, 840 | 0, 841 | pReadMdl); 842 | } 843 | 844 | // 845 | // Create preallocated WDFMEMORY. The IOCTL is METHOD_BUFFERED, 846 | // so the memory doesn't have to persist until the request is 847 | // completed. 848 | // 849 | 850 | NT_ASSERT(pDevice->InputMemory == WDF_NO_HANDLE); 851 | 852 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 853 | 854 | status = WdfMemoryCreatePreallocated( 855 | &attributes, 856 | (PVOID)&seq, 857 | sizeof(seq), 858 | &pDevice->InputMemory); 859 | 860 | if (!NT_SUCCESS(status)) 861 | { 862 | Trace( 863 | TRACE_LEVEL_ERROR, 864 | TRACE_FLAG_SPBAPI, 865 | "Failed to create WDFMEMORY - %!STATUS!", 866 | status); 867 | 868 | goto Done; 869 | } 870 | 871 | Trace( 872 | TRACE_LEVEL_INFORMATION, 873 | TRACE_FLAG_SPBAPI, 874 | "Built full duplex transfer %p with byte length=%lu", 875 | &seq, 876 | (ULONG)(writeDescriptor.TransferLength + readDescriptor.TransferLength)); 877 | 878 | // 879 | // Send full duplex IOCTL. 880 | // 881 | 882 | // 883 | // Format and send the full duplex request. 884 | // 885 | 886 | status = WdfIoTargetFormatRequestForIoctl( 887 | pDevice->TrueSpbController, 888 | pDevice->SpbRequest, 889 | IOCTL_SPB_FULL_DUPLEX, 890 | pDevice->InputMemory, 891 | nullptr, 892 | nullptr, 893 | nullptr); 894 | 895 | if (!NT_SUCCESS(status)) 896 | { 897 | Trace( 898 | TRACE_LEVEL_ERROR, 899 | TRACE_FLAG_SPBAPI, 900 | "Failed to format request - %!STATUS!", 901 | status); 902 | 903 | goto Done; 904 | } 905 | 906 | status = SpbPeripheralSendRequest( 907 | pDevice, 908 | pDevice->SpbRequest, 909 | spbRequest); 910 | 911 | if (!NT_SUCCESS(status)) 912 | { 913 | Trace( 914 | TRACE_LEVEL_ERROR, 915 | TRACE_FLAG_SPBAPI, 916 | "Failed to send SPB request %p for " 917 | "IOCTL_SPB_FULL_DUPLEX - %!STATUS!", 918 | pDevice->SpbRequest, 919 | status); 920 | 921 | goto Done; 922 | } 923 | 924 | Done: 925 | 926 | if (!NT_SUCCESS(status)) 927 | { 928 | SpbPeripheralCompleteRequestPair( 929 | pDevice, 930 | status, 931 | 0); 932 | } 933 | 934 | FuncExit(TRACE_FLAG_SPBAPI); 935 | } 936 | 937 | NTSTATUS 938 | SpbPeripheralSequence1( 939 | _In_ PPBC_DEVICE pDevice, 940 | _In_ SPBREQUEST spbRequest 941 | ) 942 | /*++ 943 | 944 | Routine Description: 945 | 946 | This routine sends a sequence of 1 transfer to the SPB controller. 947 | 948 | Arguments: 949 | 950 | pDevice - a pointer to the device context 951 | spbRequest - the framework request object 952 | 953 | Return Value: 954 | 955 | None 956 | 957 | --*/ 958 | { 959 | FuncEntry(TRACE_FLAG_SPBAPI); 960 | 961 | UNREFERENCED_PARAMETER(spbRequest); 962 | 963 | WDF_OBJECT_ATTRIBUTES attributes; 964 | PPBC_REQUEST pRequest; 965 | NTSTATUS status; 966 | 967 | pRequest = GetRequestContext(pDevice->SpbRequest); 968 | 969 | Trace( 970 | TRACE_LEVEL_INFORMATION, 971 | TRACE_FLAG_SPBAPI, 972 | "Formatting SPB request %p for IOCTL_SPB_EXECUTE_SEQUENCE", 973 | pDevice->SpbRequest); 974 | 975 | // 976 | // Save the client request. 977 | // 978 | 979 | pDevice->ClientRequest = spbRequest; 980 | 981 | // 982 | // Get buffer. 983 | // 984 | 985 | SPB_TRANSFER_DESCRIPTOR firstDescriptor; 986 | PMDL pFirstMdl; 987 | 988 | SPB_TRANSFER_DESCRIPTOR_INIT(&firstDescriptor); 989 | 990 | SpbRequestGetTransferParameters( 991 | spbRequest, 992 | 0, 993 | &firstDescriptor, 994 | &pFirstMdl); 995 | 996 | // 997 | // Build full duplex transfer using SPB transfer list. 998 | // 999 | 1000 | const ULONG transfers = 2; 1001 | 1002 | SPB_TRANSFER_LIST seq; 1003 | SPB_TRANSFER_LIST_INIT(&seq, 1); 1004 | 1005 | { 1006 | // 1007 | // PreFAST cannot figure out the SPB_TRANSFER_LIST_ENTRY 1008 | // "struct hack" size but using an index variable quiets 1009 | // the warning. This is a false positive from OACR. 1010 | // 1011 | 1012 | const ULONG index = 0; 1013 | 1014 | seq.Transfers[index] = SPB_TRANSFER_LIST_ENTRY_INIT_MDL( 1015 | firstDescriptor.Direction, 1016 | 0, 1017 | pFirstMdl); 1018 | } 1019 | 1020 | // 1021 | // Create preallocated WDFMEMORY. The IOCTL is METHOD_BUFFERED, 1022 | // so the memory doesn't have to persist until the request is 1023 | // completed. 1024 | // 1025 | 1026 | NT_ASSERT(pDevice->InputMemory == WDF_NO_HANDLE); 1027 | 1028 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 1029 | 1030 | status = WdfMemoryCreatePreallocated( 1031 | &attributes, 1032 | (PVOID)&seq, 1033 | sizeof(seq), 1034 | &pDevice->InputMemory); 1035 | 1036 | if (!NT_SUCCESS(status)) 1037 | { 1038 | Trace( 1039 | TRACE_LEVEL_ERROR, 1040 | TRACE_FLAG_SPBAPI, 1041 | "Failed to create WDFMEMORY - %!STATUS!", 1042 | status); 1043 | 1044 | goto Done; 1045 | } 1046 | 1047 | Trace( 1048 | TRACE_LEVEL_INFORMATION, 1049 | TRACE_FLAG_SPBAPI, 1050 | "Built sequence transfer %p with byte length=%lu", 1051 | &seq, 1052 | (ULONG)(firstDescriptor.TransferLength)); 1053 | 1054 | // 1055 | // Send full duplex IOCTL. 1056 | // 1057 | 1058 | // 1059 | // Format and send the full duplex request. 1060 | // 1061 | 1062 | status = WdfIoTargetFormatRequestForIoctl( 1063 | pDevice->TrueSpbController, 1064 | pDevice->SpbRequest, 1065 | IOCTL_SPB_EXECUTE_SEQUENCE, 1066 | pDevice->InputMemory, 1067 | nullptr, 1068 | nullptr, 1069 | nullptr); 1070 | 1071 | if (!NT_SUCCESS(status)) 1072 | { 1073 | Trace( 1074 | TRACE_LEVEL_ERROR, 1075 | TRACE_FLAG_SPBAPI, 1076 | "Failed to format request - %!STATUS!", 1077 | status); 1078 | 1079 | goto Done; 1080 | } 1081 | 1082 | status = SpbPeripheralSendRequest( 1083 | pDevice, 1084 | pDevice->SpbRequest, 1085 | spbRequest); 1086 | 1087 | if (!NT_SUCCESS(status)) 1088 | { 1089 | Trace( 1090 | TRACE_LEVEL_ERROR, 1091 | TRACE_FLAG_SPBAPI, 1092 | "Failed to send SPB request %p for " 1093 | "IOCTL_SPB_EXECUTE_SEQUENCE - %!STATUS!", 1094 | pDevice->SpbRequest, 1095 | status); 1096 | 1097 | goto Done; 1098 | } 1099 | 1100 | Done: 1101 | 1102 | FuncExit(TRACE_FLAG_SPBAPI); 1103 | 1104 | return status; 1105 | } 1106 | 1107 | NTSTATUS 1108 | SpbPeripheralSequence2( 1109 | _In_ PPBC_DEVICE pDevice, 1110 | _In_ SPBREQUEST spbRequest 1111 | ) 1112 | /*++ 1113 | 1114 | Routine Description: 1115 | 1116 | This routine sends a sequence of 2 transfers to the SPB controller. 1117 | 1118 | Arguments: 1119 | 1120 | pDevice - a pointer to the device context 1121 | spbRequest - the framework request object 1122 | 1123 | Return Value: 1124 | 1125 | None 1126 | 1127 | --*/ 1128 | { 1129 | FuncEntry(TRACE_FLAG_SPBAPI); 1130 | 1131 | UNREFERENCED_PARAMETER(spbRequest); 1132 | 1133 | WDF_OBJECT_ATTRIBUTES attributes; 1134 | PPBC_REQUEST pRequest; 1135 | NTSTATUS status; 1136 | 1137 | pRequest = GetRequestContext(pDevice->SpbRequest); 1138 | 1139 | Trace( 1140 | TRACE_LEVEL_INFORMATION, 1141 | TRACE_FLAG_SPBAPI, 1142 | "Formatting SPB request %p for IOCTL_SPB_EXECUTE_SEQUENCE", 1143 | pDevice->SpbRequest); 1144 | 1145 | // 1146 | // Save the client request. 1147 | // 1148 | 1149 | pDevice->ClientRequest = spbRequest; 1150 | 1151 | // 1152 | // Get input and output buffers. 1153 | // 1154 | 1155 | SPB_TRANSFER_DESCRIPTOR firstDescriptor; 1156 | SPB_TRANSFER_DESCRIPTOR secondDescriptor; 1157 | PMDL pFirstMdl; 1158 | PMDL pSecondMdl; 1159 | 1160 | SPB_TRANSFER_DESCRIPTOR_INIT(&firstDescriptor); 1161 | SPB_TRANSFER_DESCRIPTOR_INIT(&secondDescriptor); 1162 | 1163 | SpbRequestGetTransferParameters( 1164 | spbRequest, 1165 | 0, 1166 | &firstDescriptor, 1167 | &pFirstMdl); 1168 | 1169 | SpbRequestGetTransferParameters( 1170 | spbRequest, 1171 | 1, 1172 | &secondDescriptor, 1173 | &pSecondMdl); 1174 | 1175 | // 1176 | // Build full duplex transfer using SPB transfer list. 1177 | // 1178 | 1179 | const ULONG transfers = 2; 1180 | 1181 | SPB_TRANSFER_LIST_AND_ENTRIES(transfers) seq; 1182 | SPB_TRANSFER_LIST_INIT(&(seq.List), transfers); 1183 | 1184 | { 1185 | // 1186 | // PreFAST cannot figure out the SPB_TRANSFER_LIST_ENTRY 1187 | // "struct hack" size but using an index variable quiets 1188 | // the warning. This is a false positive from OACR. 1189 | // 1190 | 1191 | const ULONG index = 0; 1192 | 1193 | seq.List.Transfers[index] = SPB_TRANSFER_LIST_ENTRY_INIT_MDL( 1194 | firstDescriptor.Direction, 1195 | 0, 1196 | pFirstMdl); 1197 | 1198 | seq.List.Transfers[index + 1] = SPB_TRANSFER_LIST_ENTRY_INIT_MDL( 1199 | secondDescriptor.Direction, 1200 | 0, 1201 | pSecondMdl); 1202 | } 1203 | 1204 | // 1205 | // Create preallocated WDFMEMORY. The IOCTL is METHOD_BUFFERED, 1206 | // so the memory doesn't have to persist until the request is 1207 | // completed. 1208 | // 1209 | 1210 | NT_ASSERT(pDevice->InputMemory == WDF_NO_HANDLE); 1211 | 1212 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 1213 | 1214 | status = WdfMemoryCreatePreallocated( 1215 | &attributes, 1216 | (PVOID)&seq, 1217 | sizeof(seq), 1218 | &pDevice->InputMemory); 1219 | 1220 | if (!NT_SUCCESS(status)) 1221 | { 1222 | Trace( 1223 | TRACE_LEVEL_ERROR, 1224 | TRACE_FLAG_SPBAPI, 1225 | "Failed to create WDFMEMORY - %!STATUS!", 1226 | status); 1227 | 1228 | goto Done; 1229 | } 1230 | 1231 | Trace( 1232 | TRACE_LEVEL_INFORMATION, 1233 | TRACE_FLAG_SPBAPI, 1234 | "Built sequence transfer %p with byte length=%lu", 1235 | &seq, 1236 | (ULONG)(firstDescriptor.TransferLength + secondDescriptor.TransferLength)); 1237 | 1238 | // 1239 | // Send full duplex IOCTL. 1240 | // 1241 | 1242 | // 1243 | // Format and send the full duplex request. 1244 | // 1245 | 1246 | status = WdfIoTargetFormatRequestForIoctl( 1247 | pDevice->TrueSpbController, 1248 | pDevice->SpbRequest, 1249 | IOCTL_SPB_EXECUTE_SEQUENCE, 1250 | pDevice->InputMemory, 1251 | nullptr, 1252 | nullptr, 1253 | nullptr); 1254 | 1255 | if (!NT_SUCCESS(status)) 1256 | { 1257 | Trace( 1258 | TRACE_LEVEL_ERROR, 1259 | TRACE_FLAG_SPBAPI, 1260 | "Failed to format request - %!STATUS!", 1261 | status); 1262 | 1263 | goto Done; 1264 | } 1265 | 1266 | status = SpbPeripheralSendRequest( 1267 | pDevice, 1268 | pDevice->SpbRequest, 1269 | spbRequest); 1270 | 1271 | if (!NT_SUCCESS(status)) 1272 | { 1273 | Trace( 1274 | TRACE_LEVEL_ERROR, 1275 | TRACE_FLAG_SPBAPI, 1276 | "Failed to send SPB request %p for " 1277 | "IOCTL_SPB_EXECUTE_SEQUENCE - %!STATUS!", 1278 | pDevice->SpbRequest, 1279 | status); 1280 | 1281 | goto Done; 1282 | } 1283 | 1284 | Done: 1285 | 1286 | FuncExit(TRACE_FLAG_SPBAPI); 1287 | 1288 | return status; 1289 | } 1290 | 1291 | VOID 1292 | SpbPeripheralSequence( 1293 | _In_ PPBC_DEVICE pDevice, 1294 | _In_ SPBREQUEST spbRequest, 1295 | _In_ ULONG TransferCount 1296 | ) 1297 | /*++ 1298 | 1299 | Routine Description: 1300 | 1301 | This routine sends a sequence of 2 transfers to the SPB controller. 1302 | 1303 | Arguments: 1304 | 1305 | pDevice - a pointer to the device context 1306 | spbRequest - the framework request object 1307 | TransferCount - the transfer count 1308 | 1309 | Return Value: 1310 | 1311 | None 1312 | 1313 | --*/ 1314 | { 1315 | FuncEntry(TRACE_FLAG_SPBAPI); 1316 | 1317 | NTSTATUS status = STATUS_NOT_SUPPORTED; 1318 | 1319 | switch (TransferCount) 1320 | { 1321 | case 1: 1322 | status = SpbPeripheralSequence1(pDevice, spbRequest); 1323 | break; 1324 | case 2: 1325 | status = SpbPeripheralSequence2(pDevice, spbRequest); 1326 | break; 1327 | } 1328 | 1329 | if (!NT_SUCCESS(status)) 1330 | { 1331 | Trace( 1332 | TRACE_LEVEL_ERROR, 1333 | TRACE_FLAG_SPBAPI, 1334 | "Failed to send SPB request %p for " 1335 | "IOCTL_SPB_FULL_DUPLEX - %!STATUS!", 1336 | pDevice->SpbRequest, 1337 | status); 1338 | 1339 | goto Done; 1340 | } 1341 | 1342 | Done: 1343 | 1344 | if (!NT_SUCCESS(status)) 1345 | { 1346 | SpbPeripheralCompleteRequestPair( 1347 | pDevice, 1348 | status, 1349 | 0); 1350 | } 1351 | 1352 | FuncExit(TRACE_FLAG_SPBAPI); 1353 | } 1354 | 1355 | NTSTATUS 1356 | SpbPeripheralSendRequest( 1357 | _In_ PPBC_DEVICE pDevice, 1358 | _In_ WDFREQUEST SpbRequest, 1359 | _In_ WDFREQUEST ClientRequest 1360 | ) 1361 | /*++ 1362 | 1363 | Routine Description: 1364 | 1365 | This routine sends a write-read sequence to the SPB controller. 1366 | 1367 | Arguments: 1368 | 1369 | pDevice - a pointer to the device context 1370 | SpbRequest - the SPB request object 1371 | ClientRequest - the client request object 1372 | 1373 | Return Value: 1374 | 1375 | Status 1376 | 1377 | --*/ 1378 | { 1379 | FuncEntry(TRACE_FLAG_SPBAPI); 1380 | 1381 | PPBC_REQUEST pRequest = GetRequestContext(ClientRequest); 1382 | NTSTATUS status = STATUS_SUCCESS; 1383 | 1384 | Trace( 1385 | TRACE_LEVEL_INFORMATION, 1386 | TRACE_FLAG_SPBAPI, 1387 | "Saving client request %p, and " 1388 | "sending SPB request %p", 1389 | ClientRequest, 1390 | SpbRequest); 1391 | 1392 | // 1393 | // Init client request context. 1394 | // 1395 | 1396 | pRequest->FxDevice = pDevice->FxDevice; 1397 | 1398 | // 1399 | // Mark the client request as cancellable. 1400 | // 1401 | 1402 | if (NT_SUCCESS(status)) 1403 | { 1404 | status = WdfRequestMarkCancelableEx( 1405 | ClientRequest, 1406 | SpbPeripheralOnCancel); 1407 | } 1408 | 1409 | // 1410 | // Send the SPB request. 1411 | // 1412 | 1413 | if (NT_SUCCESS(status)) 1414 | { 1415 | WdfRequestSetCompletionRoutine( 1416 | SpbRequest, 1417 | SpbPeripheralOnCompletion, 1418 | GetRequestContext(SpbRequest)); 1419 | 1420 | BOOLEAN fSent = WdfRequestSend( 1421 | SpbRequest, 1422 | pDevice->TrueSpbController, 1423 | WDF_NO_SEND_OPTIONS); 1424 | 1425 | if (!fSent) 1426 | { 1427 | status = WdfRequestGetStatus(SpbRequest); 1428 | 1429 | Trace( 1430 | TRACE_LEVEL_ERROR, 1431 | TRACE_FLAG_SPBAPI, 1432 | "Failed to send SPB request %p - %!STATUS!", 1433 | SpbRequest, 1434 | status); 1435 | 1436 | NTSTATUS cancelStatus; 1437 | cancelStatus = WdfRequestUnmarkCancelable(ClientRequest); 1438 | 1439 | if (!NT_SUCCESS(cancelStatus)) 1440 | { 1441 | NT_ASSERTMSG("WdfRequestUnmarkCancelable should only fail if request has already been cancelled", 1442 | cancelStatus == STATUS_CANCELLED); 1443 | 1444 | Trace( 1445 | TRACE_LEVEL_INFORMATION, 1446 | TRACE_FLAG_SPBAPI, 1447 | "Client request %p has already been cancelled - " 1448 | "%!STATUS!", 1449 | ClientRequest, 1450 | cancelStatus); 1451 | } 1452 | } 1453 | } 1454 | 1455 | FuncExit(TRACE_FLAG_SPBAPI); 1456 | 1457 | return status; 1458 | } 1459 | 1460 | VOID 1461 | SpbPeripheralOnCompletion( 1462 | _In_ WDFREQUEST spbRequest, 1463 | _In_ WDFIOTARGET FxTarget, 1464 | _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, 1465 | _In_ WDFCONTEXT Context 1466 | ) 1467 | /*++ 1468 | 1469 | Routine Description: 1470 | 1471 | This routine is called when a request completes. 1472 | 1473 | Arguments: 1474 | 1475 | spbRequest - the framework request object 1476 | FxTarget - the framework IO target object 1477 | Params - a pointer to the request completion parameters 1478 | Context - the request context 1479 | 1480 | Return Value: 1481 | 1482 | None 1483 | 1484 | --*/ 1485 | { 1486 | FuncEntry(TRACE_FLAG_SPBAPI); 1487 | 1488 | UNREFERENCED_PARAMETER(FxTarget); 1489 | UNREFERENCED_PARAMETER(Context); 1490 | 1491 | PPBC_REQUEST pRequest; 1492 | PPBC_DEVICE pDevice; 1493 | NTSTATUS status; 1494 | NTSTATUS cancelStatus; 1495 | ULONG_PTR bytesCompleted; 1496 | 1497 | pRequest = GetRequestContext(spbRequest); 1498 | pDevice = GetDeviceContext(pRequest->FxDevice); 1499 | 1500 | status = Params->IoStatus.Status; 1501 | 1502 | Trace( 1503 | TRACE_LEVEL_INFORMATION, 1504 | TRACE_FLAG_SPBAPI, 1505 | "Completion callback received for SPB request %p with %!STATUS!", 1506 | spbRequest, 1507 | status); 1508 | 1509 | //if (NT_SUCCESS(status)) 1510 | //{ 1511 | // SPB_TRANSFER_DESCRIPTOR descriptor; 1512 | // SPB_TRANSFER_DIRECTION direction; 1513 | // PMDL pMdl; 1514 | // size_t transferLength; 1515 | // UCHAR buffer[8]; 1516 | // unsigned int i; 1517 | 1518 | // SPB_TRANSFER_DESCRIPTOR_INIT(&descriptor); 1519 | 1520 | // SpbRequestGetTransferParameters( 1521 | // pRequest->SpbRequest, 1522 | // 0, 1523 | // &descriptor, 1524 | // &pMdl); 1525 | 1526 | // NT_ASSERT(pMdl != NULL); 1527 | 1528 | // transferLength = descriptor.TransferLength; 1529 | // direction = descriptor.Direction; 1530 | 1531 | // for (i = 0; i < sizeof(buffer) / sizeof(buffer[0]); i++) 1532 | // { 1533 | // RequestGetByte(pMdl, transferLength, i, &buffer[i]); 1534 | // } 1535 | 1536 | // Trace( 1537 | // TRACE_LEVEL_ERROR, 1538 | // TRACE_FLAG_SPBAPI, 1539 | // "buffer %lu bytes: %02x %02x %02x %02x %02x %02x %02x %02x", 1540 | // (unsigned long)transferLength, 1541 | // buffer[0], 1542 | // buffer[1], 1543 | // buffer[2], 1544 | // buffer[3], 1545 | // buffer[4], 1546 | // buffer[5], 1547 | // buffer[6], 1548 | // buffer[7] 1549 | // ); 1550 | 1551 | //} 1552 | //if (NT_SUCCESS(status)) 1553 | //{ 1554 | // NTSTATUS dbg_status; 1555 | // PVOID pInputBuffer = nullptr; 1556 | // PVOID pOutputBuffer = nullptr; 1557 | // size_t inputBufferLength = 0; 1558 | // size_t outputBufferLength = 0; 1559 | 1560 | // dbg_status = WdfRequestRetrieveInputBuffer( 1561 | // spbRequest, 1562 | // 0, 1563 | // &pInputBuffer, 1564 | // &inputBufferLength); 1565 | 1566 | // if (NT_SUCCESS(dbg_status)) 1567 | // { 1568 | // Trace( 1569 | // TRACE_LEVEL_ERROR, 1570 | // TRACE_FLAG_SPBAPI, 1571 | // "Retrieved input buffer" 1572 | // ); 1573 | // } 1574 | 1575 | // status = WdfRequestRetrieveOutputBuffer( 1576 | // spbRequest, 1577 | // 0, 1578 | // &pOutputBuffer, 1579 | // &outputBufferLength); 1580 | 1581 | // if (NT_SUCCESS(dbg_status)) 1582 | // { 1583 | // Trace( 1584 | // TRACE_LEVEL_ERROR, 1585 | // TRACE_FLAG_SPBAPI, 1586 | // "Retrieved output buffer" 1587 | // ); 1588 | // } 1589 | //} 1590 | 1591 | // 1592 | // Unmark the client request as cancellable 1593 | // 1594 | 1595 | cancelStatus = WdfRequestUnmarkCancelable(pDevice->ClientRequest); 1596 | 1597 | if (!NT_SUCCESS(cancelStatus)) 1598 | { 1599 | NT_ASSERTMSG("WdfRequestUnmarkCancelable should only fail if request has already been cancelled", 1600 | cancelStatus == STATUS_CANCELLED); 1601 | 1602 | Trace( 1603 | TRACE_LEVEL_INFORMATION, 1604 | TRACE_FLAG_SPBAPI, 1605 | "Client request %p has already been cancelled - %!STATUS!", 1606 | pDevice->ClientRequest, 1607 | cancelStatus); 1608 | } 1609 | 1610 | // 1611 | // Complete the request pair 1612 | // 1613 | 1614 | bytesCompleted = Params->IoStatus.Information; 1615 | 1616 | SpbPeripheralCompleteRequestPair( 1617 | pDevice, 1618 | status, 1619 | bytesCompleted); 1620 | 1621 | FuncExit(TRACE_FLAG_SPBAPI); 1622 | } 1623 | 1624 | VOID 1625 | SpbPeripheralOnCancel( 1626 | _In_ WDFREQUEST spbRequest 1627 | ) 1628 | /*++ 1629 | Routine Description: 1630 | 1631 | This event is called when the client request is cancelled. 1632 | 1633 | Arguments: 1634 | 1635 | spbRequest - the framework request object 1636 | 1637 | Return Value: 1638 | 1639 | VOID 1640 | 1641 | --*/ 1642 | { 1643 | FuncEntry(TRACE_FLAG_SPBAPI); 1644 | 1645 | PPBC_REQUEST pRequest; 1646 | PPBC_DEVICE pDevice; 1647 | 1648 | pRequest = GetRequestContext(spbRequest); 1649 | pDevice = GetDeviceContext(pRequest->FxDevice); 1650 | 1651 | // 1652 | // Attempt to cancel the SPB request 1653 | // 1654 | 1655 | Trace( 1656 | TRACE_LEVEL_INFORMATION, 1657 | TRACE_FLAG_SPBAPI, 1658 | "Cancel received for client request %p, " 1659 | "attempting to cancel SPB request %p", 1660 | spbRequest, 1661 | pDevice->SpbRequest); 1662 | 1663 | WdfRequestCancelSentRequest(pDevice->SpbRequest); 1664 | 1665 | FuncExit(TRACE_FLAG_SPBAPI); 1666 | } 1667 | 1668 | VOID 1669 | SpbPeripheralCompleteRequestPair( 1670 | _In_ PPBC_DEVICE pDevice, 1671 | _In_ NTSTATUS status, 1672 | _In_ ULONG_PTR bytesCompleted 1673 | ) 1674 | /*++ 1675 | Routine Description: 1676 | 1677 | This routine marks the SpbRequest as reuse 1678 | and completes the client request. 1679 | 1680 | Arguments: 1681 | 1682 | pDevice - the device context 1683 | status - the client completion status 1684 | bytesCompleted - the number of bytes completed 1685 | for the client request 1686 | 1687 | Return Value: 1688 | 1689 | VOID 1690 | 1691 | --*/ 1692 | { 1693 | FuncEntry(TRACE_FLAG_SPBAPI); 1694 | 1695 | PPBC_REQUEST pRequest; 1696 | pRequest = GetRequestContext(pDevice->SpbRequest); 1697 | 1698 | Trace( 1699 | TRACE_LEVEL_INFORMATION, 1700 | TRACE_FLAG_SPBAPI, 1701 | "Marking SPB request %p for reuse, and completing " 1702 | "client request %p with %!STATUS! and bytes=%lu", 1703 | pDevice->SpbRequest, 1704 | pDevice->ClientRequest, 1705 | status, 1706 | (ULONG)bytesCompleted); 1707 | 1708 | // 1709 | // Mark the SPB request as reuse 1710 | // 1711 | 1712 | WDF_REQUEST_REUSE_PARAMS params; 1713 | WDF_REQUEST_REUSE_PARAMS_INIT( 1714 | ¶ms, 1715 | WDF_REQUEST_REUSE_NO_FLAGS, 1716 | STATUS_SUCCESS); 1717 | 1718 | WdfRequestReuse(pDevice->SpbRequest, ¶ms); 1719 | 1720 | if (pDevice->InputMemory != WDF_NO_HANDLE) 1721 | { 1722 | WdfObjectDelete(pDevice->InputMemory); 1723 | pDevice->InputMemory = WDF_NO_HANDLE; 1724 | } 1725 | 1726 | // 1727 | // Complete the client request 1728 | // 1729 | 1730 | if (pDevice->ClientRequest != nullptr) 1731 | { 1732 | SPBREQUEST clientRequest = pDevice->ClientRequest; 1733 | pDevice->ClientRequest = nullptr; 1734 | 1735 | SpbTraceBuffers(pDevice, clientRequest); 1736 | 1737 | // In order to satisfy SDV, assume clientRequest 1738 | // is equal to pDevice->ClientRequest. This suppresses 1739 | // a warning in the driver's cancellation path. 1740 | // 1741 | // Typically when WdfRequestUnmarkCancelable returns 1742 | // STATUS_CANCELLED a driver does not go on to complete 1743 | // the request in that context. This sample, however, 1744 | // driver has handled this condition appropriately by 1745 | // not completing the cancelled request in its 1746 | // EvtRequestCancel callback. Developers should be 1747 | // cautious when copying code from this sample, paying 1748 | // close attention to the cancellation logic. 1749 | // 1750 | _Analysis_assume_(clientRequest == pDevice->ClientRequest); 1751 | 1752 | WdfRequestCompleteWithInformation( 1753 | clientRequest, 1754 | status, 1755 | bytesCompleted); 1756 | } 1757 | 1758 | FuncExit(TRACE_FLAG_SPBAPI); 1759 | } 1760 | -------------------------------------------------------------------------------- /peripheral.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | peripheral.h 8 | 9 | Abstract: 10 | 11 | This module contains the function definitions for 12 | interaction with the SPB API. 13 | 14 | Environment: 15 | 16 | kernel-mode only 17 | 18 | Revision History: 19 | 20 | --*/ 21 | 22 | #ifndef _PERIPHERAL_H_ 23 | #define _PERIPHERAL_H_ 24 | 25 | EVT_WDF_REQUEST_COMPLETION_ROUTINE SpbPeripheralOnCompletion; 26 | EVT_WDF_REQUEST_CANCEL SpbPeripheralOnCancel; 27 | 28 | EVT_WDF_REQUEST_CANCEL SpbPeripheralOnWaitOnInterruptCancel; 29 | 30 | NTSTATUS 31 | SpbPeripheralOpen( 32 | _In_ PPBC_DEVICE pDevice); 33 | 34 | NTSTATUS 35 | SpbPeripheralClose( 36 | _In_ PPBC_DEVICE pDevice); 37 | 38 | VOID 39 | SpbPeripheralLock( 40 | _In_ PPBC_DEVICE pDevice, 41 | _In_ SPBREQUEST spbRequest); 42 | 43 | VOID 44 | SpbPeripheralUnlock( 45 | _In_ PPBC_DEVICE pDevice, 46 | _In_ SPBREQUEST spbRequest); 47 | 48 | VOID 49 | SpbPeripheralLockConnection( 50 | _In_ PPBC_DEVICE pDevice, 51 | _In_ SPBREQUEST spbRequest); 52 | 53 | VOID 54 | SpbPeripheralUnlockConnection( 55 | _In_ PPBC_DEVICE pDevice, 56 | _In_ SPBREQUEST spbRequest); 57 | 58 | VOID 59 | SpbPeripheralRead( 60 | _In_ PPBC_DEVICE pDevice, 61 | _In_ SPBREQUEST spbRequest, 62 | _In_ BOOLEAN FullDuplex); 63 | 64 | VOID 65 | SpbPeripheralWrite( 66 | _In_ PPBC_DEVICE pDevice, 67 | _In_ SPBREQUEST spbRequest, 68 | _In_ BOOLEAN FullDuplex); 69 | 70 | VOID 71 | SpbPeripheralFullDuplex( 72 | _In_ PPBC_DEVICE pDevice, 73 | _In_ SPBREQUEST spbRequest); 74 | 75 | VOID 76 | SpbPeripheralSequence( 77 | _In_ PPBC_DEVICE pDevice, 78 | _In_ SPBREQUEST spbRequest, 79 | _In_ ULONG TransferCount); 80 | 81 | NTSTATUS 82 | SpbPeripheralSequence1( 83 | _In_ PPBC_DEVICE pDevice, 84 | _In_ SPBREQUEST spbRequest); 85 | 86 | NTSTATUS 87 | SpbPeripheralSequence2( 88 | _In_ PPBC_DEVICE pDevice, 89 | _In_ SPBREQUEST spbRequest); 90 | 91 | NTSTATUS 92 | SpbPeripheralSendRequest( 93 | _In_ PPBC_DEVICE pDevice, 94 | _In_ WDFREQUEST SpbRequest, 95 | _In_ WDFREQUEST ClientRequest); 96 | 97 | VOID 98 | SpbPeripheralCompleteRequestPair( 99 | _In_ PPBC_DEVICE pDevice, 100 | _In_ NTSTATUS status, 101 | _In_ ULONG_PTR bytesCompleted); 102 | 103 | NTSTATUS 104 | FORCEINLINE 105 | RequestGetByte( 106 | _In_ PMDL mdl, 107 | _In_ size_t mdlLength, 108 | _In_ size_t Index, 109 | _Out_ UCHAR* pByte 110 | ) 111 | /*++ 112 | 113 | Routine Description: 114 | 115 | This is a helper routine used to retrieve the 116 | specified byte of the current transfer descriptor buffer. 117 | 118 | Arguments: 119 | 120 | pRequest - a pointer to the PBC request context 121 | 122 | Index - index of desired byte in current transfer descriptor buffer 123 | 124 | pByte - pointer to the location for the specified byte 125 | 126 | Return Value: 127 | 128 | STATUS_INFO_LENGTH_MISMATCH if invalid index, 129 | otherwise STATUS_SUCCESS 130 | 131 | --*/ 132 | { 133 | size_t mdlByteCount; 134 | size_t currentOffset = Index; 135 | PUCHAR pBuffer; 136 | NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH; 137 | 138 | // 139 | // Check for out-of-bounds index 140 | // 141 | 142 | if (Index < mdlLength) 143 | { 144 | while (mdl != NULL) 145 | { 146 | mdlByteCount = MmGetMdlByteCount(mdl); 147 | 148 | if (currentOffset < mdlByteCount) 149 | { 150 | pBuffer = (PUCHAR)MmGetSystemAddressForMdlSafe( 151 | mdl, 152 | NormalPagePriority | MdlMappingNoExecute); 153 | 154 | if (pBuffer != NULL) 155 | { 156 | // 157 | // Byte found, mark successful 158 | // 159 | 160 | *pByte = pBuffer[currentOffset]; 161 | status = STATUS_SUCCESS; 162 | } 163 | 164 | break; 165 | } 166 | 167 | currentOffset -= mdlByteCount; 168 | mdl = mdl->Next; 169 | } 170 | 171 | // 172 | // If after walking the MDL the byte hasn't been found, 173 | // status will still be STATUS_INFO_LENGTH_MISMATCH 174 | // 175 | } 176 | 177 | return status; 178 | } 179 | 180 | #endif // _PERIPHERAL_H_ 181 | -------------------------------------------------------------------------------- /resource.rc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define VER_FILETYPE VFT_DRV 6 | #define VER_FILESUBTYPE VFT2_DRV_SYSTEM 7 | #define VER_FILEDESCRIPTION_STR "Skeleton I2C Controller Driver" 8 | #define VER_INTERNALNAME_STR "skeletoni2c.sys" 9 | #define VER_ORIGINALFILENAME_STR "skeletoni2c.sys" 10 | 11 | #include "common.ver" 12 | -------------------------------------------------------------------------------- /spbProbe.asl: -------------------------------------------------------------------------------- 1 | // 2 | // Test controller device node. 3 | // 4 | // For a peripheral driver to access this controller 5 | // via SPB it must specify the ACPI device path within 6 | // the I2CSerialBus (or SPISerialBus) macro. Depending 7 | // on the scope this looks something like \_SB.I2C. See 8 | // spbtesttool.asl for an example. 9 | // 10 | Device(I2C) 11 | { 12 | Name(_HID, "PROBE01") 13 | Name(_UID, 1) 14 | Method(_CRS, 0x0, NotSerialized) 15 | { 16 | Name (RBUF, ResourceTemplate () 17 | { 18 | // 19 | // Sample I2C and GPIO resources. Modify to match your 20 | // platform's underlying controllers and connections. 21 | // \_SB.I2C and \_SB.GPIO are paths to predefined I2C. 22 | // 23 | I2CSerialBus(0x1D, ControllerInitiated, 400000, AddressingMode7Bit, "\\_SB.I2C", , ) 24 | //SpiSerialBus (0x0001, PolarityLow, FourWireMode, 0x10, 25 | // ControllerInitiated, 0x007A1200, ClockPolarityLow, 26 | // ClockPhaseFirst, "\\_SB.SP1", 27 | // 0x00, ResourceConsumer, ,) 28 | }) 29 | Return(RBUF) 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /spbProbe.inx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bentiss/SimplePeripheralBusProbe/c2ccaaa192d9731e4c4b4c1f00ad121030442729/spbProbe.inx -------------------------------------------------------------------------------- /spbProbe.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {8C1BB5BA-283E-460F-A682-4548A1DAFA59} 23 | $(MSBuildProjectName) 24 | 1 25 | Debug 26 | Win32 27 | {666F5885-C0E7-43DD-AD99-B596AC486945} 28 | spbProbe 29 | $(LatestTargetPlatformVersion) 30 | 31 | 32 | 33 | Windows10 34 | False 35 | Universal 36 | KMDF 37 | WindowsKernelModeDriver10.0 38 | Driver 39 | 40 | 41 | Windows10 42 | True 43 | Universal 44 | KMDF 45 | WindowsKernelModeDriver10.0 46 | Driver 47 | 48 | 49 | Windows10 50 | False 51 | Universal 52 | KMDF 53 | WindowsKernelModeDriver10.0 54 | Driver 55 | 56 | 57 | Windows10 58 | True 59 | Universal 60 | KMDF 61 | WindowsKernelModeDriver10.0 62 | Driver 63 | 64 | 65 | 66 | $(IntDir) 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | true 83 | true 84 | i2ctrace.h 85 | Trace(LEVEL,FLAGS,MSG,...) 86 | 87 | 88 | true 89 | true 90 | i2ctrace.h 91 | Trace(LEVEL,FLAGS,MSG,...) 92 | 93 | 94 | true 95 | true 96 | i2ctrace.h 97 | Trace(LEVEL,FLAGS,MSG,...) 98 | 99 | 100 | $(InfArch) 101 | true 102 | .\$(IntDir)\spbProbe.inf 103 | 104 | 105 | true 106 | true 107 | i2ctrace.h 108 | Trace(LEVEL,FLAGS,MSG,...) 109 | 110 | 111 | 112 | spbProbe 113 | 114 | 115 | spbProbe 116 | 117 | 118 | spbProbe 119 | 120 | 121 | spbProbe 122 | 123 | 124 | 125 | true 126 | Level4 127 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 128 | 129 | 130 | 131 | 132 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 133 | 134 | 135 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 136 | 137 | 138 | %(AdditionalDependencies);$(SPB_LIB_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)\SpbCxStubs.lib;$(DDK_LIB_PATH)\ntstrsafe.lib 139 | 140 | 141 | 142 | 143 | true 144 | Level4 145 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 146 | 147 | 148 | 149 | 150 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 151 | 152 | 153 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 154 | 155 | 156 | %(AdditionalDependencies);$(SPB_LIB_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)\SpbCxStubs.lib;$(DDK_LIB_PATH)\ntstrsafe.lib 157 | 158 | 159 | 160 | 161 | true 162 | Level4 163 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 164 | 165 | 166 | 167 | 168 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 169 | 170 | 171 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 172 | 173 | 174 | %(AdditionalDependencies);$(SPB_LIB_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)\SpbCxStubs.lib;$(DDK_LIB_PATH)\ntstrsafe.lib 175 | 176 | 177 | 178 | 179 | true 180 | Level4 181 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 182 | 183 | 184 | 185 | 186 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 187 | 188 | 189 | %(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) 190 | 191 | 192 | %(AdditionalDependencies);$(SPB_LIB_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)\SpbCxStubs.lib;$(DDK_LIB_PATH)\ntstrsafe.lib 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /spbProbe.vcxproj.Filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Source 6 | 7 | 8 | Source 9 | 10 | 11 | Source 12 | 13 | 14 | 15 | 16 | Headers 17 | 18 | 19 | Headers 20 | 21 | 22 | Headers 23 | 24 | 25 | Headers 26 | 27 | 28 | Headers 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {a4e5baeb-c3a4-4fb0-9574-f6ed6050c40f} 39 | 40 | 41 | {1a5224ae-77a3-4328-baa0-d786457e205f} 42 | 43 | 44 | {08f2de04-4d18-4db5-88e8-a80be66d09bb} 45 | 46 | 47 | {2571e45b-ba42-48ea-9034-1ed25fabcb9f} 48 | 49 | 50 | 51 | 52 | Inf 53 | 54 | 55 | 56 | 57 | Ressources 58 | 59 | 60 | --------------------------------------------------------------------------------