├── .gitattributes ├── .gitignore ├── DioTest ├── DioTest.cpp ├── DioTest.vcxproj └── DioTest.vcxproj.filters ├── OsrDio.sln ├── OsrDio.sln.DotSettings ├── README.md ├── docs ├── PCIe-6509_Register Interface_373665a.pdf └── PCIe_6509_User_Manual_372117d.pdf ├── inc └── OsrDio_IOCTL.h └── src ├── OsrDio.cpp ├── OsrDio.h ├── OsrDio.inf ├── OsrDio.vcxproj └── OsrDio.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 | # and except our handy tracy lib 156 | !**/Packages/Microsoft.Diagnostics.Tracing.TraceEvent.dll 157 | # Uncomment if necessary however generally it will be regenerated when needed 158 | #!**/packages/repositories.config 159 | # NuGet v3's project.json files produces more ignoreable files 160 | *.nuget.props 161 | *.nuget.targets 162 | 163 | # Microsoft Azure Build Output 164 | csx/ 165 | *.build.csdef 166 | 167 | # Microsoft Azure Emulator 168 | ecf/ 169 | rcf/ 170 | 171 | # Windows Store app package directory 172 | AppPackages/ 173 | BundleArtifacts/ 174 | 175 | # Visual Studio cache files 176 | # files ending in .cache can be ignored 177 | *.[Cc]ache 178 | # but keep track of directories ending in .cache 179 | !*.[Cc]ache/ 180 | 181 | # Others 182 | ClientBin/ 183 | [Ss]tyle[Cc]op.* 184 | ~$* 185 | *~ 186 | *.dbmdl 187 | *.dbproj.schemaview 188 | *.pfx 189 | *.publishsettings 190 | node_modules/ 191 | orleans.codegen.cs 192 | 193 | # RIA/Silverlight projects 194 | Generated_Code/ 195 | 196 | # Backup & report files from converting an old project file 197 | # to a newer Visual Studio version. Backup files are not needed, 198 | # because we have git ;-) 199 | _UpgradeReport_Files/ 200 | Backup*/ 201 | UpgradeLog*.XML 202 | UpgradeLog*.htm 203 | 204 | # SQL Server files 205 | *.mdf 206 | *.ldf 207 | 208 | # Business Intelligence projects 209 | *.rdl.data 210 | *.bim.layout 211 | *.bim_*.settings 212 | 213 | # Microsoft Fakes 214 | FakesAssemblies/ 215 | 216 | # GhostDoc plugin setting file 217 | *.GhostDoc.xml 218 | 219 | # Node.js Tools for Visual Studio 220 | .ntvs_analysis.dat 221 | 222 | # Visual Studio 6 build log 223 | *.plg 224 | 225 | # Visual Studio 6 workspace options file 226 | *.opt 227 | 228 | # Visual Studio LightSwitch build output 229 | **/*.HTMLClient/GeneratedArtifacts 230 | **/*.DesktopClient/GeneratedArtifacts 231 | **/*.DesktopClient/ModelManifest.xml 232 | **/*.Server/GeneratedArtifacts 233 | **/*.Server/ModelManifest.xml 234 | _Pvt_Extensions 235 | 236 | # LightSwitch generated files 237 | GeneratedArtifacts/ 238 | ModelManifest.xml 239 | 240 | # Paket dependency manager 241 | .paket/paket.exe 242 | 243 | # FAKE - F# Make 244 | .fake/ 245 | /*/sdv* 246 | SDV-default.xml 247 | build.err 248 | runsdvui.cmd 249 | sdv-map.h 250 | sdv-user.sdv 251 | *PVS-Studio* 252 | 253 | *.txt 254 | -------------------------------------------------------------------------------- /DioTest/DioTest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DIOTEST.CPP 3 | // 4 | // Test utility for the OSRDIO driver, which was created for our WDF seminar. 5 | // 6 | // This code is purely functional, and is definitely not designed to be any 7 | // sort of example. 8 | // 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include "..\inc\OsrDio_IOCTL.h" 16 | 17 | 18 | HANDLE 19 | OpenHandleByGUID() 20 | { 21 | CONFIGRET configReturn; 22 | DWORD lasterror; 23 | WCHAR deviceName[MAX_DEVICE_ID_LEN]; 24 | HANDLE handleToReturn = INVALID_HANDLE_VALUE; 25 | 26 | // 27 | // Get the device interface -- we only expose one 28 | // 29 | deviceName[0] = UNICODE_NULL; 30 | 31 | configReturn = CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_OSRDIO, 32 | nullptr, 33 | deviceName, 34 | _countof(deviceName), 35 | CM_GET_DEVICE_INTERFACE_LIST_PRESENT); 36 | if (configReturn != CR_SUCCESS) { 37 | lasterror = GetLastError(); 38 | 39 | printf("CM_Get_Device_Interface_List fail: %lx\n", 40 | lasterror); 41 | 42 | goto Exit; 43 | } 44 | 45 | // 46 | // Make sure there's an actual name there 47 | // 48 | if (deviceName[0] == UNICODE_NULL) { 49 | lasterror = ERROR_NOT_FOUND; 50 | goto Exit; 51 | } 52 | 53 | // 54 | // Open the device 55 | // 56 | handleToReturn = CreateFile(deviceName, 57 | GENERIC_WRITE | GENERIC_READ, 58 | 0, 59 | nullptr, 60 | OPEN_EXISTING, 61 | 0, 62 | nullptr); 63 | 64 | if (handleToReturn == INVALID_HANDLE_VALUE) { 65 | lasterror = GetLastError(); 66 | printf("CreateFile fail: %lx\n", 67 | lasterror); 68 | } 69 | 70 | Exit: 71 | 72 | // 73 | // Return a handle to the device 74 | // 75 | return handleToReturn; 76 | } 77 | 78 | HANDLE 79 | OpenHandle() 80 | { 81 | HANDLE handle; 82 | DWORD lastError; 83 | 84 | // 85 | // Open the device by name 86 | // 87 | handle = CreateFile(LR"(\\.\OSRDIO)", 88 | GENERIC_READ | GENERIC_WRITE, 89 | 0, 90 | nullptr, 91 | OPEN_EXISTING, 92 | 0, 93 | nullptr); 94 | // 95 | // If this call fails, check to figure out what the error is and report it. 96 | // 97 | if (handle == INVALID_HANDLE_VALUE) { 98 | 99 | lastError = GetLastError(); 100 | 101 | printf("CreateFile failed with error 0x%lx\n", 102 | lastError); 103 | 104 | } 105 | 106 | return handle; 107 | } 108 | 109 | void 110 | AwaitCOSFunction() 111 | { 112 | HANDLE awaitHandle; 113 | DWORD bytesRead; 114 | DWORD lastError; 115 | OSRDIO_CHANGE_DATA newLineState; 116 | 117 | awaitHandle = OpenHandle(); 118 | 119 | if (awaitHandle == INVALID_HANDLE_VALUE) { 120 | 121 | printf("\n\t\t\t\t****ERROR: CreateFile for await thread failed!\n"); 122 | 123 | return; 124 | } 125 | 126 | printf("\n\t\t\t\tAwaiting line state change...\n"); 127 | 128 | if (!DeviceIoControl(awaitHandle, 129 | IOCTL_OSRDIO_WAITFOR_CHANGE, 130 | nullptr, 131 | 0, 132 | &newLineState, 133 | sizeof(OSRDIO_CHANGE_DATA), 134 | &bytesRead, 135 | nullptr)) { 136 | 137 | lastError = GetLastError(); 138 | 139 | printf("\nDeviceIoControl IOCTL_OSRDIO_READ failed with error 0x%lx\n", 140 | lastError); 141 | 142 | printf("\nAwait thread EXITING WITH ERROR\n"); 143 | goto done; 144 | 145 | } 146 | 147 | printf("\n\n\t\t\t\tAwait thread: Change Of State Detected!\n"); 148 | printf("\t\t\t\tLatched Line State @ COS = 0x%08lx\n", 149 | newLineState.LatchedLineState); 150 | 151 | done: 152 | CloseHandle(awaitHandle); 153 | } 154 | 155 | int 156 | main(int argc, 157 | char* argv[]) 158 | { 159 | HANDLE deviceHandle; 160 | DWORD lastErrorStatus; 161 | DWORD bytesWritten; 162 | DWORD bytesRead; 163 | DWORD operation = 0; 164 | char inputBuffer[100]; 165 | 166 | printf("DIOTEST -- OSRDIO Test Utility V1.2\n"); 167 | 168 | if (argc != 1) { 169 | 170 | printf("opening by GUID\n"); 171 | deviceHandle = OpenHandleByGUID(); 172 | 173 | } else { 174 | 175 | deviceHandle = OpenHandle(); 176 | 177 | } 178 | 179 | 180 | if (deviceHandle == INVALID_HANDLE_VALUE) { 181 | exit(0); 182 | } 183 | 184 | 185 | while (TRUE) { 186 | char* endPointer = nullptr; 187 | 188 | while (endPointer != inputBuffer + 1) { 189 | 190 | printf("\n\nChoose from the following:\n"); 191 | printf("\t 1. Read current DIO line state\n"); 192 | printf("\t 2. Set output mask\n"); 193 | printf("\t 3. Set lines to assert\n"); 194 | printf("\t 4. Register COS notify\n"); 195 | printf("\t Enter zero to exit\n"); 196 | 197 | printf("\nEnter operation to perform: "); 198 | 199 | fgets(inputBuffer, 200 | sizeof(inputBuffer), 201 | stdin); 202 | 203 | operation = strtoul(inputBuffer, 204 | &endPointer, 205 | 10); 206 | } 207 | 208 | switch (operation) { 209 | 210 | case 0: { 211 | exit(EXIT_SUCCESS); 212 | } 213 | 214 | case 1: { 215 | OSRDIO_READ_DATA readDataBuffer; 216 | 217 | if (!DeviceIoControl(deviceHandle, 218 | IOCTL_OSRDIO_READ, 219 | nullptr, 220 | 0, 221 | &readDataBuffer, 222 | sizeof(OSRDIO_READ_DATA), 223 | &bytesRead, 224 | nullptr)) { 225 | 226 | lastErrorStatus = GetLastError(); 227 | 228 | printf("DeviceIoControl IOCTL_OSRDIO_READ failed with error 0x%lx\n", 229 | lastErrorStatus); 230 | 231 | exit(lastErrorStatus); 232 | } 233 | 234 | printf("Bytes read = %lu\n", 235 | bytesRead); 236 | printf("Input Line State = 0x%08lx", 237 | readDataBuffer.CurrentLineState); 238 | 239 | break; 240 | } 241 | 242 | 243 | case 2: { 244 | OSRDIO_SET_OUTPUTS_DATA outputsDataBuffer; 245 | DWORD desiredOutputMask; 246 | 247 | printf("Enter desired output mask (hex): "); 248 | 249 | char* result = fgets(inputBuffer, 250 | sizeof(inputBuffer), 251 | stdin); 252 | 253 | if (result != nullptr) { 254 | 255 | desiredOutputMask = strtoul(inputBuffer, 256 | nullptr, 257 | 16); 258 | 259 | printf("Desired output mask is 0x%08lx\n", 260 | desiredOutputMask); 261 | 262 | outputsDataBuffer.OutputLines = desiredOutputMask; 263 | 264 | if (!DeviceIoControl(deviceHandle, 265 | IOCTL_OSRDIO_SET_OUTPUTS, 266 | &outputsDataBuffer, 267 | sizeof(OSRDIO_SET_OUTPUTS_DATA), 268 | nullptr, 269 | 0, 270 | &bytesWritten, 271 | nullptr)) { 272 | 273 | lastErrorStatus = GetLastError(); 274 | 275 | printf("DeviceIoControl IOCTL_OSRDIO_SET_OUTPUTS failed with error 0x%lx\n", 276 | lastErrorStatus); 277 | 278 | exit(lastErrorStatus); 279 | } 280 | 281 | printf("Bytes written = %lu\n", 282 | bytesWritten); 283 | } 284 | 285 | break; 286 | } 287 | 288 | case 3: { 289 | OSRDIO_WRITE_DATA writeDataBuffer; 290 | DWORD linesToAssert; 291 | 292 | printf("ASSERT Lines: Remember output mask will be applied.\n"); 293 | printf("Enter bitmask of lines to assert (hex): "); 294 | 295 | char* result = fgets(inputBuffer, 296 | sizeof(inputBuffer), 297 | stdin); 298 | 299 | if (result != nullptr) { 300 | 301 | linesToAssert = strtoul(inputBuffer, 302 | nullptr, 303 | 16); 304 | printf("Mask of lines to assert is 0x%08lx\n", 305 | linesToAssert); 306 | 307 | writeDataBuffer.OutputLineState = linesToAssert; 308 | 309 | if (!DeviceIoControl(deviceHandle, 310 | IOCTL_OSRDIO_WRITE, 311 | &writeDataBuffer, 312 | sizeof(OSRDIO_WRITE_DATA), 313 | nullptr, 314 | 0, 315 | &bytesWritten, 316 | nullptr)) { 317 | 318 | lastErrorStatus = GetLastError(); 319 | 320 | printf("DeviceIoControl IOCTL_OSRDIO_WRITE failed with error 0x%lx\n", 321 | lastErrorStatus); 322 | 323 | exit(lastErrorStatus); 324 | } 325 | 326 | printf("Bytes written = %lu\n", 327 | bytesWritten); 328 | } 329 | break; 330 | } 331 | 332 | 333 | case 4: { 334 | 335 | // 336 | // Fire-up the COS thread 337 | // 338 | std::thread ChangeOfStateThread(AwaitCOSFunction); 339 | 340 | ChangeOfStateThread.detach(); 341 | 342 | break; 343 | } 344 | default: { 345 | 346 | break; 347 | } 348 | } 349 | 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /DioTest/DioTest.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 | 16.0 23 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8} 24 | Win32Proj 25 | DioTest 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | false 76 | 77 | 78 | true 79 | false 80 | 81 | 82 | false 83 | false 84 | 85 | 86 | false 87 | false 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | true 95 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | true 97 | MultiThreadedDebug 98 | 99 | 100 | Console 101 | true 102 | cfgmgr32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 103 | 104 | 105 | 106 | 107 | 108 | 109 | Level3 110 | true 111 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 112 | true 113 | MultiThreadedDebug 114 | 115 | 116 | Console 117 | true 118 | cfgmgr32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 119 | 120 | 121 | 122 | 123 | 124 | 125 | Level3 126 | true 127 | true 128 | true 129 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 130 | true 131 | MultiThreaded 132 | 133 | 134 | Console 135 | true 136 | true 137 | true 138 | cfgmgr32.lib;%(AdditionalDependencies) 139 | 140 | 141 | 142 | 143 | 144 | 145 | Level3 146 | true 147 | true 148 | true 149 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 150 | true 151 | MultiThreaded 152 | 153 | 154 | Console 155 | true 156 | true 157 | true 158 | cfgmgr32.lib;%(AdditionalDependencies) 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /DioTest/DioTest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /OsrDio.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30104.148 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OsrDio", "src\OsrDio.vcxproj", "{902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DioTest", "DioTest\DioTest.vcxproj", "{4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|ARM = Debug|ARM 13 | Debug|ARM64 = Debug|ARM64 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Release|ARM = Release|ARM 17 | Release|ARM64 = Release|ARM64 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|ARM.ActiveCfg = Debug|ARM 23 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|ARM.Build.0 = Debug|ARM 24 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|ARM.Deploy.0 = Debug|ARM 25 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|ARM64.ActiveCfg = Debug|ARM64 26 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|ARM64.Build.0 = Debug|ARM64 27 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|ARM64.Deploy.0 = Debug|ARM64 28 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|x64.ActiveCfg = Debug|x64 29 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|x64.Build.0 = Debug|x64 30 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|x64.Deploy.0 = Debug|x64 31 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|x86.ActiveCfg = Debug|Win32 32 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|x86.Build.0 = Debug|Win32 33 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Debug|x86.Deploy.0 = Debug|Win32 34 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|ARM.ActiveCfg = Release|ARM 35 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|ARM.Build.0 = Release|ARM 36 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|ARM.Deploy.0 = Release|ARM 37 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|ARM64.ActiveCfg = Release|ARM64 38 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|ARM64.Build.0 = Release|ARM64 39 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|ARM64.Deploy.0 = Release|ARM64 40 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|x64.ActiveCfg = Release|x64 41 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|x64.Build.0 = Release|x64 42 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|x64.Deploy.0 = Release|x64 43 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|x86.ActiveCfg = Release|Win32 44 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|x86.Build.0 = Release|Win32 45 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC}.Release|x86.Deploy.0 = Release|Win32 46 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Debug|ARM.ActiveCfg = Debug|Win32 47 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Debug|ARM64.ActiveCfg = Debug|Win32 48 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Debug|x64.ActiveCfg = Debug|x64 49 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Debug|x64.Build.0 = Debug|x64 50 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Debug|x86.ActiveCfg = Debug|Win32 51 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Debug|x86.Build.0 = Debug|Win32 52 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Release|ARM.ActiveCfg = Release|Win32 53 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Release|ARM64.ActiveCfg = Release|Win32 54 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Release|x64.ActiveCfg = Release|x64 55 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Release|x64.Build.0 = Release|x64 56 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Release|x86.ActiveCfg = Release|Win32 57 | {4A1F6A76-A5F3-4061-AED9-AB162AFADDC8}.Release|x86.Build.0 = Release|Win32 58 | EndGlobalSection 59 | GlobalSection(SolutionProperties) = preSolution 60 | HideSolutionNode = FALSE 61 | EndGlobalSection 62 | GlobalSection(ExtensibilityGlobals) = postSolution 63 | SolutionGuid = {2D3FA1B3-7DBE-4E92-B725-2AFA3E1C70FC} 64 | EndGlobalSection 65 | EndGlobal 66 | -------------------------------------------------------------------------------- /OsrDio.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DI 3 | DIO 4 | FE 5 | RE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OSRDIO 2 | This is a simple KMDF driver that we use as one of the first case studies in our [Writing WDF Drivers seminar](https://www.osr.com/seminars/wdf-drivers/). 3 | The driver supports a subset of the features of the National Instruments [PCIe-6509 Digital I/O Device](https://www.ni.com/en-us/support/model.pcie-6509.html). 4 | Please see the code for more descriptive information and for specific license information. 5 | -------------------------------------------------------------------------------- /docs/PCIe-6509_Register Interface_373665a.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OSRDrivers/OSRDIO/24a72ac50e929cc8834e391e2608dac79ca5e092/docs/PCIe-6509_Register Interface_373665a.pdf -------------------------------------------------------------------------------- /docs/PCIe_6509_User_Manual_372117d.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OSRDrivers/OSRDIO/24a72ac50e929cc8834e391e2608dac79ca5e092/docs/PCIe_6509_User_Manual_372117d.pdf -------------------------------------------------------------------------------- /inc/OsrDio_IOCTL.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright 2020 OSR Open Systems Resources, Inc. 4 | // All Rights Reserved 5 | // 6 | // This software is supplied for instructional purposes only. 7 | // 8 | // OSR Open Systems Resources, Inc. (OSR) expressly disclaims any warranty 9 | // for this software. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY 10 | // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, 11 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR 12 | // PURPOSE. THE ENTIRE RISK ARISING FROM THE USE OF THIS SOFTWARE REMAINS 13 | // WITH YOU. OSR's entire liability and your exclusive remedy shall not 14 | // exceed the price paid for this material. In no event shall OSR or its 15 | // suppliers be liable for any damages whatsoever (including, without 16 | // limitation, damages for loss of business profit, business interruption, 17 | // loss of business information, or any other pecuniary loss) arising out 18 | // of the use or inability to use this software, even if OSR has been 19 | // advised of the possibility of such damages. Because some states/ 20 | // jurisdictions do not allow the exclusion or limitation of liability for 21 | // consequential or incidental damages, the above limitation may not apply 22 | // to you. 23 | // 24 | // OSR Open Systems Resources, Inc. 25 | // 889 Elm St, Sixth Floor 26 | // Manchester, NH 03101 27 | // email bugs to: bugs@osr.com 28 | // 29 | // 30 | // MODULE: 31 | // 32 | // OsrDio_IOCTL.h -- Definitions shared between OsrDio driver and 33 | // applications. 34 | // 35 | // AUTHOR(S): 36 | // 37 | // OSR Open Systems Resources, Inc. 38 | // 39 | /////////////////////////////////////////////////////////////////////////////// 40 | // ReSharper disable CppClangTidyCppcoreguidelinesMacroUsage 41 | #pragma once 42 | 43 | // 44 | // This header file contains all declarations shared between driver and user 45 | // applications. 46 | // 47 | 48 | #include 49 | 50 | // 51 | // Device Interface GUID for OsrDio 52 | // 53 | // {CCF57245-9C4E-4C71-AC65-5217B37847D3} 54 | DEFINE_GUID(GUID_DEVINTERFACE_OSRDIO, 55 | 0xccf57245, 0x9c4e, 0x4c71, 0xac, 0x65, 0x52, 0x17, 0xb3, 0x78, 0x47, 0xd3); 56 | 57 | // 58 | // The following value is arbitrarily chosen from the space defined by 59 | // Microsoft as being "for non-Microsoft use" (0x8000 through 0xFFFF) 60 | // 61 | #define FILE_DEVICE_OSRDIO 0xD056 62 | 63 | // 64 | // Device control codes - Values between 2048 and 4095 arbitrarily chosen 65 | // 66 | 67 | // 68 | // IOCTL_OSRDIO_READ 69 | // 70 | // Retrieves a bitmap of the current state of both the DIO input and output 71 | // lines. 72 | // 73 | // Input Buffer: 74 | // (none) 75 | // 76 | // Output Buffer: 77 | // 78 | // OSRDIO_READ_DATA structure. The CurrentLineState field contains a 79 | // bitmap indciating the current state of all the DIO lines. A 1 bit 80 | // indicates the corresponding line is ASSERTED, a 0 bit indicates 81 | // that the corresponding line is DEASSERTED. The state of both input 82 | // and output lines is returned by this IOCTL. 83 | // 84 | typedef struct _OSRDIO_READ_DATA { 85 | ULONG CurrentLineState; 86 | } OSRDIO_READ_DATA, *POSRDIO_READ_DATA; 87 | 88 | #define IOCTL_OSRDIO_READ CTL_CODE(FILE_DEVICE_OSRDIO, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) 89 | 90 | // 91 | // IOCTL_OSRDIO_WRITE 92 | // 93 | // Sets the state of lines that have been previously set to output. Lines that 94 | // have not been previously set to output (using IOCTL_OSRDIO_SET_OUTPUTS) are 95 | // ignored. 96 | // 97 | // Input Buffer: 98 | // 99 | // OSRDIO_WRITE_DATA structure. The OutputLineState field contains 100 | // a bitmap indicating the desired state of the output lines. A 1 bit 101 | // indicates that the corresponding line should be ASSERTED, a 0 indicates 102 | // the corresponding line should be DEASSERTED. 103 | // 104 | // Current line state can be read with IOCTL_OSRDIO_READ. 105 | // 106 | // Output Buffer: 107 | // (none) 108 | // 109 | // 110 | typedef struct OSRDIO_WRITE_DATA { 111 | ULONG OutputLineState; 112 | } OSRDIO_WRITE_DATA, *POSRDIO_WRITE_DATA; 113 | 114 | #define IOCTL_OSRDIO_WRITE CTL_CODE(FILE_DEVICE_OSRDIO, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS) 115 | 116 | // 117 | // IOCTL_OSRDIO_SET_OUTPUTS 118 | // 119 | // Sets which lines are to be used for OUTPUT (sending) DIO signals. 120 | // 121 | // Input Buffer: 122 | // 123 | // OSRDIO_SET_OUTPUTS_DATA structure. The OutputLines field contains 124 | // a bitmap indicating which lines are to be used for output (1 indicates 125 | // corresponding line is used for output). Lines not set for output are 126 | // implicitly set for use as input. 127 | // 128 | // Output Buffer: 129 | // (none) 130 | // 131 | typedef struct _OSRDIO_SET_OUTPUTS_DATA { 132 | ULONG OutputLines; 133 | } OSRDIO_SET_OUTPUTS_DATA, *POSRDIO_SET_OUTPUTS_DATA; 134 | 135 | #define IOCTL_OSRDIO_SET_OUTPUTS CTL_CODE(FILE_DEVICE_OSRDIO, 2051, METHOD_BUFFERED, FILE_ANY_ACCESS) 136 | 137 | // 138 | // IOCTL_OSRDIO_WAITFOR_CHANGE 139 | // 140 | // Awaits a state change on one of the input lines. When one or more 141 | // lines changes from DEASSSERTED to ASSERTED, the bitmask of all the line's 142 | // states is returned. 143 | // 144 | // Input Buffer: 145 | // (none) 146 | // 147 | // Output Buffer: 148 | // 149 | // OSRDIO_CHANGE_DATA structure. The LatchedInputLineState field contains a 150 | // bitmap indicating the state of all the DIO lines WHEN THE STATE 151 | // CHANGE OCCURRED. A 1 bit in the LatchedInputLineState field 152 | // indicates the corresponding line is ASSERTED, a 0 bit indicates that 153 | // the corresponding line is DEASSERTED. The state of both input and 154 | // output lines at the time of the state change is returned in the 155 | // LatchedInputLineState field of this IOCTL. 156 | // 157 | typedef struct _OSRDIO_CHANGE_DATA { 158 | ULONG LatchedLineState; 159 | } OSRDIO_CHANGE_DATA, *POSRDIO_COS_DATA; 160 | 161 | 162 | #define IOCTL_OSRDIO_WAITFOR_CHANGE CTL_CODE(FILE_DEVICE_OSRDIO, 2052, METHOD_BUFFERED, FILE_ANY_ACCESS) 163 | 164 | 165 | -------------------------------------------------------------------------------- /src/OsrDio.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright 2020 OSR Open Systems Resources, Inc. 4 | // All Rights Reserved 5 | // 6 | // This software is supplied for instructional purposes only. 7 | // 8 | // OSR Open Systems Resources, Inc. (OSR) expressly disclaims any warranty 9 | // for this software. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY 10 | // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, 11 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR 12 | // PURPOSE. THE ENTIRE RISK ARISING FROM THE USE OF THIS SOFTWARE REMAINS 13 | // WITH YOU. OSR's entire liability and your exclusive remedy shall not 14 | // exceed the price paid for this material. In no event shall OSR or its 15 | // suppliers be liable for any damages whatsoever (including, without 16 | // limitation, damages for loss of business profit, business interruption, 17 | // loss of business information, or any other pecuniary loss) arising out 18 | // of the use or inability to use this software, even if OSR has been 19 | // advised of the possibility of such damages. Because some states/ 20 | // jurisdictions do not allow the exclusion or limitation of liability for 21 | // consequential or incidental damages, the above limitation may not apply 22 | // to you. 23 | // 24 | // OSR Open Systems Resources, Inc. 25 | // 889 Elm St, Sixth Floor 26 | // Manchester, NH 03101 27 | // email bugs to: bugs@osr.com 28 | // 29 | // MODULE: 30 | // 31 | // OsrDio.cpp -- OSR WDF example driver for the 32 | // National Instruments PCIe-6509 Digital I/O Board. 33 | // 34 | // AUTHOR(S): 35 | // 36 | // OSR Open Systems Resources, Inc. 37 | // 38 | // 39 | // Note about the design and implementation of this driver: 40 | // This driver was written specifically as an instructional sample. 41 | // The design goals for the driver are correctness, simplicity, and 42 | // clarity in implementation. The driver strives to demonstrate "best 43 | // practices" in WDF driver development. 44 | // 45 | // For the sake of simplicity of understanding and use in an teaching 46 | // and learning environment, the features and functions that the driver 47 | // implements are intentionally only a small subset of the functionality 48 | // that the NI PCIe-6509 supports. Those features that the driver 49 | // does support are (intended to be) implemented correctly. However, 50 | // it's important to keep in mind that, even while they're correct, the 51 | // features we implement here are not intended to reflect what we'd do 52 | // in driver to support the NI PCIe-6509 in a production environment. As 53 | // just one example, this driver idles its device in low-power mode after 54 | // 10 seconds of inactivity. That would probably be a VERY bad idea in 55 | // a production DIO driver (given that any output lines that were aserted 56 | // would all be de-asserted when the device transitioned to a low power 57 | // state). 58 | // 59 | // Notes on supported PCIe-6509 features: 60 | // Even though the NI PCIe-6509 supports 96 Digital I/O (DIO) lines, this 61 | // driver only supports the lowest 32 static DIO lines. Despite "best 62 | // practice" for this hardware being that DIO lines should be written in 63 | // blocks of 8 to reduce crosstalk, we always read/write all 32 lines 64 | // simulatneously. We always monitor any input lines for any state 65 | // change (signal transitioning from low to high or high to low). We 66 | // always set the digital filters to their max values (which will reject 67 | // transitions less than 2.54ms and accept transitions greater than 5.1ms). 68 | // 69 | // If you have a PCIe-6509, probably the easiest way to test the driver is 70 | // to simply connect some of the output lines to the input lines... then 71 | // change the states of those output lines. You should get state change 72 | // notifications of changes on the assocaited input lines. 73 | // 74 | // About the use of C++ in this driver: 75 | // The WDF interface, and the basic Windows kernel mode interface, is 76 | // a C Language interface. At OSR, we write ALL our drivers using a 77 | // very limited subset of C++ as a "better C." Not only does it provide 78 | // strong type-checking, but it also provides several enhancements to 79 | // the language (such as constexpr, enum class, RAII patterns, and 80 | // range-based loops). While many C++ constructs (such as C++ native 81 | // exception handling) are NOT supported in Windows drivers, Windows 82 | // definitely (officially) supports the narrow subset of the language 83 | // that we we use in our code. 84 | // 85 | /////////////////////////////////////////////////////////////////////////////// 86 | #include "OsrDio.h" 87 | 88 | // 89 | // DriverEntry 90 | // 91 | // This routine is called by Windows when the driver is first loaded. We 92 | // simply do our initialization, and create our an instance of a 93 | // WDFDRIVER object to establish communication between this driver and 94 | // the Framework. 95 | // 96 | // INPUTS: 97 | // 98 | // DriverObj Address of the native WDM DRIVER_OBJECT that Windows 99 | // created for this driver. 100 | // 101 | // RegistryPath UNICODE_STRING structure that represents this driver's 102 | // key in the Registry ("HKLM\System\CCS\Services\OsrDio") 103 | // 104 | NTSTATUS 105 | DriverEntry(PDRIVER_OBJECT DriverObj, 106 | PUNICODE_STRING RegistryPath) 107 | { 108 | NTSTATUS status; 109 | WDF_DRIVER_CONFIG config; 110 | 111 | ExInitializeDriverRuntime(DrvRtPoolNxOptIn); 112 | 113 | #if OSR_FIX_ZERO_BUG_ON_1909 114 | 115 | // 116 | // Compensate for an error in the 2004 WDK's version of 117 | // ExInitializeDriverRuntime that mistakenly thinks that 118 | // Windows 1909 (19H2) is build number 18362 (it is actually 119 | // 18363). This error leads to pool allocations NOT being 120 | // zeroed on Windows 1909. 121 | // 122 | RTL_OSVERSIONINFOW versionInfo; 123 | 124 | versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); 125 | 126 | RtlZeroMemory(&versionInfo, sizeof(versionInfo)); 127 | 128 | (void)RtlGetVersion(&versionInfo); 129 | 130 | if(versionInfo.dwBuildNumber <= 18363) { 131 | 132 | if(ExPoolZeroingNativelySupported == TRUE) { 133 | ExPoolZeroingNativelySupported = FALSE; 134 | } 135 | 136 | } 137 | 138 | #endif 139 | 140 | #if DBG 141 | DbgPrint("\nOsrDio Driver V1.0 -- Compiled %s %s\n", 142 | __DATE__, 143 | __TIME__); 144 | #endif 145 | 146 | // 147 | // Initialize the Driver Config structure: 148 | // Specify our Add Device event callback. 149 | // 150 | WDF_DRIVER_CONFIG_INIT(&config, 151 | OsrDioEvtDriverDeviceAdd); 152 | 153 | // 154 | // Create our WDFDRIVER object 155 | // 156 | // We specify no object attributes, because we do not need a cleanup or 157 | // or destroy event callback, or any per-driver context. 158 | // 159 | status = WdfDriverCreate(DriverObj, 160 | RegistryPath, 161 | WDF_NO_OBJECT_ATTRIBUTES, 162 | &config, 163 | WDF_NO_HANDLE); 164 | 165 | if (!NT_SUCCESS(status)) { 166 | 167 | #if DBG 168 | DbgPrint("WdfDriverCreate failed with status 0x%0x\n", 169 | status); 170 | #endif 171 | } 172 | 173 | #if DBG 174 | DbgPrint("DriverEntry: Leaving\n"); 175 | #endif 176 | 177 | return status; 178 | } 179 | 180 | // 181 | // OsrDioEvtDriverDeviceAdd 182 | // 183 | // This is the event processing callback that WDF calls when an instance of 184 | // of a device is found that our driver supports. 185 | // 186 | // The main job of this callback is to create a WDFDEVICE object instance that 187 | // represents the device that has been found and make that device accessible 188 | // to the user. We also create any necessary Queues here (to allow us to 189 | // receive and process Requests) and define attributes of our power management 190 | // policy. 191 | // 192 | // INPUTS: 193 | // 194 | // Driver Handle to the WDFDRIVER Oject created in DriverEntry 195 | // 196 | // DeviceInit Pointer to a framework-allocated WDFDEVICE_INIT structure 197 | // the servces as the "initializer" structure for the 198 | // WDFDEVICE we'll be creating. 199 | NTSTATUS 200 | OsrDioEvtDriverDeviceAdd(WDFDRIVER Driver, 201 | PWDFDEVICE_INIT DeviceInit) 202 | { 203 | NTSTATUS status; 204 | WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; 205 | WDF_OBJECT_ATTRIBUTES objAttributes; 206 | WDFDEVICE device; 207 | POSRDIO_DEVICE_CONTEXT devContext; 208 | WDF_IO_QUEUE_CONFIG queueConfig; 209 | WDF_INTERRUPT_CONFIG interruptConfig; 210 | WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; 211 | 212 | #pragma warning(suppress: 26485) // "No array to pointer decay" 213 | DECLARE_CONST_UNICODE_STRING(dosDeviceName, 214 | LR"(\DosDevices\OSRDIO)"); 215 | 216 | UNREFERENCED_PARAMETER(Driver); 217 | 218 | // 219 | // Our first task is to instantiate a WDFDEVICE Object 220 | // 221 | 222 | // 223 | // Specify the Object Attributes for our WDFDEVICE 224 | // 225 | WDF_OBJECT_ATTRIBUTES_INIT(&objAttributes); 226 | 227 | // 228 | // Associate our device context structure type with our WDFDEVICE 229 | // 230 | WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&objAttributes, 231 | OSRDIO_DEVICE_CONTEXT); 232 | 233 | // 234 | // Specify object-specific configuration. We want to specify PnP/Power 235 | // Callbacks to manage our hardware resources. This is done using the 236 | // "collector structure" WDF_PNPPOWER_EVENT_CALLBACKS 237 | // 238 | 239 | WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); 240 | 241 | // 242 | // Prepare Hardware is called to give us our hardware resources 243 | // Release Hardware is called at when we need to return hardware resources 244 | // 245 | pnpPowerCallbacks.EvtDevicePrepareHardware = OsrDioEvtDevicePrepareHardware; 246 | pnpPowerCallbacks.EvtDeviceReleaseHardware = OsrDioEvtDeviceReleaseHardware; 247 | 248 | // 249 | // These two callbacks set up and tear down hardware state that must be 250 | // done every time the device moves in and out of the D0-Working state. 251 | // 252 | pnpPowerCallbacks.EvtDeviceD0Entry = OsrDioEvtDeviceD0Entry; 253 | pnpPowerCallbacks.EvtDeviceD0Exit = OsrDioEvtDeviceD0Exit; 254 | 255 | // 256 | // Copy the contents of the PnP/Power Callbacks "collector structure" to 257 | // our WDFDEVICE_INIT structure (which is the object-specific configurator 258 | // for WDFDEVICE). 259 | // 260 | WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, 261 | &pnpPowerCallbacks); 262 | 263 | // 264 | // And now instantiate the WDFDEVICE Object. 265 | // 266 | status = WdfDeviceCreate(&DeviceInit, 267 | &objAttributes, 268 | &device); 269 | 270 | if (!NT_SUCCESS(status)) { 271 | #if DBG 272 | DbgPrint("WdfDeviceInitialize failed 0x%0x\n", 273 | status); 274 | #endif 275 | goto done; 276 | } 277 | 278 | // 279 | // WDFDEVICE Object creation is complete 280 | // 281 | 282 | // 283 | // Create a symbolic link to our WDFDEVICE so users can open 284 | // the device by NAME. Note we don't attach any unit number to this 285 | // device name, so this driver only supports one device. 286 | // 287 | status = WdfDeviceCreateSymbolicLink(device, 288 | &dosDeviceName); 289 | 290 | if (!NT_SUCCESS(status)) { 291 | #if DBG 292 | DbgPrint("WdfDeviceCreateSymbolicLink failed 0x%0x\n", 293 | status); 294 | #endif 295 | goto done; 296 | } 297 | 298 | // 299 | // And make our device accessible via a Device Interface Class GUID. 300 | // User-mode users would call CM_Get_Device_Interface_List to get a 301 | // list of OSRDIO devices by specifying GUID_DEVINTERFACE_OSRDIO. 302 | // Optionally (user mode AND kernel mode) users can also register to be 303 | // notified of the arrival/departure of this device (for user mode, see 304 | // CM_Register_Notification). 305 | // 306 | status = WdfDeviceCreateDeviceInterface(device, 307 | &GUID_DEVINTERFACE_OSRDIO, 308 | nullptr); 309 | 310 | if (!NT_SUCCESS(status)) { 311 | #if DBG 312 | DbgPrint("WdfDeviceCreateDeviceInterface failed 0x%0x\n", 313 | status); 314 | #endif 315 | goto done; 316 | } 317 | 318 | // 319 | // Get a pointer to our device context, using the accessor function 320 | // we have defined (in OsrDio.h) 321 | // 322 | devContext = OsrDioGetContextFromDevice(device); 323 | 324 | devContext->WdfDevice = device; 325 | 326 | // 327 | // Configure a queue to handle incoming requests 328 | // 329 | // We use a single, default, queue for receiving Requests, and we only 330 | // support IRP_MJ_DEVICE_CONTROL. 331 | // 332 | 333 | // 334 | // We don't have Object Attributes for our Queue that we need to specify 335 | // 336 | 337 | // 338 | // With Sequential Dispatching, we will only get one request at a time 339 | // from our Queue. 340 | // 341 | WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, 342 | WdfIoQueueDispatchSequential); 343 | 344 | queueConfig.EvtIoDeviceControl = OsrDioEvtIoDeviceControl; 345 | 346 | status = WdfIoQueueCreate(device, 347 | &queueConfig, 348 | WDF_NO_OBJECT_ATTRIBUTES, 349 | WDF_NO_HANDLE); 350 | 351 | if (!NT_SUCCESS(status)) { 352 | #if DBG 353 | DbgPrint("WdfIoQueueCreate for default queue failed 0x%0x\n", 354 | status); 355 | #endif 356 | goto done; 357 | } 358 | 359 | // 360 | // We also create a manual Queue to hold Requests that are waiting for 361 | // a state change to happen on one of the input lines. 362 | // 363 | WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, 364 | WdfIoQueueDispatchManual); 365 | 366 | status = WdfIoQueueCreate(devContext->WdfDevice, 367 | &queueConfig, 368 | WDF_NO_OBJECT_ATTRIBUTES, 369 | &devContext->PendingQueue); 370 | 371 | if (!NT_SUCCESS(status)) { 372 | 373 | DbgPrint("WdfIoQueueCreate for Rx Queue failed 0x%0x\n", 374 | status); 375 | 376 | goto done; 377 | } 378 | 379 | // 380 | // Create an interrupt object that will later be associated with the 381 | // device's interrupt resource and connected by the Framework to our ISR. 382 | // 383 | 384 | // 385 | // Again, we don't need any Object Attributes for this Object... so we 386 | // move directly to Object-specific configuration 387 | // 388 | 389 | // 390 | // Configure the Interrupt object 391 | // 392 | WDF_INTERRUPT_CONFIG_INIT(&interruptConfig, 393 | OsrDioEvtInterruptIsr, 394 | OsrDioEvtInterruptDpc); 395 | 396 | interruptConfig.EvtInterruptEnable = OsrDioEvtInterruptEnable; 397 | interruptConfig.EvtInterruptDisable = OsrDioEvtInterruptDisable; 398 | 399 | status = WdfInterruptCreate(device, 400 | &interruptConfig, 401 | WDF_NO_OBJECT_ATTRIBUTES, 402 | &devContext->WdfInterrupt); 403 | if (!NT_SUCCESS(status)) { 404 | #if DBG 405 | DbgPrint("WdfInterruptCreate failed 0x%0x\n", 406 | status); 407 | #endif 408 | goto done; 409 | } 410 | 411 | // 412 | // Initialize our idle policy 413 | // 414 | // We accept most of the defaults here. Our device will idle in D3, and 415 | // WDF will create a property sheet for Device Manager that will allow 416 | // admin users to specify whether our device should idle in low-power 417 | // state. 418 | // 419 | WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, 420 | IdleCannotWakeFromS0); 421 | 422 | // 423 | // After 10 seconds of no activity, declare our device idle 424 | // Note that "idle" in this context means that the driver does not have 425 | // any Requests in progress. So, while we have a Request on the 426 | // PendingQueue (waiting to be informed of a line state change), WDF 427 | // will *not* idle the device. Recall that a device can always be 428 | // made to enter into, and remain in, D0-Working by calling 429 | // WdfDeviceStopIdle. 430 | // 431 | idleSettings.IdleTimeout = 10 * 1000; 432 | 433 | status = WdfDeviceAssignS0IdleSettings(device, 434 | &idleSettings); 435 | 436 | if (!NT_SUCCESS(status)) { 437 | #if DBG 438 | DbgPrint("WdfDeviceAssignS0IdleSettings failed 0x%0x\n", 439 | status); 440 | #endif 441 | goto done; 442 | } 443 | 444 | done: 445 | 446 | return status; 447 | } 448 | 449 | // 450 | // OsrDioEvtDevicePrepareHardware 451 | // 452 | // This entry point is called when hardware resources are assigned to one of 453 | // our devices. 454 | // 455 | // INPUTS: 456 | // Device Handle to our WDFDEVICE object 457 | // RawResources WDFCMRESLIST of hardware resources assigned 458 | // to our device. 459 | // TranslatedResources A WDFCMRESLIST of hardware resources 460 | // assigned to our device, made directly usable 461 | // by the driver. We expect one memory resource 462 | // and one interrupt resources. 463 | // 464 | // We almost never use the Raw Resources (as these are of primary interest to 465 | // bus drivers). Here, we only reference our Translated Resources. 466 | // 467 | NTSTATUS 468 | OsrDioEvtDevicePrepareHardware(WDFDEVICE Device, 469 | WDFCMRESLIST RawResources, 470 | WDFCMRESLIST TranslatedResources) 471 | { 472 | POSRDIO_DEVICE_CONTEXT devContext; 473 | PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceTrans; 474 | NTSTATUS status; 475 | BOOLEAN interruptFound = FALSE; 476 | #if DBG 477 | DbgPrint("EvtPrepareHardware...\n"); 478 | #endif 479 | 480 | UNREFERENCED_PARAMETER(RawResources); 481 | 482 | devContext = OsrDioGetContextFromDevice(Device); 483 | 484 | #if DBG 485 | 486 | DioUtilDisplayResources(RawResources, 487 | TranslatedResources); 488 | #endif 489 | 490 | for (ULONG i = 0; i < WdfCmResourceListGetCount(TranslatedResources); i++) { 491 | 492 | // 493 | // Get the i'th partial resource descriptor from the list 494 | // 495 | resourceTrans = WdfCmResourceListGetDescriptor(TranslatedResources, 496 | i); 497 | 498 | // 499 | // Let's examine and store the resources, based on their type 500 | // 501 | switch (resourceTrans->Type) { 502 | 503 | case CmResourceTypeMemory: { 504 | 505 | // 506 | // We identify the correct BAR by its expected size. 507 | // 508 | if (resourceTrans->u.Memory.Length == DIO_BAR_SIZE) { 509 | 510 | ASSERT(devContext->DevBase == nullptr); 511 | #if DBG 512 | DbgPrint("Found expected BAR\n"); 513 | #endif 514 | // 515 | // Map the device's registers into kernel mode virtual 516 | // address space 517 | // 518 | devContext->DevBase = static_cast( 519 | MmMapIoSpaceEx(resourceTrans->u.Memory.Start, 520 | resourceTrans->u.Memory.Length, 521 | PAGE_READWRITE)); 522 | 523 | if (devContext->DevBase == nullptr) { 524 | 525 | #if DBG 526 | DbgPrint("****MapIoSpace for resource %lu FAILED!\n", 527 | i); 528 | #endif 529 | devContext->MappedLength = 0; 530 | 531 | } else { 532 | 533 | devContext->MappedLength = resourceTrans->u.Memory.Length; 534 | 535 | #if DBG 536 | DbgPrint("Mapped BAR to KVA 0x%p\n", 537 | devContext->DevBase); 538 | 539 | DbgPrint("Mapped length = %lu\n", 540 | devContext->MappedLength); 541 | #endif 542 | 543 | } 544 | 545 | } else { 546 | 547 | #if DBG 548 | DbgPrint("(not interested in this resource)\n"); 549 | #endif 550 | } 551 | 552 | break; 553 | 554 | } 555 | 556 | case CmResourceTypeInterrupt: { 557 | 558 | ASSERT(interruptFound == FALSE); 559 | 560 | // 561 | // Because our devices supports only one interrupt, and we 562 | // create our WDFINTERRUPT Object in our EvtDriverDeviceAdd 563 | // Event Processing Callback, we don't have to do anything 564 | // here. WDF will automatically connect our (one) Interrupt 565 | // Service Routine to our (one) device interrupt. 566 | // 567 | interruptFound = TRUE; 568 | #if DBG 569 | DbgPrint("Interrupt found\n"); 570 | #endif 571 | break; 572 | } 573 | 574 | 575 | default: 576 | 577 | // 578 | // This could be any other type of resources, including 579 | // device-private type added by the PCI bus driver. We must 580 | // allow for device-private resources and we must not change 581 | // them. 582 | // 583 | #if DBG 584 | DbgPrint("Resource %lu: Unhandled resource type 0x%0x\n", 585 | i, 586 | resourceTrans->Type); 587 | #endif 588 | break; 589 | 590 | } // end of switch 591 | 592 | } // end of for 593 | 594 | // 595 | // If we got both resources that we're expecting 596 | // 597 | if (devContext->DevBase == nullptr || !interruptFound) { 598 | 599 | #if DBG 600 | DbgPrint("****** Expected resources NOT FOUND\n"); 601 | DbgPrint("****** Returning error from PrepareHardware\n"); 602 | #endif 603 | status = STATUS_PNP_DRIVER_CONFIGURATION_NOT_FOUND; 604 | 605 | goto done; 606 | } 607 | 608 | // 609 | // We initialize all Digital I/O lines as inputs. 610 | // That means there are NO lines are curerntly set to be used for 611 | // output. 612 | // 613 | devContext->OutputLineMask = 0; 614 | 615 | // 616 | // And when we power-on, we want the output lines to initially 617 | // all be DE-ASSERTED 618 | // 619 | devContext->SavedOutputLineState = 0; 620 | 621 | // 622 | // Put the device is a known state, with all interrupts disabled 623 | // 624 | DioUtilDeviceReset(devContext); 625 | 626 | status = STATUS_SUCCESS; 627 | 628 | done: 629 | 630 | return status; 631 | } 632 | 633 | // 634 | // OsrDioEvtDeviceReleaseHardware 635 | // 636 | // This function is called any time Windows wants us to release our 637 | // hardware resources. Examples include "bus rebalancing" and when 638 | // the "Disable Device" function is selected in Device Manager. 639 | // This callback IS NOT CALLED during system shutdown. 640 | // 641 | // INPUTS: 642 | // Device Handle to our WDFDEVICE 643 | // ResourcesTranslated The resources we're returning 644 | // 645 | NTSTATUS 646 | OsrDioEvtDeviceReleaseHardware(WDFDEVICE Device, 647 | WDFCMRESLIST ResourcesTranslated) 648 | { 649 | POSRDIO_DEVICE_CONTEXT devContext; 650 | 651 | #if DBG 652 | DbgPrint("EvtReleaseHardware...\n"); 653 | #endif 654 | 655 | UNREFERENCED_PARAMETER(ResourcesTranslated); 656 | 657 | devContext = OsrDioGetContextFromDevice(Device); 658 | 659 | if (devContext->DevBase != nullptr) { 660 | 661 | MmUnmapIoSpace(devContext->DevBase, 662 | devContext->MappedLength); 663 | 664 | devContext->DevBase = nullptr; 665 | devContext->MappedLength = 0; 666 | 667 | } 668 | 669 | // 670 | // Note that we don't have to do anything in this function to disconnect 671 | // or "return" our interrupt resource. WDF will automatically disconect 672 | // our ISR from any interrupts. Also, interrupts from the device have 673 | // already been disabled at this point, because EvtDeviceInterruptDisable 674 | // was called before this callback. 675 | // 676 | 677 | return STATUS_SUCCESS; 678 | } 679 | 680 | 681 | // 682 | // OsrDioEvtDeviceD0Entry 683 | // 684 | // This function is called each time our device has been transitioned into 685 | // the D0-Working (fully powered on) state. This includes during the 686 | // "implicit power on" that occurs after the device is first discovered. 687 | // Our job here is to initialize or restore the state of our device. 688 | // 689 | // The device is already in D0 when this function is called. 690 | // 691 | // INPUTS: 692 | // Device Handle to our WDFDEVICE 693 | // PreviousState The state from which we transitioned to D0 694 | // 695 | NTSTATUS 696 | #pragma warning(suppress: 26812) // "Prefer 'enum class' over 'enum'" 697 | OsrDioEvtDeviceD0Entry(WDFDEVICE Device, 698 | WDF_POWER_DEVICE_STATE PreviousState) 699 | { 700 | POSRDIO_DEVICE_CONTEXT devContext; 701 | 702 | #if DBG 703 | DbgPrint("D0Entry...\n"); 704 | #endif 705 | 706 | UNREFERENCED_PARAMETER(PreviousState); 707 | 708 | devContext = OsrDioGetContextFromDevice(Device); 709 | 710 | #if DBG 711 | DbgPrint("Restoring Output Line state = 0x%08x\n", 712 | devContext->SavedOutputLineState); 713 | #endif 714 | WRITE_REGISTER_ULONG(&devContext->DevBase->Static_Digital_Output_Register, 715 | devContext->SavedOutputLineState); 716 | 717 | return STATUS_SUCCESS; 718 | } 719 | 720 | // 721 | // OsrDioEvtDeviceD0Exit 722 | // 723 | // This function is called when our device is about to transition OUT of D0. 724 | // The target state is passed as an argument. Our job here is to save any 725 | // state associated with the device, so it can be restored when power is 726 | // returned to the device. 727 | // 728 | // The device is still in D0 when this function is called. 729 | // 730 | // INPUTS: 731 | // Device Handle to our WDFDEVICE 732 | // TagetState The state to which we're transitioning, from D0 733 | // 734 | NTSTATUS 735 | OsrDioEvtDeviceD0Exit(WDFDEVICE Device, 736 | WDF_POWER_DEVICE_STATE TargetState) 737 | { 738 | POSRDIO_DEVICE_CONTEXT devContext; 739 | ULONG outputLineState; 740 | 741 | #if DBG 742 | DbgPrint("D0Exit...\n"); 743 | #endif 744 | 745 | UNREFERENCED_PARAMETER(TargetState); 746 | 747 | devContext = OsrDioGetContextFromDevice(Device); 748 | 749 | outputLineState = 750 | READ_REGISTER_ULONG(&devContext->DevBase->Static_Digital_Input_Register); 751 | 752 | outputLineState &= devContext->OutputLineMask; 753 | 754 | devContext->SavedOutputLineState = outputLineState; 755 | 756 | #if DBG 757 | DbgPrint("Saved Output Line state = 0x%08x\n", 758 | devContext->SavedOutputLineState); 759 | #endif 760 | 761 | return STATUS_SUCCESS; 762 | } 763 | 764 | // 765 | // OsrDioEvtInterruptEnable 766 | // 767 | // Called by WDF to ask us to enable hardware interrupts on our device. 768 | // 769 | // INPUTS: 770 | // Interrupt Handle to our WDFINTERRUPT object 771 | // Device the usual 772 | // 773 | NTSTATUS 774 | OsrDioEvtInterruptEnable(WDFINTERRUPT Interrupt, 775 | WDFDEVICE Device) 776 | { 777 | POSRDIO_DEVICE_CONTEXT devContext; 778 | 779 | #if DBG 780 | DbgPrint("EvtInterruptEnable\n"); 781 | #endif 782 | 783 | UNREFERENCED_PARAMETER(Interrupt); 784 | 785 | devContext = OsrDioGetContextFromDevice(Device); 786 | 787 | // 788 | // Set the device's interrupt logic to a known state, ACK'ing any 789 | // outstanding interrupts and ensuring no interrupts are enabled. 790 | // 791 | DioUtilResetDeviceInterrupts(devContext); 792 | 793 | // 794 | // And enable interrupts from the Digital Inputs, from State Changes, 795 | // and from the card to the host. 796 | // 797 | DioUtilEnableDeviceInterrupts(devContext); 798 | 799 | // 800 | // Tell the device that we're interested in getting an interrupt when 801 | // the state of any of the input lines changes. 802 | // 803 | DioUtilProgramLineDirectionAndChangeMasks(devContext); 804 | 805 | return STATUS_SUCCESS; 806 | } 807 | 808 | // 809 | // OsrDioEvtInterruptDisable 810 | // 811 | // Called by WDF to ask us to DISABLE interrupt on our device. 812 | // 813 | // INPUTS: 814 | // Interrupt Handle to our WDFINTERRUPT object 815 | // Device the usual 816 | // 817 | NTSTATUS 818 | OsrDioEvtInterruptDisable(WDFINTERRUPT Interrupt, 819 | WDFDEVICE Device) 820 | { 821 | POSRDIO_DEVICE_CONTEXT devContext; 822 | 823 | #if DBG 824 | DbgPrint("EvtInterruptDisable \n"); 825 | #endif 826 | 827 | UNREFERENCED_PARAMETER(Interrupt); 828 | 829 | devContext = OsrDioGetContextFromDevice(Device); 830 | 831 | // 832 | // ACK and disable any pending interrupts 833 | // 834 | DioUtilResetDeviceInterrupts(devContext); 835 | 836 | return STATUS_SUCCESS; 837 | } 838 | 839 | // 840 | // OsrDioEvtIoDeviceControl 841 | // 842 | // Process an device control (IRP_MJ_DEVICE_CONTROL). 843 | // 844 | // INPUTS: 845 | // Queue The queue from which the request is being dispatched 846 | // Request The WDFREQUEST that describes this I/O request 847 | // OutputBufferLength, InputBufferLength, and IoControlCode 848 | // 849 | // NOTES -- Queuing Model 850 | // 851 | // WDF calls us at this entry point when we have a device control to process. 852 | // Note that back in OsrDioEvtDevceAdd, when we created and initialized our 853 | // default Queue, we set the queue dispatch type to be SEQUENTIAL. This means 854 | // that WDF will send our driver ONE REQUEST AT A TIME from this Queue, and 855 | // will not call us with another request until we're "done" processing the 856 | // current Request. 857 | // 858 | // What's interesting is that this does NOT imply that we must complete 859 | // every Request synchronously (that is, in its EvtIoxxx callback). Look at 860 | // the code for supporting IOCTL_OSRDIO_WAITFOR_CHANGE and you'll see that 861 | // instead of completing this Request we forward it to a manual Queue and then 862 | // return with that Request in progress. This serial model makes things very 863 | // easy for us and there's very little synchronization required. 864 | // 865 | VOID 866 | OsrDioEvtIoDeviceControl(WDFQUEUE Queue, 867 | WDFREQUEST Request, 868 | size_t OutputBufferLength, 869 | size_t InputBufferLength, 870 | ULONG IoControlCode) 871 | { 872 | POSRDIO_DEVICE_CONTEXT devContext; 873 | NTSTATUS status; 874 | ULONG bytesReadorWritten; 875 | 876 | #if DBG 877 | DbgPrint("OsrDioEvtIoDeviceControl\n"); 878 | #endif 879 | 880 | UNREFERENCED_PARAMETER(InputBufferLength); 881 | 882 | // 883 | // Get a pointer to our WDFDEVICE Context 884 | // 885 | devContext = OsrDioGetContextFromDevice(WdfIoQueueGetDevice(Queue)); 886 | 887 | // 888 | // Switch based on the control code specified by the user when they 889 | // issued the DeviceIoControl function call: 890 | // 891 | switch (IoControlCode) { 892 | 893 | 894 | case IOCTL_OSRDIO_READ: { 895 | POSRDIO_READ_DATA readBuffer; 896 | #if DBG 897 | DbgPrint("Ioctl: IOCTL_OSRDIO_READ\n"); 898 | #endif 899 | status = WdfRequestRetrieveOutputBuffer(Request, 900 | sizeof(OSRDIO_READ_DATA), 901 | (PVOID*)&readBuffer, 902 | nullptr); 903 | 904 | if (!NT_SUCCESS(status)) { 905 | 906 | #if DBG 907 | DbgPrint("Error retrieving inBuffer 0x%0x\n", 908 | status); 909 | #endif 910 | bytesReadorWritten = 0; 911 | 912 | goto done; 913 | } 914 | 915 | // 916 | // Get the current line state from the device and return it in the 917 | // user's output buffer. 918 | // 919 | readBuffer->CurrentLineState = 920 | READ_REGISTER_ULONG(&devContext->DevBase->Static_Digital_Input_Register); 921 | 922 | status = STATUS_SUCCESS; 923 | bytesReadorWritten = sizeof(OSRDIO_READ_DATA); 924 | 925 | break; 926 | } 927 | 928 | case IOCTL_OSRDIO_WRITE: { 929 | 930 | POSRDIO_WRITE_DATA writeBuffer; 931 | ULONG linesToAssert; 932 | #if DBG 933 | DbgPrint("Ioctl: IOCTL_OSRDIO_WRITE\n"); 934 | #endif 935 | // 936 | // We can't write anything if there are no lines set to output 937 | // 938 | if (devContext->OutputLineMask == 0) { 939 | 940 | #if DBG 941 | DbgPrint("ERROR! Write with output line mask set to zero\n"); 942 | #endif 943 | // 944 | // STATUS_INVALID_DEVICE_STATE becomes ERROR_BAD_COMMAND in 945 | // Win32. 946 | // 947 | status = STATUS_INVALID_DEVICE_STATE; 948 | bytesReadorWritten = 0; 949 | 950 | goto done; 951 | } 952 | 953 | status = WdfRequestRetrieveInputBuffer(Request, 954 | sizeof(OSRDIO_WRITE_DATA), 955 | (PVOID*)&writeBuffer, 956 | nullptr); 957 | 958 | if (!NT_SUCCESS(status)) { 959 | 960 | #if DBG 961 | DbgPrint("Error retrieving inBuffer 0x%08lx\n", 962 | status); 963 | #endif 964 | 965 | bytesReadorWritten = 0; 966 | 967 | goto done; 968 | } 969 | 970 | // 971 | // Get the bitmask of lines the user wants to assert 972 | // 973 | linesToAssert = writeBuffer->OutputLineState; 974 | 975 | // 976 | // Only allow the user to set to "1" those lines that have 977 | // previously been set as outline lines 978 | // 979 | linesToAssert &= devContext->OutputLineMask; 980 | 981 | WRITE_REGISTER_ULONG(&devContext->DevBase->Static_Digital_Output_Register, 982 | linesToAssert); 983 | 984 | status = STATUS_SUCCESS; 985 | bytesReadorWritten = sizeof(OSRDIO_WRITE_DATA); 986 | 987 | break; 988 | } 989 | 990 | case IOCTL_OSRDIO_SET_OUTPUTS: { 991 | POSRDIO_SET_OUTPUTS_DATA outputsBuffer; 992 | #if DBG 993 | DbgPrint("Ioctl: IOCTL_OSRDIO_SET_OUTPUTS\n"); 994 | #endif 995 | status = WdfRequestRetrieveInputBuffer(Request, 996 | sizeof(OSRDIO_SET_OUTPUTS_DATA), 997 | (PVOID*)&outputsBuffer, 998 | nullptr); 999 | 1000 | if (!NT_SUCCESS(status)) { 1001 | 1002 | #if DBG 1003 | DbgPrint("Error retrieving inBuffer 0x%08lx\n", 1004 | status); 1005 | #endif 1006 | 1007 | bytesReadorWritten = 0; 1008 | 1009 | goto done; 1010 | } 1011 | 1012 | // 1013 | // Get the mask of lines that the user wants to set to Output 1014 | // 1015 | devContext->OutputLineMask = outputsBuffer->OutputLines; 1016 | 1017 | // 1018 | // Program the output mask on the device (and enable related 1019 | // state-change interrupts) 1020 | // 1021 | DioUtilProgramLineDirectionAndChangeMasks(devContext); 1022 | 1023 | status = STATUS_SUCCESS; 1024 | bytesReadorWritten = sizeof(OSRDIO_SET_OUTPUTS_DATA); 1025 | 1026 | break; 1027 | } 1028 | 1029 | 1030 | case IOCTL_OSRDIO_WAITFOR_CHANGE: { 1031 | #if DBG 1032 | DbgPrint("Ioctl: IOCTL_OSRDIO_WAITFOR_CHANGE\n"); 1033 | #endif 1034 | // 1035 | // Before doing anything... Be sure some lines are 1036 | // set for input that we could wait to see a change on. 1037 | // 1038 | if ((~devContext->OutputLineMask) == 0) { 1039 | 1040 | #if DBG 1041 | DbgPrint("ERROR! No lines set to inputs. Can't wait for change\n"); 1042 | #endif 1043 | // 1044 | // ALL the lines are set to OUTPUT... So, there are no lines 1045 | // set to input that we can wait to change. 1046 | // 1047 | // STATUS_NONE_MAPPED becomes ERROR_NONE_MAPPED in Win32 1048 | // 1049 | status = STATUS_NONE_MAPPED; 1050 | bytesReadorWritten = 0; 1051 | 1052 | goto done; 1053 | } 1054 | 1055 | // 1056 | // Check to see if the buffer passed in is what we expect 1057 | // 1058 | if (OutputBufferLength < sizeof(OSRDIO_CHANGE_DATA)) { 1059 | 1060 | #if DBG 1061 | DbgPrint("ERROR! Invalid output buffer size on WAITFOR\n"); 1062 | #endif 1063 | // 1064 | // STATUS_INVALID_BUFFER_SIZE becomes ERROR_INVALID_USER_BUFFER 1065 | // in Win32. 1066 | // 1067 | status = STATUS_INVALID_BUFFER_SIZE; 1068 | bytesReadorWritten = 0; 1069 | 1070 | goto done; 1071 | } 1072 | 1073 | #if DBG 1074 | DbgPrint("Queueing Request %p, waiting for state change\n", 1075 | Request); 1076 | #endif 1077 | 1078 | // 1079 | // Forward the Request to the PendingQueue, where it'll wait for 1080 | // the Change Of State interrupt. 1081 | // 1082 | status = WdfRequestForwardToIoQueue(Request, 1083 | devContext->PendingQueue); 1084 | 1085 | if (!NT_SUCCESS(status)) { 1086 | 1087 | // 1088 | // Odd... forwarding the Request to the PendingQueue failed. 1089 | // Return that error to our caller. 1090 | // 1091 | bytesReadorWritten = 0; 1092 | 1093 | goto done; 1094 | } 1095 | 1096 | // 1097 | // Request has been successfully forwarded to the PendingQueue. 1098 | // We now return WITH THAT REQUEST IN PROGRESS. We'll complete 1099 | // it later, in our DpcForIsr, after a state change triggers an 1100 | // interrupt and our ISR queues a callback to our DpcForIsr. 1101 | // 1102 | goto doneDoNotComplete; 1103 | } 1104 | 1105 | default: { 1106 | #if DBG 1107 | DbgPrint("Received IOCTL 0x%x\n", 1108 | IoControlCode); 1109 | #endif 1110 | // 1111 | // STATUS_INVALID_PARAMETER becomes ERROR_INVALID_PARAMETER 1112 | // in Win32 1113 | // 1114 | status = STATUS_INVALID_PARAMETER; 1115 | bytesReadorWritten = 0; 1116 | 1117 | break; 1118 | } 1119 | } 1120 | 1121 | done: 1122 | 1123 | WdfRequestCompleteWithInformation(Request, 1124 | status, 1125 | bytesReadorWritten); 1126 | doneDoNotComplete: 1127 | 1128 | return; 1129 | } 1130 | 1131 | // 1132 | // OsrDioEvtInterruptIsr 1133 | // 1134 | // This is our driver's interrupt service routine. 1135 | // 1136 | // INPUTS: 1137 | // Interrupt Handle to our WDFINTERRUPT object describing this Interrupt 1138 | // 1139 | // MessageId The zero-based message number of the MSI/MSI-x message 1140 | // we're processing. This device only has one MSI, so there's 1141 | // no need to check the message ID that's passed to us. 1142 | // 1143 | BOOLEAN 1144 | OsrDioEvtInterruptIsr(WDFINTERRUPT Interrupt, 1145 | ULONG MessageID) 1146 | { 1147 | POSRDIO_DEVICE_CONTEXT devContext; 1148 | ULONG interruptStatus; 1149 | ULONG lineState; 1150 | BOOLEAN returnValue; 1151 | ULONG changeDetectReg; 1152 | 1153 | #if DBG 1154 | DbgPrint("ISR...\n"); 1155 | #endif 1156 | 1157 | UNREFERENCED_PARAMETER(MessageID); 1158 | 1159 | devContext = OsrDioGetContextFromDevice(WdfInterruptGetDevice(Interrupt)); 1160 | 1161 | // 1162 | // Get the pending interrupt status 1163 | // 1164 | // If an interrupt is being request from the device to the host, this 1165 | // will also acknowledge (and clear) that interrupt 1166 | // 1167 | interruptStatus = 1168 | READ_REGISTER_ULONG(&devContext->DevBase->Volatile_Interrupt_Status_Register); 1169 | 1170 | #if DBG 1171 | DbgPrint("IntStatus = 0x%08x\n", 1172 | interruptStatus); 1173 | #endif 1174 | 1175 | // 1176 | // Is there an interrupt pending from this device? 1177 | // 1178 | if ((interruptStatus & Vol_Int) == 0) { 1179 | 1180 | // 1181 | // Our device DID NOT cause this interrupt. We therefore will 1182 | // return FALSE to the Windows Interrupt Dispatcher 1183 | // 1184 | returnValue = FALSE; 1185 | 1186 | DbgPrint("Not our interrupt\n"); 1187 | 1188 | // 1189 | // The interrupt was not caused by our device 1190 | // 1191 | goto done; 1192 | } 1193 | 1194 | // 1195 | // Our device DID cause this interrupt, so we will return TRUE to the 1196 | // Windows Interrupt Dispatcher 1197 | // 1198 | returnValue = TRUE; 1199 | 1200 | // 1201 | // So... Our device interrupted. Find out why. 1202 | // 1203 | // Is the interrupt because a Digital Input line state change was detected? 1204 | // 1205 | changeDetectReg = 1206 | READ_REGISTER_ULONG(&devContext->DevBase->ChangeDetectStatusRegister); 1207 | 1208 | if (((changeDetectReg & ChangeDetectStatus) == 1) && 1209 | ((changeDetectReg & ChangeDetectError) == 0)) { 1210 | 1211 | // 1212 | // Yes... the state of one of the Digital Input lines has changed, 1213 | // AND the ERROR bit is not set. So, we will notify the user, if 1214 | // they have asked to be notified. 1215 | // 1216 | #if DBG 1217 | DbgPrint("ChangeDetectReg: Line state change SET and NO ERROR\n"); 1218 | #endif 1219 | 1220 | // 1221 | // Read the latched state of the DIO lines at the change 1222 | // 1223 | lineState = 1224 | READ_REGISTER_ULONG(&devContext->DevBase->DI_ChangeDetectLatched_Register); 1225 | 1226 | #if DBG 1227 | DbgPrint("Line state latched on change = 0x%08x\n", 1228 | lineState); 1229 | #endif 1230 | 1231 | // 1232 | // Save the state of the lines at change, for returning to the 1233 | // user 1234 | // 1235 | devContext->LatchedInputLineState = lineState; 1236 | 1237 | // 1238 | // Queue a DpcForIsr to return the data to the user and notify 1239 | // them of this state change 1240 | // 1241 | WdfInterruptQueueDpcForIsr(Interrupt); 1242 | } 1243 | 1244 | // 1245 | // Acknowledge (and clear) the condition that caused the interrupt. 1246 | // Doing this "resets" the Digital Input state change logic, and will 1247 | // cause it to recognize new state changes. 1248 | // 1249 | 1250 | if (changeDetectReg & ChangeDetectStatus) { 1251 | #if DBG 1252 | DbgPrint("ACK'ing change detect\n"); 1253 | #endif 1254 | // 1255 | // Acknowledge the state change on the Digital Input lines 1256 | // 1257 | WRITE_REGISTER_ULONG(&devContext->DevBase->ChangeDetectIRQ_Register, 1258 | ChangeDetectIRQ_Acknowledge); 1259 | } 1260 | 1261 | // 1262 | // If there was an error on the Digital Input lines, this would also 1263 | // cause an interrupt. If there IS an error, ACK and clear that error 1264 | // (so we will get notification of subsequent line state changes) 1265 | // 1266 | if (changeDetectReg & ChangeDetectError) { 1267 | 1268 | #if DBG 1269 | DbgPrint("ACK'ing change detect ERROR\n"); 1270 | #endif 1271 | WRITE_REGISTER_ULONG(&devContext->DevBase->ChangeDetectIRQ_Register, 1272 | ChangeDetectErrorIRQ_Acknowledge); 1273 | } 1274 | 1275 | done: 1276 | return returnValue; 1277 | } 1278 | 1279 | // 1280 | // OsrDioEvtInterruptDpc 1281 | // 1282 | // This is our DpcForIsr function, where we complete any processing that was 1283 | // started in our ISR. 1284 | // 1285 | // INPUTS: 1286 | // Interrupt WDFINTERRUPT related to this call 1287 | // Device WDFDEVICE handle 1288 | // 1289 | VOID 1290 | OsrDioEvtInterruptDpc(WDFINTERRUPT Interrupt, 1291 | WDFOBJECT Device) 1292 | { 1293 | POSRDIO_DEVICE_CONTEXT devContext; 1294 | WDFREQUEST waitingRequest; 1295 | POSRDIO_COS_DATA changeDataToReturn; 1296 | NTSTATUS status; 1297 | ULONG_PTR bytesReturned; 1298 | 1299 | UNREFERENCED_PARAMETER(Interrupt); 1300 | 1301 | #if DBG 1302 | DbgPrint("DPC for ISR...\n"); 1303 | #endif 1304 | 1305 | devContext = OsrDioGetContextFromDevice(Device); 1306 | 1307 | // 1308 | // IF there's a IOCTL_OSRDIO_WAITFOR_CHANGE Request that's pending, 1309 | // get a handle to it from the Queue where we stored it earlier. 1310 | // 1311 | status = WdfIoQueueRetrieveNextRequest(devContext->PendingQueue, 1312 | &waitingRequest); 1313 | // 1314 | // If there's no Requests waiting to be notified of the state change 1315 | // (or if there was some other odd error) just leave the DpcForIsr. 1316 | // 1317 | if (!NT_SUCCESS(status)) { 1318 | 1319 | #if DBG 1320 | DbgPrint("RetrieveNextRequest failed. Status = 0x%0x\n", 1321 | status); 1322 | 1323 | DbgPrint("Leaving DPC\n"); 1324 | #endif 1325 | goto done; 1326 | } 1327 | 1328 | ASSERT(waitingRequest != nullptr); 1329 | 1330 | // 1331 | // Get the requestor's output buffer, so we can return the state of the 1332 | // Digital Input lines. 1333 | // 1334 | status = WdfRequestRetrieveOutputBuffer(waitingRequest, 1335 | sizeof(OSRDIO_CHANGE_DATA), 1336 | (PVOID*)&changeDataToReturn, 1337 | nullptr); 1338 | if (NT_SUCCESS(status)) { 1339 | 1340 | // 1341 | // Return the data to the user 1342 | // 1343 | changeDataToReturn->LatchedLineState = devContext->LatchedInputLineState; 1344 | 1345 | #if DBG 1346 | DbgPrint("Completing Request %p: Returning latched line state = 0x%08lx\n", 1347 | waitingRequest, 1348 | changeDataToReturn->LatchedLineState); 1349 | #endif 1350 | 1351 | status = STATUS_SUCCESS; 1352 | bytesReturned = sizeof(OSRDIO_CHANGE_DATA); 1353 | 1354 | } else { 1355 | 1356 | // 1357 | // We'll return whatever status WdfRequestRetrieveOutputBuffer returned 1358 | // and zero bytes of data. 1359 | // 1360 | bytesReturned = 0; 1361 | } 1362 | 1363 | 1364 | WdfRequestCompleteWithInformation(waitingRequest, 1365 | status, 1366 | bytesReturned); 1367 | 1368 | done: 1369 | 1370 | return; 1371 | } 1372 | 1373 | // 1374 | // Utility Routines 1375 | // 1376 | // We would ordinarily locate these in a separate module from the mainline 1377 | // driver code. But given that this example driver is so short, it seems 1378 | // more convenient to just put there here. 1379 | // 1380 | 1381 | 1382 | // 1383 | // DioUtilProgramLineDirectionAndChangeMasks 1384 | // 1385 | // Set the line directions (indicating which lines are used for input and 1386 | // which for output) as well as the digtal filters for the input lines. Also 1387 | // program the device to interrupt whenever the state of one of the 1388 | // input lines changes (in either direction). 1389 | // 1390 | _Use_decl_annotations_ 1391 | VOID 1392 | DioUtilProgramLineDirectionAndChangeMasks(POSRDIO_DEVICE_CONTEXT DevContext) 1393 | { 1394 | #if DBG 1395 | DbgPrint("DioUtilProgramLineDirectionAndChangeMasks...\n"); 1396 | #endif 1397 | 1398 | // 1399 | // Set digital filters on the input lines to maximum filtering, to 1400 | // eliminate noise-related artifacts from showing-up on input lines 1401 | // during state changes. 1402 | // 1403 | WRITE_REGISTER_ULONG(&DevContext->DevBase->DI_FilterRegister_Port0and1, 1404 | Filter_Large_All_Lines); 1405 | 1406 | WRITE_REGISTER_ULONG(&DevContext->DevBase->DI_FilterRegister_Port2and3, 1407 | Filter_Large_All_Lines); 1408 | 1409 | // 1410 | // Tell the device which lines are Digital Inputs and which are Digital 1411 | // Outputs 1412 | // 1413 | WRITE_REGISTER_ULONG(&DevContext->DevBase->DIO_Direction_Register, 1414 | DevContext->OutputLineMask); 1415 | 1416 | // 1417 | // Having set the OUTPUT lines, set the remaining lines (which are 1418 | // INPUT lines) to detect state changes 1419 | // 1420 | 1421 | // 1422 | // Enable "rising edge" state change interrupts 1423 | // 1424 | WRITE_REGISTER_ULONG(&DevContext->DevBase->DI_ChangeIrqRE_Register, 1425 | (~DevContext->OutputLineMask)); 1426 | 1427 | // 1428 | // Enable "falling edge" state change interrupts 1429 | // 1430 | WRITE_REGISTER_ULONG(&DevContext->DevBase->DI_ChangeIrqFE_Register, 1431 | (~DevContext->OutputLineMask)); 1432 | } 1433 | 1434 | // 1435 | // DioUtilResetDeviceInterrupts 1436 | // 1437 | // ACKs, clears, and leave DISabled all device interrupts 1438 | // 1439 | _Use_decl_annotations_ 1440 | VOID 1441 | DioUtilResetDeviceInterrupts(POSRDIO_DEVICE_CONTEXT DevContext) 1442 | { 1443 | #if DBG 1444 | DbgPrint("DioUtilResetDeviceInterrupts...\n"); 1445 | #endif 1446 | 1447 | // 1448 | // Software reset the device 1449 | // 1450 | WRITE_REGISTER_ULONG(&DevContext->DevBase->Joint_Reset_Register, 1451 | Software_Reset); 1452 | 1453 | // 1454 | // Disable and acknowledge all interrupts (per NI Spec, section 2) 1455 | // 1456 | WRITE_REGISTER_ULONG(&DevContext->DevBase->Interrupt_Mask_Register, 1457 | (Clear_CPU_Int | Clear_STC3_Int)); 1458 | 1459 | WRITE_REGISTER_ULONG(&DevContext->DevBase->GlobalInterruptEnable_Register, 1460 | (DI_Interrupt_Disable | 1461 | WatchdogTimer_Interrupt_Disable)); 1462 | 1463 | WRITE_REGISTER_ULONG(&DevContext->DevBase->ChangeDetectIRQ_Register, 1464 | (ChangeDetectIRQ_Acknowledge | 1465 | ChangeDetectIRQ_Disable | 1466 | ChangeDetectErrorIRQ_Acknowledge | 1467 | ChangeDetectErrorIRQ_Disable)); 1468 | } 1469 | 1470 | // 1471 | // DioUtilEnableDeviceInterrupts 1472 | // 1473 | // Enables the Digital Inputs to interrupt the device's interrupt controller, 1474 | // and the device's interrupt controller to interrupt the host. 1475 | // 1476 | _Use_decl_annotations_ 1477 | VOID 1478 | DioUtilEnableDeviceInterrupts(POSRDIO_DEVICE_CONTEXT DevContext) 1479 | { 1480 | #if DBG 1481 | DbgPrint("DioUtilEnableDeviceInterrupts...\n"); 1482 | #endif 1483 | 1484 | // 1485 | // Enable interrupts from the Digital Inputs 1486 | // 1487 | WRITE_REGISTER_ULONG(&DevContext->DevBase->GlobalInterruptEnable_Register, 1488 | DI_Interrupt_Enable); 1489 | 1490 | // 1491 | // And enable interrupts as a result of state changes on the Digital Input 1492 | // lines 1493 | // 1494 | WRITE_REGISTER_ULONG(&DevContext->DevBase->ChangeDetectIRQ_Register, 1495 | (ChangeDetectErrorIRQ_Enable | 1496 | ChangeDetectIRQ_Enable)); 1497 | 1498 | // 1499 | // Enable interrupts from the device to the host 1500 | // 1501 | WRITE_REGISTER_ULONG(&DevContext->DevBase->Interrupt_Mask_Register, 1502 | (Set_CPU_Int | Set_STC3_Int)); 1503 | } 1504 | 1505 | // 1506 | // DioUtilDeviceReset 1507 | // 1508 | // Puts the device in a known, pristine, condition... ready to accept user 1509 | // commands. All previous settings on the device are lost/reset. 1510 | // 1511 | _Use_decl_annotations_ 1512 | VOID 1513 | DioUtilDeviceReset(POSRDIO_DEVICE_CONTEXT DevContext) 1514 | { 1515 | #if DBG 1516 | DbgPrint("DioUtilDeviceReset...\n"); 1517 | #endif 1518 | 1519 | // 1520 | // Reset/Clear/ACK any interrupts on the device 1521 | // 1522 | DioUtilResetDeviceInterrupts(DevContext); 1523 | 1524 | // 1525 | // Set all lines for INPUT, and ensure the output line state is 1526 | // set to "all lines DEASSERTED" 1527 | // 1528 | WRITE_REGISTER_ULONG(&DevContext->DevBase->DIO_Direction_Register, 1529 | 0x00000000); 1530 | 1531 | // 1532 | // Reset the device's idea of the output line state, just in case it 1533 | // "remembers" a previous state from when the output lines were enabled. 1534 | // 1535 | WRITE_REGISTER_ULONG(&DevContext->DevBase->Static_Digital_Output_Register, 1536 | 0x00000000); 1537 | 1538 | // 1539 | // Set the change detect registers to zeros. We set these to functional 1540 | // values when we set the OUTPUT mask. 1541 | // 1542 | WRITE_REGISTER_ULONG(&DevContext->DevBase->DI_ChangeIrqRE_Register, 1543 | 0x00000000); 1544 | 1545 | WRITE_REGISTER_ULONG(&DevContext->DevBase->DI_ChangeIrqFE_Register, 1546 | 0x00000000); 1547 | } 1548 | 1549 | #if DBG 1550 | /////////////////////////////////////////////////////////////////////////////// 1551 | // 1552 | // DioUtilDisplayResources 1553 | // 1554 | // Debugging function to display the resources assigned to the device 1555 | // 1556 | // INPUTS: 1557 | // 1558 | // Resources WDFCMRESLIST of hardware resources assigned 1559 | // to our device. 1560 | // 1561 | // ResourcesTranslated A WDFCMRESLIST of hardware resources 1562 | // assigned to our device, made usable. 1563 | // 1564 | // RETURNS: 1565 | // 1566 | // None. 1567 | // 1568 | // IRQL: 1569 | // 1570 | // Always PASSIVE_LEVEL 1571 | // 1572 | /////////////////////////////////////////////////////////////////////////////// 1573 | _Use_decl_annotations_ 1574 | VOID 1575 | DioUtilDisplayResources( 1576 | WDFCMRESLIST Resources, 1577 | WDFCMRESLIST ResourcesTranslated) 1578 | { 1579 | PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceTrans; 1580 | 1581 | UNREFERENCED_PARAMETER(Resources); 1582 | 1583 | DbgPrint("Dumping device resources:\n"); 1584 | 1585 | for (ULONG i = 0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++) { 1586 | 1587 | // 1588 | // Get the i'th partial resource descriptor from the list 1589 | // 1590 | resourceTrans = WdfCmResourceListGetDescriptor(ResourcesTranslated, 1591 | i); 1592 | 1593 | if (!resourceTrans) { 1594 | 1595 | DbgPrint("NULL resource returned??\n"); 1596 | 1597 | goto done; 1598 | } 1599 | 1600 | 1601 | // 1602 | // Examine and print the resources, based on their type 1603 | // 1604 | switch (resourceTrans->Type) { 1605 | 1606 | case CmResourceTypeMemory: { 1607 | DbgPrint("\tResource %lu: Register\n", 1608 | i); 1609 | 1610 | DbgPrint("\t\tBase: 0x%I64x\n", 1611 | resourceTrans->u.Memory.Start.QuadPart); 1612 | 1613 | DbgPrint("\t\tLength: %lu\n", 1614 | resourceTrans->u.Memory.Length); 1615 | 1616 | 1617 | break; 1618 | } 1619 | case CmResourceTypeInterrupt: { 1620 | 1621 | DbgPrint("\tResource %lu: Interrupt\n", 1622 | i); 1623 | 1624 | DbgPrint("\t\tInt type: %s\n", 1625 | (resourceTrans->Flags & CM_RESOURCE_INTERRUPT_MESSAGE ? 1626 | "MSI" : 1627 | "LBI")); 1628 | 1629 | if (resourceTrans->Flags & CM_RESOURCE_INTERRUPT_MESSAGE) { 1630 | PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceRaw; 1631 | 1632 | resourceRaw = WdfCmResourceListGetDescriptor(Resources, 1633 | i); 1634 | 1635 | DbgPrint("\t\tMSI Messages Allocated: %u\n", 1636 | resourceRaw->u.MessageInterrupt.Raw.MessageCount); 1637 | } 1638 | 1639 | break; 1640 | } 1641 | 1642 | case CmResourceTypePort: { 1643 | DbgPrint("\tResource %lu: Port\n", 1644 | i); 1645 | break; 1646 | } 1647 | case CmResourceTypeDma: { 1648 | DbgPrint("\tResource %lu: DMA\n", 1649 | i); 1650 | break; 1651 | } 1652 | case CmResourceTypeBusNumber: { 1653 | DbgPrint("\tResource %lu: BusNumber\n", 1654 | i); 1655 | break; 1656 | } 1657 | case CmResourceTypeMemoryLarge: { 1658 | DbgPrint("\tResource %lu: MemLarge\n", 1659 | i); 1660 | break; 1661 | } 1662 | case CmResourceTypeNonArbitrated: { 1663 | DbgPrint("\tResource %lu: NonArbitrated\n", 1664 | i); 1665 | break; 1666 | } 1667 | case CmResourceTypeDevicePrivate: { 1668 | DbgPrint("\tResource %lu: DevicePrivate\n", 1669 | i); 1670 | break; 1671 | } 1672 | case CmResourceTypePcCardConfig: { 1673 | DbgPrint("\tResource %lu: PcCardConfig\n", 1674 | i); 1675 | break; 1676 | } 1677 | default: { 1678 | 1679 | DbgPrint("\tResource %lu: Unhandled resource type 0x%0x\n", 1680 | i, 1681 | resourceTrans->Type); 1682 | break; 1683 | } 1684 | } 1685 | 1686 | } 1687 | 1688 | done: 1689 | 1690 | return; 1691 | } 1692 | #endif 1693 | -------------------------------------------------------------------------------- /src/OsrDio.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright 2020 OSR Open Systems Resources, Inc. 4 | // All Rights Reserved 5 | // 6 | // This software is supplied for instructional purposes only. 7 | // 8 | // OSR Open Systems Resources, Inc. (OSR) expressly disclaims any warranty 9 | // for this software. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY 10 | // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, 11 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR 12 | // PURPOSE. THE ENTIRE RISK ARISING FROM THE USE OF THIS SOFTWARE REMAINS 13 | // WITH YOU. OSR's entire liability and your exclusive remedy shall not 14 | // exceed the price paid for this material. In no event shall OSR or its 15 | // suppliers be liable for any damages whatsoever (including, without 16 | // limitation, damages for loss of business profit, business interruption, 17 | // loss of business information, or any other pecuniary loss) arising out 18 | // of the use or inability to use this software, even if OSR has been 19 | // advised of the possibility of such damages. Because some states/ 20 | // jurisdictions do not allow the exclusion or limitation of liability for 21 | // consequential or incidental damages, the above limitation may not apply 22 | // to you. 23 | // 24 | // OSR Open Systems Resources, Inc. 25 | // 889 Elm St, Sixth Floor 26 | // Manchester, NH 03101 27 | // email bugs to: bugs@osr.com 28 | // 29 | // 30 | // MODULE: 31 | // 32 | // OsrDio.h -- WDF Example Driver, master header file 33 | // 34 | // AUTHOR(S): 35 | // 36 | // OSR Open Systems Resources, Inc. 37 | // 38 | /////////////////////////////////////////////////////////////////////////////// 39 | #pragma once 40 | 41 | // 42 | // Standard Windows headers 43 | #include 44 | #include 45 | 46 | #define OSR_FIX_ZERO_BUG_ON_1909 1 47 | 48 | // ReSharper disable once CppUnusedIncludeDirective 49 | #include "OsrDio_IOCTL.h" 50 | 51 | #pragma warning(disable: 4201) // "non-standard extension used" 52 | 53 | // 54 | // WDF drivers should typically be built with Code Analysis enabled for all 55 | // builds, and with the setting set to "Microsoft Driver Minimum Rules" 56 | // (which is actually the MSFT recommended rule set). 57 | // 58 | // 59 | // We generally do not recommend routinely building with Code Analysis (CA) 60 | // checking for the CPP Core Guidelines enabled. But... occasionally 61 | // running CA with these warnings enabled can be useful. To facilitate that 62 | // we disable the following warnings that we really don't care about: 63 | // 64 | // 26493 no C-style casts 65 | // 26461 variable can be made const 66 | // 26464 initialize everything 67 | // 26438 avoid goto 68 | // 26489 don't deref a point that could be null 69 | // 70 | #pragma warning(disable: 26493 26461 26494 26464 26438 26489) 71 | 72 | // 73 | // The size of the device memory area on the NI PCIe-6509 74 | // 75 | constexpr ULONG DIO_BAR_SIZE = 512 * 1024; 76 | 77 | 78 | // ReSharper disable CppInconsistentNaming 79 | 80 | // 81 | // Macros for building a structure for the register map, even though 82 | // the map is large and non-contiguous. 83 | // 84 | // We thank @shuffle2 via ghettoha.xxx for inspiring this technique 85 | // 86 | // ReSharper disable CppClangTidyCppcoreguidelinesMacroUsage 87 | // ReSharper disable IdentifierTypo 88 | 89 | #define CAT_(x, y) x ## y 90 | #define CAT(x, y) CAT_(x, y) 91 | 92 | #define REGPAD(_size_) UCHAR CAT(_pad_, __COUNTER__)[_size_] 93 | #define REGSTRUCT_START(_name_, _size_) typedef struct _##_name_ { union { REGPAD(_size_); 94 | #define REGSTRUCT_END(_name_) };} _name_, *P##_name_ // NOLINT(bugprone-macro-parentheses) 95 | 96 | #define REGDEF_ULONG(_field_, _off_) struct { REGPAD(_off_); ULONG _field_; } 97 | #define REGDEF(_off_, _field_) struct { REGPAD(_off_); _field_; } 98 | 99 | // 100 | // DIO_REGISTERS Structure Definition 101 | // 102 | // The NI PCIe-6509 has a register map that is spread-out through its 103 | // 512K of Memory Mapper I/O space. We define a typedef'ed structure 104 | // named "DIO_REGISTERS" that describes the register map using the 105 | // macros defined above. The registers are all 32-bits wide. 106 | // 107 | // All register names are as specified in the NI documentation. 108 | // 109 | REGSTRUCT_START(DIO_REGISTERS, DIO_BAR_SIZE); 110 | ULONG CHInCh_Identification_Register; // This register is at offset 0 111 | // 112 | // 113 | // OFFSET 114 | // REGISTER NAME from BAR 0 115 | // ============================== ========== 116 | REGDEF_ULONG( Static_Digital_Input_Register, 0x20530 ); 117 | 118 | REGDEF_ULONG( Static_Digital_Output_Register, 0x204B0 ); 119 | REGDEF_ULONG( DIO_Direction_Register, 0x204B4 ); 120 | REGDEF_ULONG( DI_FilterRegister_Port0and1, 0x2054C ); 121 | REGDEF_ULONG( DI_FilterRegister_Port2and3, 0x20550 ); 122 | 123 | // 124 | // DIO Change of State (RE = "Rising Edge", FE "Falling Edge") 125 | // and DIO Interrupt Registers 126 | // 127 | REGDEF_ULONG( ChangeDetectStatusRegister, 0x20540 ); // READ 128 | REGDEF_ULONG( DI_ChangeIrqRE_Register, 0x20540 ); // WRITE 129 | REGDEF_ULONG( DI_ChangeIrqFE_Register, 0x20544 ); // WRITE 130 | REGDEF_ULONG( DI_ChangeDetectLatched_Register, 0x20544 ); 131 | 132 | REGDEF_ULONG( GlobalInterruptStatus_Register, 0x20070 ); 133 | REGDEF_ULONG( GlobalInterruptEnable_Register, 0x20078 ); 134 | REGDEF_ULONG( DI_Interrupt_Status_Register, 0x2007E ); 135 | REGDEF_ULONG( ChangeDetectIRQ_Register, 0x20554 ); 136 | 137 | // 138 | // Board-Wide Interrupt Controller Registers 139 | // 140 | REGDEF_ULONG( Interrupt_Mask_Register, 0x0005C ); 141 | REGDEF_ULONG( Interrupt_Status_Register, 0x00060 ); 142 | REGDEF_ULONG( Volatile_Interrupt_Status_Register, 0x00068 ); 143 | REGDEF_ULONG( IntForwarding_ControlStatus, 0x22204 ); 144 | REGDEF_ULONG( IntForwarding_DestinationReg, 0x22208 ); 145 | 146 | // 147 | // Miscellaneous Board-Level Registers 148 | // 149 | REGDEF_ULONG( Scrap_Register, 0X00200 ); 150 | REGDEF_ULONG( PCI_Subsystem_ID_Access_Register, 0x010AC ); 151 | REGDEF_ULONG( ScratchpadRegister, 0x20004 ); 152 | REGDEF_ULONG( Signature_Register, 0x20060 ); 153 | REGDEF_ULONG( Joint_Reset_Register, 0x20064 ); // WRITE 154 | REGDEF_ULONG( TimeSincePowerUpRegister, 0x20064 ); // READ 155 | 156 | REGSTRUCT_END(DIO_REGISTERS); 157 | 158 | // ReSharper disable IdentifierTypo 159 | // ReSharper restore CppClangTidyCppcoreguidelinesMacroUsage 160 | 161 | constexpr ULONG BIT_NUMBER(int x) 162 | { 163 | return (ULONG)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 | Debug 22 | ARM 23 | 24 | 25 | Release 26 | ARM 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Release 34 | ARM64 35 | 36 | 37 | 38 | 39 | 40 | 41 | {902A0D37-B4A9-4A98-BCA6-67D70D66EFBC} 42 | {8c0e3d8b-df43-455b-815a-4a0e72973bc6} 43 | v4.5 44 | 12.0 45 | Debug 46 | Win32 47 | OsrDio 48 | 10.0.20348.0 49 | 50 | 51 | WindowsKernelModeDriver10.0 52 | Driver 53 | KMDF 54 | Universal 55 | 56 | 57 | WindowsKernelModeDriver10.0 58 | Driver 59 | KMDF 60 | Universal 61 | 62 | 63 | WindowsKernelModeDriver10.0 64 | Driver 65 | KMDF 66 | Universal 67 | 68 | 69 | WindowsKernelModeDriver10.0 70 | Driver 71 | KMDF 72 | Universal 73 | 74 | 75 | WindowsKernelModeDriver10.0 76 | Driver 77 | KMDF 78 | Universal 79 | 80 | 81 | WindowsKernelModeDriver10.0 82 | Driver 83 | KMDF 84 | Universal 85 | 86 | 87 | WindowsKernelModeDriver10.0 88 | Driver 89 | KMDF 90 | Universal 91 | 92 | 93 | WindowsKernelModeDriver10.0 94 | Driver 95 | KMDF 96 | Universal 97 | 98 | 99 | 100 | Windows10 101 | true 102 | 1 103 | true 104 | 105 | 106 | Windows10 107 | false 108 | true 109 | 110 | 111 | Windows10 112 | true 113 | 1 114 | true 115 | 116 | 117 | Windows10 118 | false 119 | true 120 | 121 | 122 | Windows10 123 | true 124 | 1 125 | true 126 | 127 | 128 | Windows10 129 | false 130 | true 131 | 132 | 133 | Windows10 134 | true 135 | 1 136 | true 137 | 138 | 139 | Windows10 140 | false 141 | true 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | DbgengKernelDebugger 153 | false 154 | ..\inc;$(IncludePath) 155 | C:\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverRecommendedRules.ruleset 156 | false 157 | 158 | 159 | DbgengKernelDebugger 160 | C:\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverRecommendedRules.ruleset 161 | ..\inc;$(IncludePath) 162 | false 163 | false 164 | 165 | 166 | DbgengKernelDebugger 167 | false 168 | ..\inc;$(IncludePath) 169 | C:\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverRecommendedRules.ruleset 170 | false 171 | 172 | 173 | DbgengKernelDebugger 174 | C:\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverRecommendedRules.ruleset 175 | ..\inc;$(IncludePath) 176 | false 177 | false 178 | 179 | 180 | DbgengKernelDebugger 181 | false 182 | ..\inc;$(IncludePath) 183 | C:\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverRecommendedRules.ruleset 184 | false 185 | 186 | 187 | DbgengKernelDebugger 188 | C:\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverRecommendedRules.ruleset 189 | ..\inc;$(IncludePath) 190 | false 191 | false 192 | 193 | 194 | DbgengKernelDebugger 195 | false 196 | ..\inc;$(IncludePath) 197 | C:\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverRecommendedRules.ruleset 198 | false 199 | 200 | 201 | DbgengKernelDebugger 202 | C:\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverRecommendedRules.ruleset 203 | ..\inc;$(IncludePath) 204 | false 205 | false 206 | 207 | 208 | 209 | false 210 | true 211 | trace.h 212 | true 213 | true 214 | _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions);POOL_NX_OPTIN=1;POOL_ZERO_DOWNLEVEL_SUPPORT=1 215 | 216 | 217 | %(AdditionalDependencies);usbdex.lib;ntstrsafe.lib 218 | 219 | 220 | /fd SHA256 %(AdditionalOptions) 221 | 222 | 223 | 224 | 225 | false 226 | true 227 | trace.h 228 | true 229 | _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions);POOL_NX_OPTIN=1;POOL_ZERO_DOWNLEVEL_SUPPORT=1 230 | true 231 | 232 | 233 | %(AdditionalDependencies);usbdex.lib;ntstrsafe.lib 234 | 235 | 236 | /fd SHA256 %(AdditionalOptions) 237 | 238 | 239 | 240 | 241 | false 242 | true 243 | trace.h 244 | true 245 | true 246 | _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions);POOL_NX_OPTIN=1;POOL_ZERO_DOWNLEVEL_SUPPORT=1 247 | 248 | 249 | %(AdditionalDependencies);usbdex.lib;ntstrsafe.lib 250 | 251 | 252 | /fd SHA256 %(AdditionalOptions) 253 | 254 | 255 | 256 | 257 | false 258 | true 259 | trace.h 260 | true 261 | _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions);POOL_NX_OPTIN=1;POOL_ZERO_DOWNLEVEL_SUPPORT=1 262 | true 263 | 264 | 265 | %(AdditionalDependencies);usbdex.lib;ntstrsafe.lib 266 | 267 | 268 | /fd SHA256 %(AdditionalOptions) 269 | 270 | 271 | 272 | 273 | false 274 | true 275 | trace.h 276 | true 277 | true 278 | _ARM_;ARM;_USE_DECLSPECS_FOR_SAL=1;STD_CALL;%(PreprocessorDefinitions);POOL_NX_OPTIN=1;POOL_ZERO_DOWNLEVEL_SUPPORT=1 279 | 280 | 281 | %(AdditionalDependencies);usbdex.lib;ntstrsafe.lib 282 | 283 | 284 | 285 | 286 | 287 | false 288 | true 289 | trace.h 290 | true 291 | _ARM_;ARM;_USE_DECLSPECS_FOR_SAL=1;STD_CALL;%(PreprocessorDefinitions);POOL_NX_OPTIN=1;POOL_ZERO_DOWNLEVEL_SUPPORT=1 292 | true 293 | 294 | 295 | %(AdditionalDependencies);usbdex.lib;ntstrsafe.lib 296 | 297 | 298 | 299 | 300 | 301 | false 302 | true 303 | trace.h 304 | true 305 | true 306 | _ARM64_;ARM64;_USE_DECLSPECS_FOR_SAL=1;STD_CALL;%(PreprocessorDefinitions);POOL_NX_OPTIN=1;POOL_ZERO_DOWNLEVEL_SUPPORT=1 307 | 308 | 309 | %(AdditionalDependencies);usbdex.lib;ntstrsafe.lib 310 | 311 | 312 | 313 | 314 | 315 | false 316 | true 317 | trace.h 318 | true 319 | _ARM64_;ARM64;_USE_DECLSPECS_FOR_SAL=1;STD_CALL;%(PreprocessorDefinitions);POOL_NX_OPTIN=1;POOL_ZERO_DOWNLEVEL_SUPPORT=1 320 | true 321 | 322 | 323 | %(AdditionalDependencies);usbdex.lib;ntstrsafe.lib 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | -------------------------------------------------------------------------------- /src/OsrDio.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Driver Files 24 | 25 | 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | --------------------------------------------------------------------------------